| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| |
|
| | __title__ = "FreeCAD Structure" |
| | __author__ = "Yorik van Havre" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import FreeCAD |
| | import ArchComponent |
| | import ArchCommands |
| | import ArchProfile |
| | import Draft |
| | import DraftVecUtils |
| |
|
| | from FreeCAD import Vector |
| | from draftutils import params |
| | from draftutils import gui_utils |
| |
|
| | if FreeCAD.GuiUp: |
| | from PySide import QtCore, QtGui |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| | import FreeCADGui |
| | import ArchPrecast |
| | import draftguitools.gui_trackers as DraftTrackers |
| | from draftutils.translate import translate |
| | else: |
| | |
| | def translate(ctxt, txt): |
| | return txt |
| |
|
| | def QT_TRANSLATE_NOOP(ctxt, txt): |
| | return txt |
| |
|
| | |
| |
|
| |
|
| | |
| | Categories = [] |
| | Presets = ArchProfile.readPresets() |
| | for pre in Presets: |
| | if pre[1] not in Categories: |
| | Categories.append(pre[1]) |
| |
|
| |
|
| | def makeStructure(baseobj=None, length=None, width=None, height=None, name=None): |
| | """makeStructure([baseobj],[length],[width],[height],[name]): creates a |
| | structure element based on the given profile object and the given |
| | extrusion height. If no base object is given, you can also specify |
| | length and width for a cubic object.""" |
| |
|
| | if not FreeCAD.ActiveDocument: |
| | FreeCAD.Console.PrintError("No active document. Aborting\n") |
| | return |
| | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Structure") |
| | _Structure(obj) |
| | if FreeCAD.GuiUp: |
| | _ViewProviderStructure(obj.ViewObject) |
| | if baseobj: |
| | obj.Base = baseobj |
| | if FreeCAD.GuiUp: |
| | obj.Base.ViewObject.hide() |
| | if width: |
| | obj.Width = width |
| | else: |
| | obj.Width = params.get_param_arch("StructureWidth") |
| | if height: |
| | obj.Height = height |
| | else: |
| | if not length: |
| | obj.Height = params.get_param_arch("StructureHeight") |
| | if length: |
| | obj.Length = length |
| | else: |
| | if not baseobj: |
| | |
| | |
| | obj.Length = params.get_param_arch("StructureLength") |
| | if baseobj: |
| | w = 0 |
| | h = 0 |
| | if hasattr(baseobj, "Width") and hasattr(baseobj, "Height"): |
| | w = baseobj.Width.Value |
| | h = baseobj.Height.Value |
| | elif hasattr(baseobj, "Length") and hasattr(baseobj, "Width"): |
| | w = baseobj.Length.Value |
| | h = baseobj.Width.Value |
| | elif hasattr(baseobj, "Length") and hasattr(baseobj, "Height"): |
| | w = baseobj.Length.Value |
| | h = baseobj.Height.Value |
| | if w and h: |
| | if length and not height: |
| | obj.Width = w |
| | obj.Height = h |
| | elif height and not length: |
| | obj.Width = w |
| | obj.Length = h |
| |
|
| | if obj.Length > obj.Height: |
| | obj.IfcType = "Beam" |
| | obj.Label = name if name else translate("Arch", "Beam") |
| | elif obj.Height > obj.Length: |
| | obj.IfcType = "Column" |
| | obj.Label = name if name else translate("Arch", "Column") |
| | return obj |
| |
|
| |
|
| | def makeStructuralSystem(objects=[], axes=[], name=None): |
| | """makeStructuralSystem([objects],[axes],[name]): makes a structural system |
| | based on the given objects and axes""" |
| |
|
| | if not FreeCAD.ActiveDocument: |
| | FreeCAD.Console.PrintError("No active document. Aborting\n") |
| | return |
| | result = [] |
| | if not axes: |
| | print("At least one axis must be given") |
| | return |
| | if objects: |
| | if not isinstance(objects, list): |
| | objects = [objects] |
| | else: |
| | objects = [None] |
| | for o in objects: |
| | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StructuralSystem") |
| | obj.Label = name if name else translate("Arch", "StructuralSystem") |
| | _StructuralSystem(obj) |
| | if FreeCAD.GuiUp: |
| | _ViewProviderStructuralSystem(obj.ViewObject) |
| | if o: |
| | obj.Base = o |
| | obj.Axes = axes |
| | result.append(obj) |
| | if FreeCAD.GuiUp and o: |
| | o.ViewObject.hide() |
| | Draft.formatObject(obj, o) |
| | FreeCAD.ActiveDocument.recompute() |
| | if len(result) == 1: |
| | return result[0] |
| | else: |
| | return result |
| |
|
| |
|
| | def placeAlongEdge(p1, p2, horizontal=False): |
| | """placeAlongEdge(p1,p2,[horizontal]): returns a Placement positioned at p1, with Z axis oriented towards p2. |
| | If horizontal is True, then the X axis is oriented towards p2, not the Z axis""" |
| |
|
| | pl = FreeCAD.Placement() |
| | pl.Base = p1 |
| | import WorkingPlane |
| |
|
| | up = WorkingPlane.get_working_plane(update=False).axis |
| | zaxis = p2.sub(p1) |
| | yaxis = up.cross(zaxis) |
| | if yaxis.Length > 0: |
| | xaxis = zaxis.cross(yaxis) |
| | if horizontal: |
| | pl.Rotation = FreeCAD.Rotation(zaxis, yaxis, xaxis, "ZXY") |
| | else: |
| | pl.Rotation = FreeCAD.Rotation(xaxis, yaxis, zaxis, "ZXY") |
| | pl.Rotation = FreeCAD.Rotation( |
| | pl.Rotation.multVec(FreeCAD.Vector(0, 0, 1)), 90 |
| | ).multiply(pl.Rotation) |
| | return pl |
| |
|
| |
|
| | class CommandStructuresFromSelection: |
| | """The Arch Structures from selection command definition.""" |
| |
|
| | def __init__(self): |
| | pass |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": "Arch_MultipleStructures", |
| | "MenuText": QT_TRANSLATE_NOOP("Arch_StructuresFromSelection", "Multiple Structures"), |
| | "ToolTip": QT_TRANSLATE_NOOP( |
| | "Arch_StructuresFromSelection", |
| | "Creates multiple BIM Structures from a selected base, using each selected edge as an extrusion path", |
| | ), |
| | } |
| |
|
| | def IsActive(self): |
| | v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") |
| | return v |
| |
|
| | def Activated(self): |
| | selex = FreeCADGui.Selection.getSelectionEx() |
| | if len(selex) >= 2: |
| | FreeCAD.ActiveDocument.openTransaction( |
| | translate("Arch", "Create Structures From Selection") |
| | ) |
| | FreeCADGui.addModule("Arch") |
| | FreeCADGui.addModule("Draft") |
| | base = selex[ |
| | 0 |
| | ].Object |
| | for selexi in selex[ |
| | 1: |
| | ]: |
| | if len(selexi.SubElementNames) == 0: |
| | subelement_names = [ |
| | "Edge" + str(i) for i in range(1, len(selexi.Object.Shape.Edges) + 1) |
| | ] |
| | else: |
| | subelement_names = [ |
| | sub for sub in selexi.SubElementNames if sub.startswith("Edge") |
| | ] |
| | for sub in subelement_names: |
| | FreeCADGui.doCommand( |
| | "structure = Arch.makeStructure(FreeCAD.ActiveDocument." + base.Name + ")" |
| | ) |
| | FreeCADGui.doCommand( |
| | "structure.Tool = (FreeCAD.ActiveDocument." |
| | + selexi.Object.Name |
| | + ", '" |
| | + sub |
| | + "')" |
| | ) |
| | FreeCADGui.doCommand("structure.BasePerpendicularToTool = True") |
| | FreeCADGui.doCommand("Draft.autogroup(structure)") |
| | FreeCAD.ActiveDocument.commitTransaction() |
| | FreeCAD.ActiveDocument.recompute() |
| | else: |
| | FreeCAD.Console.PrintError( |
| | translate( |
| | "Arch", |
| | "Select the base object first and then the edges to use as extrusion paths", |
| | ) |
| | + "\n" |
| | ) |
| |
|
| |
|
| | class CommandStructuralSystem: |
| | """The Arch Structural System command definition.""" |
| |
|
| | def __init__(self): |
| | pass |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": "Arch_StructuralSystem", |
| | "MenuText": QT_TRANSLATE_NOOP("Arch_StructuralSystem", "Structural System"), |
| | "ToolTip": QT_TRANSLATE_NOOP( |
| | "Arch_StructuralSystem", |
| | "Create a structural system from a selected structure and axis", |
| | ), |
| | } |
| |
|
| | def IsActive(self): |
| | return not FreeCAD.ActiveDocument is None |
| |
|
| | def Activated(self): |
| | sel = FreeCADGui.Selection.getSelection() |
| | if sel: |
| | st = Draft.getObjectsOfType(sel, "Structure") |
| | ax = Draft.getObjectsOfType(sel, "Axis") |
| | if ax: |
| | FreeCAD.ActiveDocument.openTransaction( |
| | translate("Arch", "Create Structural System") |
| | ) |
| | FreeCADGui.addModule("Arch") |
| | if st: |
| | FreeCADGui.doCommand( |
| | "obj = Arch.makeStructuralSystem(" |
| | + ArchCommands.getStringList(st) |
| | + ", " |
| | + ArchCommands.getStringList(ax) |
| | + ")" |
| | ) |
| | else: |
| | FreeCADGui.doCommand( |
| | "obj = Arch.makeStructuralSystem(axes = " |
| | + ArchCommands.getStringList(ax) |
| | + ")" |
| | ) |
| | FreeCADGui.addModule("Draft") |
| | FreeCADGui.doCommand("Draft.autogroup(obj)") |
| | FreeCAD.ActiveDocument.commitTransaction() |
| | FreeCAD.ActiveDocument.recompute() |
| | else: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Select at least an axis object") + "\n" |
| | ) |
| |
|
| |
|
| | class _CommandStructure: |
| | "the Arch Structure command definition" |
| |
|
| | def __init__(self): |
| |
|
| | self.beammode = False |
| |
|
| | def GetResources(self): |
| |
|
| | return { |
| | "Pixmap": "Arch_Structure", |
| | "MenuText": QT_TRANSLATE_NOOP("Arch_Structure", "Structure"), |
| | "Accel": "S, T", |
| | "ToolTip": QT_TRANSLATE_NOOP( |
| | "Arch_Structure", |
| | "Creates a structure from scratch or from a selected object (sketch, wire, face or solid)", |
| | ), |
| | } |
| |
|
| | def IsActive(self): |
| |
|
| | return not FreeCAD.ActiveDocument is None |
| |
|
| | def Activated(self): |
| |
|
| | self.doc = FreeCAD.ActiveDocument |
| | self.Width = params.get_param_arch("StructureWidth") |
| | if self.beammode: |
| | self.Height = params.get_param_arch("StructureLength") |
| | self.Length = params.get_param_arch("StructureHeight") |
| | else: |
| | self.Length = params.get_param_arch("StructureLength") |
| | self.Height = params.get_param_arch("StructureHeight") |
| | self.Profile = None |
| | self.bpoint = None |
| | self.bmode = False |
| | self.precastvalues = None |
| | self.wp = None |
| | sel = FreeCADGui.Selection.getSelection() |
| | if sel: |
| | st = Draft.getObjectsOfType(sel, "Structure") |
| | ax = Draft.getObjectsOfType(sel, "Axis") |
| | if ax: |
| | FreeCADGui.runCommand("Arch_StructuralSystem") |
| | return |
| | elif not (ax) and not (st): |
| | self.doc.openTransaction(translate("Arch", "Create Structure")) |
| | FreeCADGui.addModule("Arch") |
| | for obj in sel: |
| | FreeCADGui.doCommand( |
| | "obj = Arch.makeStructure(FreeCAD.ActiveDocument." + obj.Name + ")" |
| | ) |
| | FreeCADGui.addModule("Draft") |
| | FreeCADGui.doCommand("Draft.autogroup(obj)") |
| | self.doc.commitTransaction() |
| | self.doc.recompute() |
| | return |
| |
|
| | |
| | import WorkingPlane |
| |
|
| | FreeCAD.activeDraftCommand = self |
| | self.wp = WorkingPlane.get_working_plane() |
| | self.points = [] |
| | self.tracker = DraftTrackers.boxTracker() |
| | self.tracker.width(self.Width) |
| | self.tracker.height(self.Height) |
| | self.tracker.length(self.Length) |
| | self.tracker.setRotation(self.wp.get_placement().Rotation) |
| | self.tracker.on() |
| | self.precast = ArchPrecast._PrecastTaskPanel() |
| | self.dents = ArchPrecast._DentsTaskPanel() |
| | self.precast.Dents = self.dents |
| | if self.beammode: |
| | title = translate("Arch", "First point of the beam") |
| | else: |
| | title = translate("Arch", "Base point of column") |
| | FreeCADGui.Snapper.getPoint( |
| | callback=self.getPoint, |
| | movecallback=self.update, |
| | extradlg=[self.taskbox(), self.precast.form, self.dents.form], |
| | title=title, |
| | ) |
| | FreeCADGui.draftToolBar.continueCmd.show() |
| |
|
| | def getPoint(self, point=None, obj=None): |
| | "this function is called by the snapper when it has a 3D point" |
| |
|
| | self.bmode = self.modeb.isChecked() |
| | if point is None: |
| | FreeCAD.activeDraftCommand = None |
| | FreeCADGui.Snapper.off() |
| | self.tracker.finalize() |
| | return |
| | if self.bmode and (self.bpoint is None): |
| | self.bpoint = point |
| | FreeCADGui.Snapper.getPoint( |
| | last=point, |
| | callback=self.getPoint, |
| | movecallback=self.update, |
| | extradlg=[self.taskbox(), self.precast.form, self.dents.form], |
| | title=translate("Arch", "Next point") + ":", |
| | mode="line", |
| | ) |
| | return |
| | FreeCAD.activeDraftCommand = None |
| | FreeCADGui.Snapper.off() |
| | self.tracker.off() |
| | horiz = True |
| | self.doc.openTransaction(translate("Arch", "Create Structure")) |
| | FreeCADGui.addModule("Arch") |
| | FreeCADGui.addModule("WorkingPlane") |
| | if self.bmode: |
| | self.Length = point.sub(self.bpoint).Length |
| | params.set_param_arch("StructureHeight", self.Length) |
| | if self.Profile is not None: |
| | try: |
| | self.precastvalues = self.precast.getValues() |
| | except Exception: |
| | pass |
| | if ("Precast" in self.Profile) and self.precastvalues: |
| | |
| | self.precastvalues["PrecastType"] = self.Profile.split("_")[1] |
| | self.precastvalues["Length"] = self.Length |
| | self.precastvalues["Width"] = self.Width |
| | self.precastvalues["Height"] = self.Height |
| | argstring = "" |
| | |
| | if self.bmode: |
| | delta = FreeCAD.Vector(0, 0 - self.Width / 2, 0) |
| | else: |
| | delta = FreeCAD.Vector(-self.Length / 2, -self.Width / 2, 0) |
| | delta = self.wp.get_global_coords(delta, as_vector=True) |
| | point = point.add(delta) |
| | if self.bpoint: |
| | self.bpoint = self.bpoint.add(delta) |
| | |
| | for pair in self.precastvalues.items(): |
| | argstring += pair[0].lower() + "=" |
| | if isinstance(pair[1], str): |
| | argstring += '"' + pair[1] + '",' |
| | else: |
| | argstring += str(pair[1]) + "," |
| | FreeCADGui.addModule("ArchPrecast") |
| | FreeCADGui.doCommand("s = ArchPrecast.makePrecast(" + argstring + ")") |
| | else: |
| | |
| | FreeCADGui.doCommand("p = Arch.makeProfile(" + str(self.Profile) + ")") |
| | if ( |
| | abs(self.Length - self.Profile[4]) >= 0.1 |
| | ) or self.bmode: |
| | |
| | FreeCADGui.doCommand( |
| | "s = Arch.makeStructure(p,length=" + str(self.Length) + ")" |
| | ) |
| | horiz = False |
| | else: |
| | |
| | FreeCADGui.doCommand( |
| | "s = Arch.makeStructure(p,height=" + str(self.Height) + ")" |
| | ) |
| | |
| | |
| | FreeCADGui.doCommand('s.Profile = "' + self.Profile[2] + '"') |
| | else: |
| | FreeCADGui.doCommand( |
| | "s = Arch.makeStructure(length=" |
| | + str(self.Length) |
| | + ",width=" |
| | + str(self.Width) |
| | + ",height=" |
| | + str(self.Height) |
| | + ")" |
| | ) |
| |
|
| | |
| | if self.bmode and self.bpoint: |
| | FreeCADGui.doCommand( |
| | "s.Placement = Arch.placeAlongEdge(" |
| | + DraftVecUtils.toString(self.bpoint) |
| | + "," |
| | + DraftVecUtils.toString(point) |
| | + "," |
| | + str(horiz) |
| | + ")" |
| | ) |
| | else: |
| | FreeCADGui.doCommand("s.Placement.Base = " + DraftVecUtils.toString(point)) |
| | FreeCADGui.doCommand("wp = WorkingPlane.get_working_plane()") |
| | FreeCADGui.doCommand( |
| | "s.Placement.Rotation = s.Placement.Rotation.multiply(wp.get_placement().Rotation)" |
| | ) |
| |
|
| | FreeCADGui.addModule("Draft") |
| | FreeCADGui.doCommand("Draft.autogroup(s)") |
| | self.doc.commitTransaction() |
| | self.doc.recompute() |
| | |
| | self.tracker.finalize() |
| | if FreeCADGui.draftToolBar.continueMode: |
| | self.Activated() |
| |
|
| | def _createItemlist(self, baselist): |
| | "create nice labels for presets in the task panel" |
| |
|
| | ilist = [] |
| | for p in baselist: |
| | f = FreeCAD.Units.Quantity(p[4], FreeCAD.Units.Length).getUserPreferred() |
| | d = params.get_param("Decimals", path="Units") |
| | s1 = str(round(p[4] / f[1], d)) |
| | s2 = str(round(p[5] / f[1], d)) |
| | s3 = str(f[2]) |
| | ilist.append(p[2] + " (" + s1 + "x" + s2 + s3 + ")") |
| | return ilist |
| |
|
| | def taskbox(self): |
| | "sets up a taskbox widget" |
| |
|
| | w = QtGui.QWidget() |
| | ui = FreeCADGui.UiLoader() |
| | w.setWindowTitle(translate("Arch", "Structure options")) |
| | grid = QtGui.QGridLayout(w) |
| |
|
| | |
| | labelmode = QtGui.QLabel(translate("Arch", "Parameters of the structure") + ":") |
| | self.modeb = QtGui.QRadioButton(translate("Arch", "Beam")) |
| | self.modec = QtGui.QRadioButton(translate("Arch", "Column")) |
| | if self.bpoint or self.beammode: |
| | self.modeb.setChecked(True) |
| | else: |
| | self.modec.setChecked(True) |
| | grid.addWidget(labelmode, 0, 0, 1, 2) |
| | grid.addWidget(self.modeb, 1, 0, 1, 1) |
| | grid.addWidget(self.modec, 1, 1, 1, 1) |
| |
|
| | |
| | labelc = QtGui.QLabel(translate("Arch", "Category")) |
| | self.valuec = QtGui.QComboBox() |
| | self.valuec.addItems([" ", "Precast concrete"] + Categories) |
| | grid.addWidget(labelc, 2, 0, 1, 1) |
| | grid.addWidget(self.valuec, 2, 1, 1, 1) |
| |
|
| | |
| | labelp = QtGui.QLabel(translate("Arch", "Preset")) |
| | self.vPresets = QtGui.QComboBox() |
| | self.pSelect = [None] |
| | fpresets = [" "] |
| | self.vPresets.addItems(fpresets) |
| | grid.addWidget(labelp, 3, 0, 1, 1) |
| | grid.addWidget(self.vPresets, 3, 1, 1, 1) |
| |
|
| | |
| | label1 = QtGui.QLabel(translate("Arch", "Length")) |
| | self.vLength = ui.createWidget("Gui::InputField") |
| | if self.modeb.isChecked(): |
| | self.vLength.setText( |
| | FreeCAD.Units.Quantity(self.Height, FreeCAD.Units.Length).UserString |
| | ) |
| | else: |
| | self.vLength.setText( |
| | FreeCAD.Units.Quantity(self.Length, FreeCAD.Units.Length).UserString |
| | ) |
| | grid.addWidget(label1, 4, 0, 1, 1) |
| | grid.addWidget(self.vLength, 4, 1, 1, 1) |
| |
|
| | |
| | label2 = QtGui.QLabel(translate("Arch", "Width")) |
| | self.vWidth = ui.createWidget("Gui::InputField") |
| | self.vWidth.setText(FreeCAD.Units.Quantity(self.Width, FreeCAD.Units.Length).UserString) |
| | grid.addWidget(label2, 5, 0, 1, 1) |
| | grid.addWidget(self.vWidth, 5, 1, 1, 1) |
| |
|
| | |
| | label3 = QtGui.QLabel(translate("Arch", "Height")) |
| | self.vHeight = ui.createWidget("Gui::InputField") |
| | if self.modeb.isChecked(): |
| | self.vHeight.setText( |
| | FreeCAD.Units.Quantity(self.Length, FreeCAD.Units.Length).UserString |
| | ) |
| | else: |
| | self.vHeight.setText( |
| | FreeCAD.Units.Quantity(self.Height, FreeCAD.Units.Length).UserString |
| | ) |
| | grid.addWidget(label3, 6, 0, 1, 1) |
| | grid.addWidget(self.vHeight, 6, 1, 1, 1) |
| |
|
| | |
| | value4 = QtGui.QPushButton(translate("Arch", "Switch Length/Height")) |
| | grid.addWidget(value4, 7, 0, 1, 1) |
| | value5 = QtGui.QPushButton(translate("Arch", "Switch Length/Width")) |
| | grid.addWidget(value5, 7, 1, 1, 1) |
| |
|
| | |
| | QtCore.QObject.connect( |
| | self.valuec, QtCore.SIGNAL("currentIndexChanged(int)"), self.setCategory |
| | ) |
| | QtCore.QObject.connect( |
| | self.vPresets, QtCore.SIGNAL("currentIndexChanged(int)"), self.setPreset |
| | ) |
| | QtCore.QObject.connect(self.vLength, QtCore.SIGNAL("valueChanged(double)"), self.setLength) |
| | QtCore.QObject.connect(self.vWidth, QtCore.SIGNAL("valueChanged(double)"), self.setWidth) |
| | QtCore.QObject.connect(self.vHeight, QtCore.SIGNAL("valueChanged(double)"), self.setHeight) |
| | QtCore.QObject.connect(value4, QtCore.SIGNAL("pressed()"), self.rotateLH) |
| | QtCore.QObject.connect(value5, QtCore.SIGNAL("pressed()"), self.rotateLW) |
| | QtCore.QObject.connect(self.modeb, QtCore.SIGNAL("toggled(bool)"), self.switchLH) |
| |
|
| | |
| | stored = params.get_param_arch("StructurePreset") |
| | if stored: |
| | if stored.lower().startswith("precast_"): |
| | self.valuec.setCurrentIndex(1) |
| | tp = stored.split("_")[1] |
| | if tp and (tp in self.precast.PrecastTypes): |
| | self.vPresets.setCurrentIndex(self.precast.PrecastTypes.index(tp)) |
| | elif ";" in stored: |
| | stored = stored.split(";") |
| | if len(stored) >= 3: |
| | if stored[1] in Categories: |
| | self.valuec.setCurrentIndex(2 + Categories.index(stored[1])) |
| | ps = [p[2] for p in self.pSelect] |
| | if stored[2] in ps: |
| | self.vPresets.setCurrentIndex(ps.index(stored[2])) |
| | return w |
| |
|
| | def update(self, point, info): |
| | "this function is called by the Snapper when the mouse is moved" |
| |
|
| | if FreeCADGui.Control.activeDialog(): |
| | try: |
| | self.precastvalues = self.precast.getValues() |
| | except Exception: |
| | pass |
| | if self.Height >= self.Length: |
| | delta = Vector(0, 0, self.Height / 2) |
| | else: |
| | delta = Vector(self.Length / 2, 0, 0) |
| | delta = self.wp.get_global_coords(delta, as_vector=True) |
| | if self.modec.isChecked(): |
| | self.tracker.pos(point.add(delta)) |
| | self.tracker.on() |
| | else: |
| | if self.bpoint: |
| | delta = Vector(0, 0, -self.Height / 2) |
| | delta = self.wp.get_global_coords(delta, as_vector=True) |
| | self.tracker.update([self.bpoint.add(delta), point.add(delta)]) |
| | self.tracker.on() |
| | l = (point.sub(self.bpoint)).Length |
| | self.vLength.setText(FreeCAD.Units.Quantity(l, FreeCAD.Units.Length).UserString) |
| | else: |
| | self.tracker.off() |
| |
|
| | def setWidth(self, d): |
| |
|
| | self.Width = d |
| | self.tracker.width(d) |
| | params.set_param_arch("StructureWidth", d) |
| |
|
| | def setHeight(self, d): |
| |
|
| | self.Height = d |
| | self.tracker.height(d) |
| | if self.modeb.isChecked(): |
| | params.set_param_arch("StructureLength", d) |
| | else: |
| | params.set_param_arch("StructureHeight", d) |
| |
|
| | def setLength(self, d): |
| |
|
| | self.Length = d |
| | self.tracker.length(d) |
| | if self.modeb.isChecked(): |
| | params.set_param_arch("StructureHeight", d) |
| | else: |
| | params.set_param_arch("StructureLength", d) |
| |
|
| | def setCategory(self, i): |
| |
|
| | self.vPresets.clear() |
| | if i > 1: |
| | self.precast.form.hide() |
| | self.pSelect = [p for p in Presets if p[1] == Categories[i - 2]] |
| | fpresets = self._createItemlist(self.pSelect) |
| | self.vPresets.addItems(fpresets) |
| | self.setPreset(0) |
| | elif i == 1: |
| | self.precast.form.show() |
| | self.pSelect = self.precast.PrecastTypes |
| | fpresets = self.precast.PrecastTypes |
| | self.vPresets.addItems(fpresets) |
| | self.setPreset(0) |
| | else: |
| | self.precast.form.hide() |
| | self.pSelect = [None] |
| | fpresets = [" "] |
| | self.vPresets.addItems(fpresets) |
| | params.set_param_arch("StructurePreset", "") |
| |
|
| | def setPreset(self, i): |
| |
|
| | self.Profile = None |
| | elt = self.pSelect[i] |
| | if elt: |
| | if elt in self.precast.PrecastTypes: |
| | self.precast.setPreset(elt) |
| | self.Profile = "Precast_" + elt |
| | if elt in ["Pillar", "Beam"]: |
| | self.dents.form.show() |
| | else: |
| | self.dents.form.hide() |
| | params.set_param_arch("StructurePreset", self.Profile) |
| | else: |
| | self.vLength.setText( |
| | FreeCAD.Units.Quantity(float(elt[4]), FreeCAD.Units.Length).UserString |
| | ) |
| | self.vWidth.setText( |
| | FreeCAD.Units.Quantity(float(elt[5]), FreeCAD.Units.Length).UserString |
| | ) |
| | self.Profile = elt |
| | params.set_param_arch("StructurePreset", ";".join([str(i) for i in self.Profile])) |
| |
|
| | def switchLH(self, bmode): |
| |
|
| | if bmode: |
| | self.bmode = True |
| | if self.Height > self.Length: |
| | self.rotateLH() |
| | else: |
| | self.bmode = False |
| | if self.Length > self.Height: |
| | self.rotateLH() |
| | self.tracker.setRotation(FreeCAD.Rotation()) |
| |
|
| | def rotateLH(self): |
| |
|
| | l = self.vLength.text() |
| | h = self.vHeight.text() |
| | self.vLength.setText(h) |
| | self.vHeight.setText(l) |
| |
|
| | def rotateLW(self): |
| |
|
| | l = self.vLength.text() |
| | w = self.vWidth.text() |
| | self.vLength.setText(w) |
| | self.vWidth.setText(l) |
| |
|
| |
|
| | class _Structure(ArchComponent.Component): |
| | "The Structure object" |
| |
|
| | def __init__(self, obj): |
| |
|
| | ArchComponent.Component.__init__(self, obj) |
| | self.Type = "Structure" |
| | self.setProperties(obj) |
| | obj.IfcType = "Beam" |
| |
|
| | def setProperties(self, obj): |
| |
|
| | pl = obj.PropertiesList |
| | if not "Tool" in pl: |
| | obj.addProperty( |
| | "App::PropertyLinkSubList", |
| | "Tool", |
| | "ExtrusionPath", |
| | QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element"), |
| | locked=True, |
| | ) |
| | if not "ComputedLength" in pl: |
| | obj.addProperty( |
| | "App::PropertyDistance", |
| | "ComputedLength", |
| | "ExtrusionPath", |
| | QT_TRANSLATE_NOOP("App::Property", "The computed length of the extrusion path"), |
| | 1, |
| | locked=True, |
| | ) |
| | 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)", |
| | ), |
| | locked=True, |
| | ) |
| | 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)", |
| | ), |
| | locked=True, |
| | ) |
| | 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", |
| | ), |
| | locked=True, |
| | ) |
| | 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)", |
| | ), |
| | locked=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)", |
| | ), |
| | locked=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)", |
| | ), |
| | locked=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)", |
| | ), |
| | locked=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" |
| | ), |
| | locked=True, |
| | ) |
| | if not "Width" in pl: |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "Width", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "The width of this element, if not based on a profile" |
| | ), |
| | locked=True, |
| | ) |
| | if not "Height" in pl: |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "Height", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The height or extrusion depth of this element. Keep 0 for automatic", |
| | ), |
| | locked=True, |
| | ) |
| | if not "Normal" in pl: |
| | obj.addProperty( |
| | "App::PropertyVector", |
| | "Normal", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)", |
| | ), |
| | locked=True, |
| | ) |
| | if not "Nodes" in pl: |
| | obj.addProperty( |
| | "App::PropertyVectorList", |
| | "Nodes", |
| | "Structure", |
| | QT_TRANSLATE_NOOP("App::Property", "The structural nodes of this element"), |
| | locked=True, |
| | ) |
| | if not "Profile" in pl: |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "Profile", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "A description of the standard profile this element is based upon", |
| | ), |
| | locked=True, |
| | ) |
| | if not "NodesOffset" in pl: |
| | obj.addProperty( |
| | "App::PropertyDistance", |
| | "NodesOffset", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "Offset distance between the centerline and the nodes line" |
| | ), |
| | locked=True, |
| | ) |
| | if not "FaceMaker" in pl: |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "FaceMaker", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "The facemaker type to use to build the profile of this object" |
| | ), |
| | locked=True, |
| | ) |
| | obj.FaceMaker = ["None", "Simple", "Cheese", "Bullseye"] |
| | if not "ArchSketchData" in pl: |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "ArchSketchData", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Use Base ArchSketch (if used) data (e.g. widths, aligns, offsets) instead of Wall's properties", |
| | ), |
| | locked=True, |
| | ) |
| | obj.ArchSketchData = True |
| | if not "ArchSketchEdges" in pl: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "ArchSketchEdges", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Selected edges (or group of edges) of the base ArchSketch, to use in creating the shape of this BIM Structure (instead of using all the Base shape's edges by default). Input are index numbers of edges or groups.", |
| | ), |
| | locked=True, |
| | ) |
| | else: |
| | |
| | type = obj.getTypeIdOfProperty("ArchSketchEdges") |
| | if type == "App::PropertyIntegerList": |
| | oldIntValue = obj.ArchSketchEdges |
| | newStrValue = [str(x) for x in oldIntValue] |
| | obj.removeProperty("ArchSketchEdges") |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "ArchSketchEdges", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Selected edges (or group of edges) of the base ArchSketch, to use in creating the shape of this BIM Structure (instead of using all the Base shape's edges by default). Input are index numbers of edges or groups.", |
| | ), |
| | locked=True, |
| | ) |
| | obj.ArchSketchEdges = newStrValue |
| | if not hasattr(obj, "ArchSketchPropertySet"): |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "ArchSketchPropertySet", |
| | "Structure", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Select User Defined PropertySet to use in creating variant shape, with same ArchSketch ", |
| | ), |
| | locked=True, |
| | ) |
| | obj.ArchSketchPropertySet = ["Default"] |
| | if not hasattr(self, "ArchSkPropSetPickedUuid"): |
| | self.ArchSkPropSetPickedUuid = "" |
| | if not hasattr(self, "ArchSkPropSetListPrev"): |
| | self.ArchSkPropSetListPrev = [] |
| |
|
| | def dumps(self): |
| | dump = super().dumps() |
| | if not isinstance(dump, tuple): |
| | dump = (dump,) |
| | dump = dump + (self.ArchSkPropSetPickedUuid, self.ArchSkPropSetListPrev) |
| | return dump |
| |
|
| | def loads(self, state): |
| | self.Type = "Structure" |
| | if state == None: |
| | return |
| | elif state[0] == "S": |
| | return |
| | elif state[0] == "Structure": |
| | self.ArchSkPropSetPickedUuid = state[1] |
| | self.ArchSkPropSetListPrev = state[2] |
| | elif state[0] != "Structure": |
| | self.ArchSkPropSetPickedUuid = state[0] |
| | self.ArchSkPropSetListPrev = state[1] |
| |
|
| | def onDocumentRestored(self, obj): |
| |
|
| | ArchComponent.Component.onDocumentRestored(self, obj) |
| | self.setProperties(obj) |
| |
|
| | if ( |
| | hasattr(obj, "ArchSketchData") |
| | and obj.ArchSketchData |
| | and Draft.getType(obj.Base) == "ArchSketch" |
| | ): |
| | if hasattr(obj, "ArchSketchEdges"): |
| | obj.setEditorMode("ArchSketchEdges", ["ReadOnly"]) |
| | if hasattr(obj, "ArchSketchPropertySet"): |
| | obj.setEditorMode("ArchSketchPropertySet", 0) |
| | else: |
| | if hasattr(obj, "ArchSketchEdges"): |
| | obj.setEditorMode("ArchSketchEdges", 0) |
| | if hasattr(obj, "ArchSketchPropertySet"): |
| | obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) |
| |
|
| | |
| |
|
| | def execute(self, obj): |
| | "creates the structure shape" |
| |
|
| | import Part |
| | import DraftGeomUtils |
| |
|
| | if self.clone(obj): |
| | return |
| | if obj.Base and not self.ensureBase(obj): |
| | return |
| |
|
| | base = None |
| | pl = obj.Placement |
| |
|
| | |
| | propSetPickedUuidPrev = self.ArchSkPropSetPickedUuid |
| | propSetListPrev = self.ArchSkPropSetListPrev |
| | propSetSelectedNamePrev = obj.ArchSketchPropertySet |
| | propSetSelectedNameCur = None |
| | propSetListCur = None |
| | if Draft.getType(obj.Base) == "ArchSketch": |
| | baseProxy = obj.Base.Proxy |
| | if hasattr(baseProxy, "getPropertySet"): |
| | |
| | propSetListCur = baseProxy.getPropertySet(obj.Base) |
| | |
| | propSetSelectedNameCur = baseProxy.getPropertySet( |
| | obj.Base, propSetUuid=propSetPickedUuidPrev |
| | ) |
| | if propSetSelectedNameCur: |
| | if propSetListPrev != propSetListCur: |
| | obj.ArchSketchPropertySet = propSetListCur |
| | obj.ArchSketchPropertySet = propSetSelectedNameCur |
| | self.ArchSkPropSetListPrev = propSetListCur |
| | |
| | |
| | |
| | elif propSetSelectedNamePrev != propSetSelectedNameCur: |
| | obj.ArchSketchPropertySet = propSetSelectedNameCur |
| | else: |
| | if propSetListCur: |
| | if propSetListPrev != propSetListCur: |
| | obj.ArchSketchPropertySet = propSetListCur |
| | obj.ArchSketchPropertySet = "Default" |
| | |
| | |
| |
|
| | extdata = self.getExtrusionData(obj) |
| |
|
| | if extdata: |
| | sh = extdata[0] |
| | if not isinstance(sh, list): |
| | sh = [sh] |
| | ev = extdata[1] |
| | if not isinstance(ev, list): |
| | ev = [ev] |
| | pla = extdata[2] |
| | 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): |
| | evi = ev[i] |
| | else: |
| | evi = ev[-1] |
| | if isinstance(evi, FreeCAD.Vector): |
| | evi = FreeCAD.Vector(evi) |
| | else: |
| | evi = evi.copy() |
| | if i < len(pla): |
| | pli = pla[i] |
| | else: |
| | pli = pla[-1].copy() |
| | shi.Placement = pli.multiply(shi.Placement) |
| | if isinstance(evi, FreeCAD.Vector): |
| | extv = pla[0].Rotation.multVec(evi) |
| | shi = shi.extrude(extv) |
| | else: |
| | try: |
| | shi = evi.makePipe(shi) |
| | except Part.OCCError: |
| | FreeCAD.Console.PrintError( |
| | translate( |
| | "Arch", |
| | "Error: The base shape could not 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(): |
| | return |
| | if not obj.Base.Shape.isValid(): |
| | if not obj.Base.Shape.Solids: |
| | |
| | return |
| | elif obj.Base.Shape.Solids: |
| | base = obj.Base.Shape.copy() |
| | elif obj.Base.isDerivedFrom("Mesh::Feature"): |
| | if obj.Base.Mesh.isSolid(): |
| | if obj.Base.Mesh.countComponents() == 1: |
| | sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) |
| | if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()): |
| | base = sh |
| | else: |
| | FreeCAD.Console.PrintWarning( |
| | translate("Arch", "This mesh is an invalid solid") + "\n" |
| | ) |
| | obj.Base.ViewObject.show() |
| | if (not base) and (not obj.Additions): |
| | |
| | return |
| |
|
| | base = self.processSubShapes(obj, base, pl) |
| | self.applyShape(obj, base, pl) |
| |
|
| | def getExtrusionData(self, obj): |
| | """returns (shape,extrusion vector or path,placement) or None""" |
| | if hasattr(obj, "IfcType"): |
| | IfcType = obj.IfcType |
| | else: |
| | IfcType = None |
| | import Part |
| | import DraftGeomUtils |
| |
|
| | data = ArchComponent.Component.getExtrusionData(self, obj) |
| | if data: |
| | if not isinstance(data[0], list): |
| | |
| | return data |
| | length = obj.Length.Value |
| | width = obj.Width.Value |
| | height = obj.Height.Value |
| | if not height: |
| | height = self.getParentHeight(obj) |
| | baseface = None |
| | extrusion = None |
| | normal = None |
| | if obj.Base: |
| | if hasattr(obj.Base, "Shape"): |
| | if obj.Base.Shape: |
| | if obj.Base.Shape.Solids: |
| | return None |
| | elif obj.Base.Shape.Faces: |
| | if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces, tol=0.01): |
| | return None |
| | else: |
| | baseface = obj.Base.Shape.copy() |
| | elif obj.Base.Shape.Wires: |
| | |
| | |
| | |
| | structureBaseShapeWires = [] |
| | baseShapeWires = [] |
| | faceMaker = None |
| |
|
| | if ( |
| | hasattr(obj.Base, "Proxy") |
| | and obj.ArchSketchData |
| | and hasattr(obj.Base.Proxy, "getStructureBaseShapeWires") |
| | ): |
| | propSetUuid = self.ArchSkPropSetPickedUuid |
| |
|
| | |
| | structureBaseShapeWires = obj.Base.Proxy.getStructureBaseShapeWires( |
| | obj.Base, propSetUuid=propSetUuid |
| | ) |
| | |
| | if ( |
| | structureBaseShapeWires |
| | ): |
| | |
| | baseShapeWires = structureBaseShapeWires.get("slabWires") |
| | faceMaker = structureBaseShapeWires.get("faceMaker") |
| | elif obj.Base.isDerivedFrom("Sketcher::SketchObject"): |
| | skGeom = obj.Base.GeometryFacadeList |
| | skGeomEdges = [] |
| | skPlacement = ( |
| | obj.Base.Placement |
| | ) |
| | |
| | |
| | for ig, geom in enumerate(skGeom): |
| | |
| | |
| | |
| | |
| | if (not obj.ArchSketchEdges and not geom.Construction) or str( |
| | ig |
| | ) in obj.ArchSketchEdges: |
| | |
| | |
| | if isinstance( |
| | geom.Geometry, |
| | ( |
| | Part.LineSegment, |
| | Part.Circle, |
| | Part.ArcOfCircle, |
| | Part.Ellipse, |
| | Part.BSplineCurve, |
| | ), |
| | ): |
| | skGeomEdgesI = geom.Geometry.toShape() |
| | skGeomEdges.append(skGeomEdgesI) |
| | clusterTransformed = [] |
| | for cluster in Part.getSortedClusters(skGeomEdges): |
| | edgesTransformed = [] |
| | for edge in cluster: |
| | edge.Placement = edge.Placement.multiply(skPlacement) |
| | edgesTransformed.append(edge) |
| | clusterTransformed.append(edgesTransformed) |
| | for clusterT in clusterTransformed: |
| | baseShapeWires.append(Part.Wire(clusterT)) |
| |
|
| | if not baseShapeWires: |
| | baseShapeWires = obj.Base.Shape.Wires |
| | if faceMaker or (obj.FaceMaker != "None"): |
| | if not faceMaker: |
| | faceMaker = obj.FaceMaker |
| | try: |
| | baseface = Part.makeFace( |
| | baseShapeWires, "Part::FaceMaker" + str(faceMaker) |
| | ) |
| | except Exception: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Facemaker returned an error") + "\n" |
| | ) |
| | |
| | if not baseface: |
| | for w in baseShapeWires: |
| | if not w.isClosed(): |
| | p0 = w.OrderedVertexes[0].Point |
| | p1 = w.OrderedVertexes[-1].Point |
| | if p0 != p1: |
| | e = Part.LineSegment(p0, p1).toShape() |
| | w.add(e) |
| | w.fix(0.1, 0, 1) |
| | f = Part.Face(w) |
| | |
| | if baseface: |
| | baseface = baseface.fuse(f) |
| | else: |
| | |
| | baseface = f.copy() |
| | elif length and width and height: |
| | |
| | |
| | use_profile_orientation = hasattr(obj, "Profile") and obj.Profile |
| |
|
| | if (length > height) and (IfcType in ["Beam", "Column"]): |
| | h2 = height / 2 or 0.5 |
| | w2 = width / 2 or 0.5 |
| | if use_profile_orientation: |
| | v1 = Vector(-w2, -h2, 0) |
| | v2 = Vector(w2, -h2, 0) |
| | v3 = Vector(w2, h2, 0) |
| | v4 = Vector(-w2, h2, 0) |
| | else: |
| | v1 = Vector(0, -w2, -h2) |
| | v2 = Vector(0, w2, -h2) |
| | v3 = Vector(0, w2, h2) |
| | v4 = Vector(0, -w2, h2) |
| | else: |
| | l2 = length / 2 or 0.5 |
| | w2 = width / 2 or 0.5 |
| | v1 = Vector(-l2, -w2, 0) |
| | v2 = Vector(l2, -w2, 0) |
| | v3 = Vector(l2, w2, 0) |
| | v4 = Vector(-l2, w2, 0) |
| | import Part |
| |
|
| | baseface = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) |
| | if baseface: |
| | 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: |
| | 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") and 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() |
| | else: |
| | normal = baseface.Faces[0].normalAt( |
| | 0, 0 |
| | ) |
| | base = None |
| | placement = None |
| | inverse_placement = None |
| | if len(baseface.Faces) > 1: |
| | base = [] |
| | placement = [] |
| | hint = baseface.Faces[0].normalAt(0, 0) |
| | for f in baseface.Faces: |
| | bf, pf = self.rebase(f, hint) |
| | base.append(bf) |
| | placement.append(pf) |
| | inverse_placement = placement[0].inverse() |
| | else: |
| | 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], True) |
| | if isinstance(extrusion, FreeCAD.Vector): |
| | extrusion = inverse_placement.Rotation.multVec(extrusion) |
| | elif normal: |
| | normal = inverse_placement.Rotation.multVec(normal) |
| | if not normal: |
| | normal = Vector(0, 0, 1) |
| | if not normal.Length: |
| | normal = Vector(0, 0, 1) |
| | extrusion = normal |
| | if (length > height) and (IfcType in ["Beam", "Column"]): |
| | if length: |
| | extrusion = normal.multiply(length) |
| | else: |
| | if height: |
| | extrusion = normal.multiply(height) |
| | if extrusion: |
| | return (base, extrusion, placement) |
| | return None |
| |
|
| | def onChanged(self, obj, prop): |
| |
|
| | |
| | |
| | |
| | if FreeCAD.ActiveDocument.Restoring: |
| | return |
| |
|
| | if hasattr(obj, "IfcType"): |
| | IfcType = obj.IfcType |
| | else: |
| | IfcType = None |
| | self.hideSubobjects(obj, prop) |
| | if prop in ["Shape", "ResetNodes", "NodesOffset"]: |
| | |
| | nodes = None |
| | extdata = self.getExtrusionData(obj) |
| | if extdata and not isinstance(extdata[0], list): |
| | nodes = extdata[0] |
| | if IfcType in ["Beam", "Column"]: |
| | if not isinstance(extdata[1], FreeCAD.Vector): |
| | nodes = extdata[1] |
| | elif extdata[1].Length > 0: |
| | if hasattr(nodes, "CenterOfMass"): |
| | import Part |
| |
|
| | nodes = Part.LineSegment( |
| | nodes.CenterOfMass, nodes.CenterOfMass.add(extdata[1]) |
| | ).toShape() |
| | if isinstance(extdata[1], FreeCAD.Vector): |
| | nodes.Placement = nodes.Placement.multiply(extdata[2]) |
| | offset = FreeCAD.Vector() |
| | if hasattr(obj, "NodesOffset"): |
| | offset = FreeCAD.Vector(0, 0, obj.NodesOffset.Value) |
| | if obj.Nodes and (prop != "ResetNodes"): |
| | if hasattr(self, "nodes"): |
| | if self.nodes: |
| | if obj.Nodes != self.nodes: |
| | |
| | return |
| | else: |
| | |
| | |
| | if nodes: |
| | self.nodes = [v.Point.add(offset) for v in nodes.Vertexes] |
| | return |
| | |
| | if nodes: |
| | self.nodes = [v.Point.add(offset) for v in nodes.Vertexes] |
| | obj.Nodes = self.nodes |
| | ArchComponent.Component.onChanged(self, obj, prop) |
| |
|
| | if prop == "ArchSketchPropertySet" and Draft.getType(obj.Base) == "ArchSketch": |
| | baseProxy = obj.Base.Proxy |
| | if hasattr(baseProxy, "getPropertySet"): |
| | uuid = baseProxy.getPropertySet(obj, propSetName=obj.ArchSketchPropertySet) |
| | self.ArchSkPropSetPickedUuid = uuid |
| | if ( |
| | hasattr(obj, "ArchSketchData") |
| | and obj.ArchSketchData |
| | and Draft.getType(obj.Base) == "ArchSketch" |
| | ): |
| | if hasattr(obj, "ArchSketchEdges"): |
| | obj.setEditorMode("ArchSketchEdges", ["ReadOnly"]) |
| | if hasattr(obj, "ArchSketchPropertySet"): |
| | obj.setEditorMode("ArchSketchPropertySet", 0) |
| | else: |
| | if hasattr(obj, "ArchSketchEdges"): |
| | obj.setEditorMode("ArchSketchEdges", 0) |
| | if hasattr(obj, "ArchSketchPropertySet"): |
| | obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) |
| |
|
| | def getNodeEdges(self, obj): |
| | "returns a list of edges from structural nodes" |
| |
|
| | edges = [] |
| | if obj.Nodes: |
| | import Part |
| |
|
| | for i in range(len(obj.Nodes) - 1): |
| | edges.append( |
| | Part.LineSegment( |
| | obj.Placement.multVec(obj.Nodes[i]), obj.Placement.multVec(obj.Nodes[i + 1]) |
| | ).toShape() |
| | ) |
| | if hasattr(obj.ViewObject, "NodeType"): |
| | if (obj.ViewObject.NodeType == "Area") and (len(obj.Nodes) > 2): |
| | edges.append( |
| | Part.LineSegment( |
| | obj.Placement.multVec(obj.Nodes[-1]), |
| | obj.Placement.multVec(obj.Nodes[0]), |
| | ).toShape() |
| | ) |
| | return edges |
| |
|
| |
|
| | class _ViewProviderStructure(ArchComponent.ViewProviderComponent): |
| | "A View Provider for the Structure object" |
| |
|
| | def __init__(self, vobj): |
| |
|
| | ArchComponent.ViewProviderComponent.__init__(self, vobj) |
| |
|
| | |
| | |
| | ArchComponent.ViewProviderComponent.setProperties(self, vobj) |
| |
|
| | self.setProperties(vobj) |
| | vobj.ShapeColor = ArchCommands.getDefaultColor("Structure") |
| |
|
| | def setProperties(self, vobj): |
| |
|
| | pl = vobj.PropertiesList |
| | if not "ShowNodes" in pl: |
| | vobj.addProperty( |
| | "App::PropertyBool", |
| | "ShowNodes", |
| | "Nodes", |
| | QT_TRANSLATE_NOOP("App::Property", "If the nodes are visible or not"), |
| | locked=True, |
| | ).ShowNodes = False |
| | if not "NodeLine" in pl: |
| | vobj.addProperty( |
| | "App::PropertyFloat", |
| | "NodeLine", |
| | "Nodes", |
| | QT_TRANSLATE_NOOP("App::Property", "The width of the nodes line"), |
| | locked=True, |
| | ) |
| | if not "NodeSize" in pl: |
| | vobj.addProperty( |
| | "App::PropertyFloat", |
| | "NodeSize", |
| | "Nodes", |
| | QT_TRANSLATE_NOOP("App::Property", "The size of the node points"), |
| | locked=True, |
| | ) |
| | vobj.NodeSize = 6 |
| | if not "NodeColor" in pl: |
| | vobj.addProperty( |
| | "App::PropertyColor", |
| | "NodeColor", |
| | "Nodes", |
| | QT_TRANSLATE_NOOP("App::Property", "The color of the nodes line"), |
| | locked=True, |
| | ) |
| | vobj.NodeColor = (1.0, 1.0, 1.0, 1.0) |
| | if not "NodeType" in pl: |
| | vobj.addProperty( |
| | "App::PropertyEnumeration", |
| | "NodeType", |
| | "Nodes", |
| | QT_TRANSLATE_NOOP("App::Property", "The type of structural node"), |
| | locked=True, |
| | ) |
| | vobj.NodeType = ["Linear", "Area"] |
| |
|
| | def onDocumentRestored(self, vobj): |
| |
|
| | self.setProperties(vobj) |
| |
|
| | def getIcon(self): |
| |
|
| | import Arch_rc |
| |
|
| | if hasattr(self, "Object"): |
| | if hasattr(self.Object, "CloneOf"): |
| | if self.Object.CloneOf: |
| | return ":/icons/Arch_Structure_Clone.svg" |
| | return ":/icons/Arch_Structure_Tree.svg" |
| |
|
| | def updateData(self, obj, prop): |
| |
|
| | if prop == "Nodes": |
| | if obj.Nodes: |
| | if hasattr(self, "nodes"): |
| | p = [] |
| | self.pointset.numPoints.setValue(0) |
| | self.lineset.coordIndex.deleteValues(0) |
| | self.faceset.coordIndex.deleteValues(0) |
| | for n in obj.Nodes: |
| | p.append([n.x, n.y, n.z]) |
| | self.coords.point.setValues(0, len(p), p) |
| | self.pointset.numPoints.setValue(len(p)) |
| | self.lineset.coordIndex.setValues(0, len(p) + 1, list(range(len(p))) + [-1]) |
| | if hasattr(obj.ViewObject, "NodeType"): |
| | if (obj.ViewObject.NodeType == "Area") and (len(p) > 2): |
| | self.coords.point.set1Value(len(p), p[0][0], p[0][1], p[0][2]) |
| | self.lineset.coordIndex.setValues( |
| | 0, len(p) + 2, list(range(len(p) + 1)) + [-1] |
| | ) |
| | self.faceset.coordIndex.setValues( |
| | 0, len(p) + 1, list(range(len(p))) + [-1] |
| | ) |
| |
|
| | elif prop in ["IfcType"]: |
| | if hasattr(obj.ViewObject, "NodeType"): |
| | if hasattr(obj, "IfcType"): |
| | IfcType = obj.IfcType |
| | else: |
| | IfcType = None |
| | if IfcType == "Slab": |
| | obj.ViewObject.NodeType = "Area" |
| | else: |
| | obj.ViewObject.NodeType = "Linear" |
| | else: |
| | ArchComponent.ViewProviderComponent.updateData(self, obj, prop) |
| |
|
| | def onChanged(self, vobj, prop): |
| |
|
| | if prop == "ShowNodes": |
| | if hasattr(self, "nodes"): |
| | vobj.Annotation.removeChild(self.nodes) |
| | del self.nodes |
| | if vobj.ShowNodes: |
| | from pivy import coin |
| |
|
| | self.nodes = coin.SoAnnotation() |
| | self.coords = coin.SoCoordinate3() |
| | self.mat = coin.SoMaterial() |
| | self.pointstyle = coin.SoDrawStyle() |
| | self.pointstyle.style = coin.SoDrawStyle.POINTS |
| | self.pointset = coin.SoType.fromName("SoBrepPointSet").createInstance() |
| | self.linestyle = coin.SoDrawStyle() |
| | self.linestyle.style = coin.SoDrawStyle.LINES |
| | self.lineset = coin.SoType.fromName("SoBrepEdgeSet").createInstance() |
| | self.facestyle = coin.SoDrawStyle() |
| | self.facestyle.style = coin.SoDrawStyle.FILLED |
| | self.shapehints = coin.SoShapeHints() |
| | self.shapehints.faceType = coin.SoShapeHints.UNKNOWN_FACE_TYPE |
| | self.fmat = coin.SoMaterial() |
| | self.fmat.transparency.setValue(0.75) |
| | self.faceset = coin.SoIndexedFaceSet() |
| | self.nodes.addChild(self.coords) |
| | self.nodes.addChild(self.mat) |
| | self.nodes.addChild(self.pointstyle) |
| | self.nodes.addChild(self.pointset) |
| | self.nodes.addChild(self.linestyle) |
| | self.nodes.addChild(self.lineset) |
| | self.nodes.addChild(self.facestyle) |
| | self.nodes.addChild(self.shapehints) |
| | self.nodes.addChild(self.fmat) |
| | self.nodes.addChild(self.faceset) |
| | vobj.Annotation.addChild(self.nodes) |
| | self.updateData(vobj.Object, "Nodes") |
| | self.onChanged(vobj, "NodeColor") |
| | self.onChanged(vobj, "NodeLine") |
| | self.onChanged(vobj, "NodeSize") |
| |
|
| | elif prop == "NodeColor": |
| | if hasattr(self, "mat"): |
| | l = vobj.NodeColor |
| | self.mat.diffuseColor.setValue([l[0], l[1], l[2]]) |
| | self.fmat.diffuseColor.setValue([l[0], l[1], l[2]]) |
| |
|
| | elif prop == "NodeLine": |
| | if hasattr(self, "linestyle"): |
| | self.linestyle.lineWidth = vobj.NodeLine |
| |
|
| | elif prop == "NodeSize": |
| | if hasattr(self, "pointstyle"): |
| | self.pointstyle.pointSize = vobj.NodeSize |
| |
|
| | elif prop == "NodeType": |
| | self.updateData(vobj.Object, "Nodes") |
| |
|
| | else: |
| | ArchComponent.ViewProviderComponent.onChanged(self, vobj, prop) |
| |
|
| | def setEdit(self, vobj, mode): |
| | if mode != 0: |
| | return None |
| |
|
| | taskd = StructureTaskPanel(vobj.Object) |
| | taskd.obj = self.Object |
| | taskd.update() |
| | FreeCADGui.Control.showDialog(taskd) |
| | return True |
| |
|
| |
|
| | class StructureTaskPanel(ArchComponent.ComponentTaskPanel): |
| |
|
| | def __init__(self, obj): |
| |
|
| | ArchComponent.ComponentTaskPanel.__init__(self) |
| | 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.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.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.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.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.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.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", "Selects 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 |
| |
|
| | def editNodes(self): |
| |
|
| | FreeCADGui.Control.closeDialog() |
| | FreeCADGui.runCommand("Draft_Edit") |
| |
|
| | def resetNodes(self): |
| |
|
| | self.Object.Proxy.onChanged(self.Object, "ResetNodes") |
| |
|
| | def extendNodes(self, other=None): |
| |
|
| | if not other: |
| | self.observer = StructSelectionObserver(self.extendNodes) |
| | FreeCADGui.Selection.addObserver(self.observer) |
| | FreeCAD.Console.PrintMessage(translate("Arch", "Choose another Structure object:")) |
| | else: |
| | FreeCADGui.Selection.removeObserver(self.observer) |
| | self.observer = None |
| | if Draft.getType(other) != "Structure": |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "The chosen object is not a Structure") + "\n" |
| | ) |
| | else: |
| | if not other.Nodes: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "The chosen object has no structural nodes") + "\n" |
| | ) |
| | else: |
| | if (len(self.Object.Nodes) != 2) or (len(other.Nodes) != 2): |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "One of these objects has more than 2 nodes") + "\n" |
| | ) |
| | else: |
| | import DraftGeomUtils |
| |
|
| | nodes1 = [self.Object.Placement.multVec(v) for v in self.Object.Nodes] |
| | nodes2 = [other.Placement.multVec(v) for v in other.Nodes] |
| | intersect = DraftGeomUtils.findIntersection( |
| | nodes1[0], nodes1[1], nodes2[0], nodes2[1], True, True |
| | ) |
| | if not intersect: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Unable to find a suitable intersection point") |
| | + "\n" |
| | ) |
| | else: |
| | intersect = intersect[0] |
| | FreeCAD.Console.PrintMessage(translate("Arch", "Intersection found.\n")) |
| | if DraftGeomUtils.findClosest(intersect, nodes1) == 0: |
| | self.Object.Nodes = [ |
| | self.Object.Placement.inverse().multVec(intersect), |
| | self.Object.Nodes[1], |
| | ] |
| | else: |
| | self.Object.Nodes = [ |
| | self.Object.Nodes[0], |
| | self.Object.Placement.inverse().multVec(intersect), |
| | ] |
| |
|
| | def connectNodes(self, other=None): |
| |
|
| | if not other: |
| | self.observer = StructSelectionObserver(self.connectNodes) |
| | FreeCADGui.Selection.addObserver(self.observer) |
| | FreeCAD.Console.PrintMessage(translate("Arch", "Choose another Structure object:")) |
| | else: |
| | FreeCADGui.Selection.removeObserver(self.observer) |
| | self.observer = None |
| | if Draft.getType(other) != "Structure": |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "The chosen object is not a Structure") + "\n" |
| | ) |
| | else: |
| | if not other.Nodes: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "The chosen object has no structural nodes") + "\n" |
| | ) |
| | else: |
| | if (len(self.Object.Nodes) != 2) or (len(other.Nodes) != 2): |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "One of these objects has more than 2 nodes") + "\n" |
| | ) |
| | else: |
| | import DraftGeomUtils |
| |
|
| | nodes1 = [self.Object.Placement.multVec(v) for v in self.Object.Nodes] |
| | nodes2 = [other.Placement.multVec(v) for v in other.Nodes] |
| | intersect = DraftGeomUtils.findIntersection( |
| | nodes1[0], nodes1[1], nodes2[0], nodes2[1], True, True |
| | ) |
| | if not intersect: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Unable to find a suitable intersection point") |
| | + "\n" |
| | ) |
| | else: |
| | intersect = intersect[0] |
| | FreeCAD.Console.PrintMessage( |
| | translate("Arch", "Intersection found.") + "\n" |
| | ) |
| | if DraftGeomUtils.findClosest(intersect, nodes1) == 0: |
| | self.Object.Nodes = [ |
| | self.Object.Placement.inverse().multVec(intersect), |
| | self.Object.Nodes[1], |
| | ] |
| | else: |
| | self.Object.Nodes = [ |
| | self.Object.Nodes[0], |
| | self.Object.Placement.inverse().multVec(intersect), |
| | ] |
| | if DraftGeomUtils.findClosest(intersect, nodes2) == 0: |
| | other.Nodes = [ |
| | other.Placement.inverse().multVec(intersect), |
| | other.Nodes[1], |
| | ] |
| | else: |
| | other.Nodes = [ |
| | other.Nodes[0], |
| | other.Placement.inverse().multVec(intersect), |
| | ] |
| |
|
| | def toggleNodes(self): |
| |
|
| | if self.nodevis: |
| | for obj in self.nodevis: |
| | obj[0].ViewObject.ShowNodes = obj[1] |
| | self.nodevis = None |
| | else: |
| | self.nodevis = [] |
| | for obj in FreeCAD.ActiveDocument.Objects: |
| | if hasattr(obj.ViewObject, "ShowNodes"): |
| | 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: |
| | |
| | objectList.append(selExi.Object) |
| | else: |
| | subElementsNames = [ |
| | subElementName |
| | for subElementName in selExi.SubElementNames |
| | if subElementName.startswith("Edge") |
| | ] |
| | |
| | if len(subElementsNames) > 0: |
| | objectList.append((selExi.Object, subElementsNames)) |
| | if self.Object.getTypeIdOfProperty("Tool") != "App::PropertyLinkSubList": |
| | |
| | self.Object.removeProperty("Tool") |
| | self.Object.addProperty( |
| | "App::PropertyLinkSubList", |
| | "Tool", |
| | "Structure", |
| | QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element"), |
| | locked=True, |
| | ) |
| | 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: |
| | FreeCADGui.Selection.removeObserver(self.observer) |
| | if self.nodevis: |
| | self.toggleNodes() |
| | FreeCAD.ActiveDocument.recompute() |
| | FreeCADGui.ActiveDocument.resetEdit() |
| | return True |
| |
|
| |
|
| | class StructSelectionObserver: |
| |
|
| | def __init__(self, callback): |
| | self.callback = callback |
| |
|
| | def addSelection(self, docName, objName, sub, pos): |
| | print("got ", objName) |
| | obj = FreeCAD.getDocument(docName).getObject(objName) |
| | self.callback(obj) |
| |
|
| |
|
| | class _StructuralSystem( |
| | ArchComponent.Component |
| | ): |
| | "The Structural System object" |
| |
|
| | def __init__(self, obj): |
| |
|
| | ArchComponent.Component.__init__(self, obj) |
| | self.Type = "StructuralSystem" |
| |
|
| | obj.addProperty( |
| | "App::PropertyLinkList", |
| | "Axes", |
| | "Arch", |
| | QT_TRANSLATE_NOOP("App::Property", "Axes systems this structure is built on"), |
| | locked=True, |
| | ) |
| | obj.addProperty( |
| | "App::PropertyIntegerList", |
| | "Exclude", |
| | "Arch", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The element numbers to exclude when this structure is based on axes", |
| | ), |
| | locked=True, |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "Align", |
| | "Arch", |
| | QT_TRANSLATE_NOOP("App::Property", "If true the element are aligned with axes"), |
| | locked=True, |
| | ).Align = False |
| |
|
| | def loads(self, state): |
| |
|
| | self.Type = "StructuralSystem" |
| |
|
| | def execute(self, obj): |
| | "creates the structure shape" |
| |
|
| | import Part |
| | import DraftGeomUtils |
| |
|
| | |
| | pl = obj.Placement |
| | if obj.Base: |
| | if hasattr(obj.Base, "Shape"): |
| | if obj.Base.Shape.isNull(): |
| | return |
| | if not obj.Base.Shape.Solids: |
| | return |
| |
|
| | base = None |
| |
|
| | |
| | pts = self.getAxisPoints(obj) |
| | if hasattr(obj, "Align"): |
| | if obj.Align == False: |
| | apl = self.getAxisPlacement(obj) |
| | if obj.Align: |
| | apl = None |
| | else: |
| | apl = self.getAxisPlacement(obj) |
| |
|
| | if pts: |
| | fsh = [] |
| | for i in range(len(pts)): |
| | sh = obj.Base.Shape.copy() |
| | if hasattr(obj, "Exclude"): |
| | if i in obj.Exclude: |
| | continue |
| | if apl: |
| | sh.Placement.Rotation = sh.Placement.Rotation.multiply(apl.Rotation) |
| | sh.translate(pts[i]) |
| | fsh.append(sh) |
| |
|
| | if fsh: |
| | base = Part.makeCompound(fsh) |
| | base = self.processSubShapes(obj, base, pl) |
| |
|
| | if base: |
| | if not base.isNull(): |
| | if base.isValid() and base.Solids: |
| | if base.Volume < 0: |
| | base.reverse() |
| | if base.Volume < 0: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Could not compute a shape") |
| | ) |
| | return |
| | base = base.removeSplitter() |
| | obj.Shape = base |
| | if not pl.isNull(): |
| | obj.Placement = pl |
| |
|
| | def getAxisPoints(self, obj): |
| | "returns the gridpoints of linked axes" |
| | import DraftGeomUtils |
| |
|
| | pts = [] |
| | if len(obj.Axes) == 1: |
| | if hasattr(obj, "Align"): |
| | if obj.Align: |
| | p0 = obj.Axes[0].Shape.Edges[0].Vertexes[1].Point |
| | for e in obj.Axes[0].Shape.Edges: |
| | p = e.Vertexes[1].Point |
| | p = p.sub(p0) |
| | pts.append(p) |
| | else: |
| | for e in obj.Axes[0].Shape.Edges: |
| | pts.append(e.Vertexes[0].Point) |
| | else: |
| | for e in obj.Axes[0].Shape.Edges: |
| | pts.append(e.Vertexes[0].Point) |
| | elif len(obj.Axes) >= 2: |
| | set1 = obj.Axes[0].Shape.Edges |
| | set2 = obj.Axes[1].Shape.Edges |
| | for e1 in set1: |
| | for e2 in set2: |
| | pts.extend(DraftGeomUtils.findIntersection(e1, e2)) |
| | return pts |
| |
|
| | def getAxisPlacement(self, obj): |
| | "returns an axis placement" |
| | if obj.Axes: |
| | return obj.Axes[0].Placement |
| | return None |
| |
|
| |
|
| | class _ViewProviderStructuralSystem(ArchComponent.ViewProviderComponent): |
| | "A View Provider for the Structural System object" |
| |
|
| | def getIcon(self): |
| |
|
| | import Arch_rc |
| |
|
| | return ":/icons/Arch_StructuralSystem_Tree.svg" |
| |
|
| |
|
| | if FreeCAD.GuiUp: |
| | FreeCADGui.addCommand("Arch_Structure", _CommandStructure()) |
| | FreeCADGui.addCommand("Arch_StructuralSystem", CommandStructuralSystem()) |
| | FreeCADGui.addCommand("Arch_StructuresFromSelection", CommandStructuresFromSelection()) |
| |
|
| | class _ArchStructureGroupCommand: |
| |
|
| | def GetCommands(self): |
| | return ("Arch_Structure", "Arch_StructuralSystem", "Arch_StructuresFromSelection") |
| |
|
| | def GetResources(self): |
| | return { |
| | "MenuText": QT_TRANSLATE_NOOP("Arch_StructureTools", "Structure Tools"), |
| | "ToolTip": QT_TRANSLATE_NOOP("Arch_StructureTools", "Structure tools"), |
| | } |
| |
|
| | def IsActive(self): |
| | return not FreeCAD.ActiveDocument is None |
| |
|
| | FreeCADGui.addCommand("Arch_StructureTools", _ArchStructureGroupCommand()) |
| |
|