Draft: make arrays explodable
Fixes #13085. This PR adds the `PlacementList` property to non-link arrays.
This commit is contained in:
@@ -32,6 +32,7 @@ See also the `upgrade` function.
|
||||
# @{
|
||||
import FreeCAD as App
|
||||
from draftfunctions import cut
|
||||
from draftmake import make_copy
|
||||
from draftutils import utils
|
||||
from draftutils import params
|
||||
from draftutils import gui_utils
|
||||
@@ -85,13 +86,33 @@ def downgrade(objects, delete=False, force=None):
|
||||
|
||||
# actions definitions
|
||||
def explode(obj):
|
||||
"""Explode a Draft block."""
|
||||
pl = obj.Placement
|
||||
for o in obj.Components:
|
||||
o.Placement = pl.multiply(o.Placement)
|
||||
if App.GuiUp:
|
||||
o.ViewObject.Visibility = True
|
||||
"""Explode a Draft block or array."""
|
||||
obj_pl = obj.Placement
|
||||
# block:
|
||||
if getattr(obj, "Components", []):
|
||||
delete_list.append(obj)
|
||||
for comp in obj.Components:
|
||||
comp.Placement = obj_pl.multiply(comp.Placement)
|
||||
comp.Visibility = True
|
||||
return True
|
||||
# array:
|
||||
if getattr(obj, "Base", None) is None:
|
||||
return False
|
||||
if not hasattr(obj, "PlacementList"):
|
||||
return False
|
||||
base = obj.Base
|
||||
delete_list.append(obj)
|
||||
if getattr(obj, "ExpandArray", False):
|
||||
for lnk in obj.ElementList:
|
||||
new = make_copy.make_copy(base)
|
||||
new.Placement = obj_pl.multiply(lnk.Placement)
|
||||
new.Visibility = True
|
||||
delete_list.append(lnk)
|
||||
else:
|
||||
for arr_pl in obj.PlacementList:
|
||||
new = make_copy.make_copy(base)
|
||||
new.Placement = obj_pl.multiply(arr_pl)
|
||||
new.Visibility = True
|
||||
return True
|
||||
|
||||
def cut2(objects):
|
||||
@@ -240,15 +261,22 @@ def downgrade(objects, delete=False, force=None):
|
||||
shapify = utils.shapify
|
||||
result = eval(force)(objects)
|
||||
else:
|
||||
_msg(translate("draft","Upgrade: Unknown force method:") + " " + force)
|
||||
_msg(translate("draft", "Upgrade: Unknown force method:") + " " + force)
|
||||
result = None
|
||||
else:
|
||||
# applying transformation automatically
|
||||
# we have a block, we explode it
|
||||
if len(objects) == 1 and utils.get_type(objects[0]) == "Block":
|
||||
result = explode(objects[0])
|
||||
if result:
|
||||
_msg(translate("draft","Found 1 block: exploding it"))
|
||||
_msg(translate("draft", "Found 1 block: exploding it"))
|
||||
|
||||
# we have an array, we explode it
|
||||
elif len(objects) == 1 \
|
||||
and "Array" in utils.get_type(objects[0]) \
|
||||
and hasattr(objects[0], "PlacementList"):
|
||||
result = explode(objects[0])
|
||||
if result:
|
||||
_msg(translate("draft", "Found 1 array: exploding it"))
|
||||
|
||||
# we have one multi-solids compound object: extract its solids
|
||||
elif len(objects) == 1 \
|
||||
@@ -257,7 +285,7 @@ def downgrade(objects, delete=False, force=None):
|
||||
result = splitCompounds(objects)
|
||||
# print(result)
|
||||
if result:
|
||||
_msg(translate("draft","Found 1 multi-solids compound: exploding it"))
|
||||
_msg(translate("draft", "Found 1 multi-solids compound: exploding it"))
|
||||
|
||||
# special case, we have one parametric object: we "de-parametrize" it
|
||||
elif len(objects) == 1 \
|
||||
@@ -266,7 +294,7 @@ def downgrade(objects, delete=False, force=None):
|
||||
and not objects[0].isDerivedFrom("PartDesign::Feature"):
|
||||
result = utils.shapify(objects[0])
|
||||
if result:
|
||||
_msg(translate("draft","Found 1 parametric object: breaking its dependencies"))
|
||||
_msg(translate("draft", "Found 1 parametric object: breaking its dependencies"))
|
||||
add_list.append(result)
|
||||
# delete_list.append(objects[0])
|
||||
|
||||
@@ -274,35 +302,35 @@ def downgrade(objects, delete=False, force=None):
|
||||
elif len(objects) == 2:
|
||||
result = cut2(objects)
|
||||
if result:
|
||||
_msg(translate("draft","Found 2 objects: subtracting them"))
|
||||
_msg(translate("draft", "Found 2 objects: subtracting them"))
|
||||
|
||||
elif len(faces) > 1:
|
||||
# one object with several faces: split it
|
||||
if len(objects) == 1:
|
||||
result = splitFaces(objects)
|
||||
if result:
|
||||
_msg(translate("draft","Found several faces: splitting them"))
|
||||
_msg(translate("draft", "Found several faces: splitting them"))
|
||||
# several objects: remove all the faces from the first one
|
||||
else:
|
||||
result = subtr(objects)
|
||||
if result:
|
||||
_msg(translate("draft","Found several objects: subtracting them from the first one"))
|
||||
_msg(translate("draft", "Found several objects: subtracting them from the first one"))
|
||||
|
||||
# only one face: we extract its wires
|
||||
elif len(faces) > 0:
|
||||
result = getWire(objects[0])
|
||||
if result:
|
||||
_msg(translate("draft","Found 1 face: extracting its wires"))
|
||||
_msg(translate("draft", "Found 1 face: extracting its wires"))
|
||||
|
||||
# no faces: split wire into single edges
|
||||
elif not onlyedges:
|
||||
result = splitWires(objects)
|
||||
if result:
|
||||
_msg(translate("draft","Found only wires: extracting their edges"))
|
||||
_msg(translate("draft", "Found only wires: extracting their edges"))
|
||||
|
||||
# no result has been obtained
|
||||
if not result:
|
||||
_msg(translate("draft","No more downgrade possible"))
|
||||
_msg(translate("draft", "No more downgrade possible"))
|
||||
|
||||
if delete:
|
||||
for o in delete_list:
|
||||
|
||||
@@ -65,15 +65,18 @@ class Array(DraftLink):
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
super(Array, self).onDocumentRestored(obj)
|
||||
if hasattr(obj, "Count"):
|
||||
# Count property was added in v0.21 and PlacementList property was added
|
||||
# for non-link arrays in v1.1, obj should be OK if both are present:
|
||||
if hasattr(obj, "Count") and hasattr(obj, "PlacementList"):
|
||||
return
|
||||
self.update_properties_0v21(obj)
|
||||
|
||||
def update_properties_0v21(self, obj):
|
||||
if not hasattr(obj, "Count"):
|
||||
_wrn("v0.21, " + obj.Label + ", " + translate("draft", "added property 'Count'"))
|
||||
if not hasattr(obj, "PlacementList"):
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added hidden property 'PlacementList'"))
|
||||
|
||||
self.set_general_properties(obj)
|
||||
self.execute(obj) # Required to update Count to the correct value.
|
||||
_wrn("v0.21, " + obj.Label + ", "
|
||||
+ translate("draft", "added property 'Count'"))
|
||||
self.execute(obj) # Required to update Count and/or PlacementList.
|
||||
|
||||
def set_properties(self, obj):
|
||||
"""Set properties only if they don't exist."""
|
||||
@@ -141,6 +144,16 @@ class Array(DraftLink):
|
||||
obj.Count = 0
|
||||
obj.setEditorMode("Count", 1) # Read only
|
||||
|
||||
if not self.use_link:
|
||||
if "PlacementList" not in properties:
|
||||
_tip = QT_TRANSLATE_NOOP("App::Property",
|
||||
"The placement for each array element")
|
||||
obj.addProperty("App::PropertyPlacementList",
|
||||
"PlacementList",
|
||||
"Objects",
|
||||
_tip)
|
||||
obj.PlacementList = []
|
||||
|
||||
def set_ortho_properties(self, obj):
|
||||
"""Set orthogonal properties only if they don't exist."""
|
||||
properties = obj.PropertiesList
|
||||
|
||||
@@ -204,8 +204,10 @@ class DraftLink(DraftObject):
|
||||
and getattr(obj, 'AlwaysSyncPlacement', False):
|
||||
for pla,child in zip(pls,obj.ElementList):
|
||||
child.Placement = pla
|
||||
elif obj.Count != len(pls):
|
||||
obj.Count = len(pls)
|
||||
else:
|
||||
obj.PlacementList = pls
|
||||
if obj.Count != len(pls):
|
||||
obj.Count = len(pls)
|
||||
|
||||
if obj.Base:
|
||||
shape = getattr(obj.Base, 'Shape', None)
|
||||
|
||||
@@ -262,6 +262,16 @@ class PathArray(DraftLink):
|
||||
obj.ExpandArray = False
|
||||
obj.setPropertyStatus('Shape', 'Transient')
|
||||
|
||||
if not self.use_link:
|
||||
if "PlacementList" not in properties:
|
||||
_tip = QT_TRANSLATE_NOOP("App::Property",
|
||||
"The placement for each array element")
|
||||
obj.addProperty("App::PropertyPlacementList",
|
||||
"PlacementList",
|
||||
"Objects",
|
||||
_tip)
|
||||
obj.PlacementList = []
|
||||
|
||||
def set_align_properties(self, obj, properties):
|
||||
"""Set general properties only if they don't exist."""
|
||||
if "ExtraTranslation" not in properties:
|
||||
@@ -541,37 +551,39 @@ class PathArray(DraftLink):
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
super().onDocumentRestored(obj)
|
||||
# Run updates in order:
|
||||
self.ensure_updated(obj)
|
||||
|
||||
def ensure_updated(self, obj):
|
||||
# ReversePath was added together with several Spacing properties in v1.1.
|
||||
# V1.1 props should be OK if it is present.
|
||||
if hasattr(obj, "ReversePath"):
|
||||
# ReversePath was added together with several Spacing properties in v1.1,
|
||||
# and PlacementList property was added for non-link arrays in v1.1,
|
||||
# obj should be OK if both are present:
|
||||
if hasattr(obj, "ReversePath") and hasattr(obj, "PlacementList"):
|
||||
return
|
||||
|
||||
# Fuse property was added in v1.0. Check if it is already present to
|
||||
# correctly issue warning.
|
||||
fuse_was_present = hasattr(obj, "Fuse")
|
||||
|
||||
self.set_properties(obj)
|
||||
if hasattr(obj, "PathObj"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'PathObj' property to 'PathObject'"))
|
||||
if hasattr(obj, "PathSubs"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'PathSubs' property to 'PathSubelements'"))
|
||||
if hasattr(obj, "Xlate"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'Xlate' property to 'ExtraTranslation'"))
|
||||
if not hasattr(obj, "Fuse"):
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
if obj.getGroupOfProperty("Count") != "Spacing":
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "moved 'Count' property to 'Spacing' subsection"))
|
||||
if not hasattr(obj, "ReversePath"):
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added 'ReversePath', 'SpacingMode', 'SpacingUnit', 'UseSpacingPattern' and 'SpacingPattern' properties"))
|
||||
if not hasattr(obj, "PlacementList"):
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added hidden property 'PlacementList'"))
|
||||
|
||||
self.set_properties(obj)
|
||||
obj.setGroupOfProperty("Count", "Spacing")
|
||||
if hasattr(obj, "PathObj"):
|
||||
obj.PathObject = obj.PathObj
|
||||
obj.removeProperty("PathObj")
|
||||
if hasattr(obj, "PathSubs"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'PathSubs' property to 'PathSubelements'"))
|
||||
obj.PathSubelements = obj.PathSubs
|
||||
obj.removeProperty("PathSubs")
|
||||
if hasattr(obj, "Xlate"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'Xlate' property to 'ExtraTranslation'"))
|
||||
obj.ExtraTranslation = obj.Xlate
|
||||
obj.removeProperty("Xlate")
|
||||
if not fuse_was_present:
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
obj.setGroupOfProperty("Count", "Spacing")
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "moved 'Count' to 'Spacing' subsection"))
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added 'ReversePath', 'SpacingMode', 'SpacingUnit', 'UseSpacingPattern' and 'SpacingPattern' properties"))
|
||||
self.execute(obj) # Required to update PlacementList.
|
||||
|
||||
|
||||
# Alias for compatibility with v0.18 and earlier
|
||||
|
||||
@@ -127,6 +127,16 @@ class PathTwistedArray(DraftLink):
|
||||
obj.ExpandArray = False
|
||||
obj.setPropertyStatus('Shape', 'Transient')
|
||||
|
||||
if not self.use_link:
|
||||
if "PlacementList" not in properties:
|
||||
_tip = QT_TRANSLATE_NOOP("App::Property",
|
||||
"The placement for each array element")
|
||||
obj.addProperty("App::PropertyPlacementList",
|
||||
"PlacementList",
|
||||
"Objects",
|
||||
_tip)
|
||||
obj.PlacementList = []
|
||||
|
||||
def linkSetup(self, obj):
|
||||
"""Set up the object as a link object."""
|
||||
super().linkSetup(obj)
|
||||
@@ -134,11 +144,18 @@ class PathTwistedArray(DraftLink):
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
super().onDocumentRestored(obj)
|
||||
# Fuse property was added in v1.0, obj should be OK if it is present:
|
||||
if hasattr(obj, "Fuse"):
|
||||
# Fuse property was added in v1.0 and PlacementList property was added
|
||||
# for non-link arrays in v1.1, obj should be OK if both are present:
|
||||
if hasattr(obj, "Fuse") and hasattr(obj, "PlacementList"):
|
||||
return
|
||||
|
||||
if not hasattr(obj, "Fuse"):
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
if not hasattr(obj, "PlacementList"):
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added hidden property 'PlacementList'"))
|
||||
|
||||
self.set_properties(obj)
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
self.execute(obj) # Required to update PlacementList.
|
||||
|
||||
def execute(self, obj):
|
||||
"""Execute when the object is created or recomputed."""
|
||||
|
||||
@@ -114,6 +114,16 @@ class PointArray(DraftLink):
|
||||
_tip)
|
||||
obj.setPropertyStatus('Shape', 'Transient')
|
||||
|
||||
if not self.use_link:
|
||||
if "PlacementList" not in properties:
|
||||
_tip = QT_TRANSLATE_NOOP("App::Property",
|
||||
"The placement for each array element")
|
||||
obj.addProperty("App::PropertyPlacementList",
|
||||
"PlacementList",
|
||||
"Objects",
|
||||
_tip)
|
||||
obj.PlacementList = []
|
||||
|
||||
def execute(self, obj):
|
||||
"""Run when the object is created or recomputed."""
|
||||
if self.props_changed_placement_only(obj) \
|
||||
@@ -132,18 +142,25 @@ class PointArray(DraftLink):
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
super().onDocumentRestored(obj)
|
||||
# Fuse property was added in v1.0, obj should be OK if it is present:
|
||||
if hasattr(obj, "Fuse"):
|
||||
# Fuse property was added in v1.0 and PlacementList property was added
|
||||
# for non-link arrays in v1.1, obj should be OK if both are present:
|
||||
if hasattr(obj, "Fuse") and hasattr(obj, "PlacementList"):
|
||||
return
|
||||
|
||||
if not hasattr(obj, "ExtraPlacement"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "added 'ExtraPlacement' property"))
|
||||
self.set_properties(obj)
|
||||
if hasattr(obj, "PointList"):
|
||||
_wrn("v0.19, " + obj.Label + ", " + translate("draft", "migrated 'PointList' property to 'PointObject'"))
|
||||
if not hasattr(obj, "Fuse"):
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
if not hasattr(obj, "PlacementList"):
|
||||
_wrn("v1.1, " + obj.Label + ", " + translate("draft", "added hidden property 'PlacementList'"))
|
||||
|
||||
self.set_properties(obj)
|
||||
if hasattr(obj, "PointList"):
|
||||
obj.PointObject = obj.PointList
|
||||
obj.removeProperty("PointList")
|
||||
_wrn("v1.0, " + obj.Label + ", " + translate("draft", "added 'Fuse' property"))
|
||||
|
||||
self.execute(obj) # Required to update PlacementList.
|
||||
|
||||
def remove_equal_vecs (vec_list):
|
||||
"""Remove equal vectors from a list.
|
||||
|
||||
Reference in New Issue
Block a user