diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index fe8adf90a3..81de3c8b52 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -611,7 +611,23 @@ class _Structure(ArchComponent.Component): pl = obj.PropertiesList if not "Tool" in pl: - obj.addProperty("App::PropertyLink","Tool","Structure",QT_TRANSLATE_NOOP("App::Property","An optional extrusion path for this element")) + obj.addProperty("App::PropertyLinkSubList", "Tool", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element")) + if not "ComputedLength" in pl: + obj.addProperty("App::PropertyDistance", "ComputedLength", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "The computed length of the extrusion path"), 1) + if not "ToolOffsetFirst" in pl: + obj.addProperty("App::PropertyDistance", "ToolOffsetFirst", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Start offset distance along the extrusion path (positive: extend, negative: trim")) + if not "ToolOffsetLast" in pl: + obj.addProperty("App::PropertyDistance", "ToolOffsetLast", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "End offset distance along the extrusion path (positive: extend, negative: trim")) + if not "BasePerpendicularToTool" in pl: + obj.addProperty("App::PropertyBool", "BasePerpendicularToTool", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Automatically align the Base of the Structure perpendicular to the Tool axis")) + if not "BaseOffsetX" in pl: + obj.addProperty("App::PropertyDistance", "BaseOffsetX", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "X offset between the Base origin and the Tool axis (only used if BasePerpendicularToTool is True)")) + if not "BaseOffsetY" in pl: + obj.addProperty("App::PropertyDistance", "BaseOffsetY", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Y offset between the Base origin and the Tool axis (only used if BasePerpendicularToTool is True)")) + if not "BaseMirror" in pl: + obj.addProperty("App::PropertyBool", "BaseMirror", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Mirror the Base along its Y axis (only used if BasePerpendicularToTool is True)")) + if not "BaseRotation" in pl: + obj.addProperty("App::PropertyAngle", "BaseRotation", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Base rotation around the Tool axis (only used if BasePerpendicularToTool is True)")) if not "Length" in pl: obj.addProperty("App::PropertyLength","Length","Structure",QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile")) if not "Width" in pl: @@ -660,6 +676,7 @@ class _Structure(ArchComponent.Component): if not isinstance(pla,list): pla = [pla] base = [] + extrusion_length = 0.0 for i in range(len(sh)): shi = sh[i] if i < len(ev): @@ -685,10 +702,12 @@ class _Structure(ArchComponent.Component): FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object")+"\n") return base.append(shi) + extrusion_length += evi.Length if len(base) == 1: base = base[0] else: base = Part.makeCompound(base) + obj.ComputedLength = FreeCAD.Units.Quantity(extrusion_length, FreeCAD.Units.Length) if obj.Base: if hasattr(obj.Base,'Shape'): if obj.Base.Shape.isNull(): @@ -785,13 +804,30 @@ class _Structure(ArchComponent.Component): import Part baseface = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) if baseface: - if obj.Tool: - if obj.Tool.Shape: - edges = obj.Tool.Shape.Edges - if len(edges) == 1 and DraftGeomUtils.geomType(edges[0]) == "Line": - extrusion = DraftGeomUtils.vec(edges[0]) + if hasattr(obj, "Tool") and obj.Tool: + tool = obj.Tool + edges = DraftGeomUtils.get_referenced_edges(tool) + if len(edges) > 0: + extrusion = Part.Wire(Part.__sortEdges__(edges)) + if hasattr(obj, "ToolOffsetFirst"): + offset_start = float(obj.ToolOffsetFirst.getValueAs("mm")) else: - extrusion = obj.Tool.Shape.copy() + offset_start = 0.0 + if hasattr(obj, "ToolOffsetLast"): + offset_end = float(obj.ToolOffsetLast.getValueAs("mm")) + else: + offset_end = 0.0 + if offset_start != 0.0 or offset_end != 0.0: + extrusion = DraftGeomUtils.get_extended_wire(extrusion, offset_start, offset_end) + if hasattr(obj, "BasePerpendicularToTool") and obj.BasePerpendicularToTool: + pl = FreeCAD.Placement() + if hasattr(obj, "BaseRotation"): + pl.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), -obj.BaseRotation) + if hasattr(obj, "BaseOffsetX") and hasattr(obj, "BaseOffsetY"): + pl.translate(FreeCAD.Vector(obj.BaseOffsetX, obj.BaseOffsetY, 0)) + if hasattr(obj, "BaseMirror"): + pl.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 1, 0), 180) + baseface.Placement = DraftGeomUtils.get_placement_perpendicular_to_wire(extrusion).multiply(pl) else: if obj.Normal.Length: normal = Vector(obj.Normal).normalize() @@ -813,6 +849,8 @@ class _Structure(ArchComponent.Component): base, placement = self.rebase(baseface) inverse_placement = placement.inverse() if extrusion: + if len(extrusion.Edges) == 1 and DraftGeomUtils.geomType(extrusion.Edges[0]) == "Line": + extrusion = DraftGeomUtils.vec(extrusion.Edges[0]) if isinstance(extrusion, FreeCAD.Vector): extrusion = inverse_placement.Rotation.multVec(extrusion) elif normal: @@ -848,8 +886,8 @@ class _Structure(ArchComponent.Component): ev = extdata[2].Rotation.multVec(extdata[1]) nodes.Placement = nodes.Placement.multiply(extdata[2]) if IfcType not in ["Slab"]: - if obj.Tool: - nodes = obj.Tool.Shape + if not isinstance(extdata[1], FreeCAD.Vector): + nodes = extdata[1] elif extdata[1].Length > 0: if hasattr(nodes,"CenterOfMass"): import Part @@ -1044,45 +1082,56 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): def __init__(self,obj): ArchComponent.ComponentTaskPanel.__init__(self) - self.optwid = QtGui.QWidget() - self.optwid.setWindowTitle(QtGui.QApplication.translate("Arch", "Node Tools", None)) - lay = QtGui.QVBoxLayout(self.optwid) + self.nodes_widget = QtGui.QWidget() + self.nodes_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Node Tools", None)) + lay = QtGui.QVBoxLayout(self.nodes_widget) - self.resetButton = QtGui.QPushButton(self.optwid) + self.resetButton = QtGui.QPushButton(self.nodes_widget) self.resetButton.setIcon(QtGui.QIcon(":/icons/edit-undo.svg")) self.resetButton.setText(QtGui.QApplication.translate("Arch", "Reset nodes", None)) lay.addWidget(self.resetButton) QtCore.QObject.connect(self.resetButton, QtCore.SIGNAL("clicked()"), self.resetNodes) - self.editButton = QtGui.QPushButton(self.optwid) + self.editButton = QtGui.QPushButton(self.nodes_widget) self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg")) self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit nodes", None)) lay.addWidget(self.editButton) QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes) - self.extendButton = QtGui.QPushButton(self.optwid) + self.extendButton = QtGui.QPushButton(self.nodes_widget) self.extendButton.setIcon(QtGui.QIcon(":/icons/Snap_Perpendicular.svg")) self.extendButton.setText(QtGui.QApplication.translate("Arch", "Extend nodes", None)) self.extendButton.setToolTip(QtGui.QApplication.translate("Arch", "Extends the nodes of this element to reach the nodes of another element", None)) lay.addWidget(self.extendButton) QtCore.QObject.connect(self.extendButton, QtCore.SIGNAL("clicked()"), self.extendNodes) - self.connectButton = QtGui.QPushButton(self.optwid) + self.connectButton = QtGui.QPushButton(self.nodes_widget) self.connectButton.setIcon(QtGui.QIcon(":/icons/Snap_Intersection.svg")) self.connectButton.setText(QtGui.QApplication.translate("Arch", "Connect nodes", None)) self.connectButton.setToolTip(QtGui.QApplication.translate("Arch", "Connects nodes of this element with the nodes of another element", None)) lay.addWidget(self.connectButton) QtCore.QObject.connect(self.connectButton, QtCore.SIGNAL("clicked()"), self.connectNodes) - self.toggleButton = QtGui.QPushButton(self.optwid) + self.toggleButton = QtGui.QPushButton(self.nodes_widget) self.toggleButton.setIcon(QtGui.QIcon(":/icons/dagViewVisible.svg")) self.toggleButton.setText(QtGui.QApplication.translate("Arch", "Toggle all nodes", None)) self.toggleButton.setToolTip(QtGui.QApplication.translate("Arch", "Toggles all structural nodes of the document on/off", None)) lay.addWidget(self.toggleButton) QtCore.QObject.connect(self.toggleButton, QtCore.SIGNAL("clicked()"), self.toggleNodes) - self.form = [self.form,self.optwid] + self.extrusion_widget = QtGui.QWidget() + self.extrusion_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Extrusion Tools", None)) + lay = QtGui.QVBoxLayout(self.extrusion_widget) + + self.selectToolButton = QtGui.QPushButton(self.extrusion_widget) + self.selectToolButton.setIcon(QtGui.QIcon()) + self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Select tool...", None)) + self.selectToolButton.setToolTip(QtGui.QApplication.translate("Arch", "Select object or edges to be used as a Tool (extrusion path)", None)) + lay.addWidget(self.selectToolButton) + QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool) + + self.form = [self.form, self.nodes_widget, self.extrusion_widget] self.Object = obj self.observer = None self.nodevis = None @@ -1177,6 +1226,42 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): self.nodevis.append([obj,obj.ViewObject.ShowNodes]) obj.ViewObject.ShowNodes = True + def setSelectionFromTool(self): + FreeCADGui.Selection.clearSelection() + if hasattr(self.Object, "Tool"): + tool = self.Object.Tool + if hasattr(tool, "Shape") and tool.Shape: + FreeCADGui.Selection.addSelection(tool) + else: + if not isinstance(tool, list): + tool = [tool] + for o, subs in tool: + FreeCADGui.Selection.addSelection(o, subs) + QtCore.QObject.disconnect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool) + QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setToolFromSelection) + self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Done", None)) + + def setToolFromSelection(self): + objectList = [] + selEx = FreeCADGui.Selection.getSelectionEx() + for selExi in selEx: + if len(selExi.SubElementNames) == 0: + # Add entirely selected objects + objectList.append(selExi.Object) + else: + subElementsNames = [subElementName for subElementName in selExi.SubElementNames if subElementName.startswith("Edge")] + # Check that at least an edge is selected from the object's shape + if len(subElementsNames) > 0: + objectList.append((selExi.Object, subElementsNames)) + if self.Object.getTypeIdOfProperty("Tool") != "App::PropertyLinkSubList": + # Upgrade property Tool from App::PropertyLink to App::PropertyLinkSubList (note: Undo/Redo fails) + self.Object.removeProperty("Tool") + self.Object.addProperty("App::PropertyLinkSubList", "Tool", "Structure", QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element")) + self.Object.Tool = objectList + QtCore.QObject.disconnect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setToolFromSelection) + QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool) + self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Select tool...", None)) + def accept(self): if self.observer: