From 6345b824e5fd9b10127c266acaf0e322681908b4 Mon Sep 17 00:00:00 2001 From: Furgo <148809153+furgo16@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:07:48 +0200 Subject: [PATCH] Add BIM workbench to .pre-commit-config.yaml (#21591) * Add BIM workbench to .pre-commit-config.yaml * pre-commit: ignore translations * pre-commit: add additional ignore pattern * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 5 +- src/Mod/BIM/Arch.py | 135 +- src/Mod/BIM/ArchAxis.py | 560 +++-- src/Mod/BIM/ArchAxisSystem.py | 106 +- src/Mod/BIM/ArchBuilding.py | 122 +- src/Mod/BIM/ArchBuildingPart.py | 853 +++++--- src/Mod/BIM/ArchCommands.py | 848 +++++--- src/Mod/BIM/ArchComponent.py | 990 +++++---- src/Mod/BIM/ArchCurtainWall.py | 532 +++-- src/Mod/BIM/ArchCutPlane.py | 20 +- src/Mod/BIM/ArchEquipment.py | 162 +- src/Mod/BIM/ArchFence.py | 128 +- src/Mod/BIM/ArchFloor.py | 171 +- src/Mod/BIM/ArchFrame.py | 174 +- src/Mod/BIM/ArchGrid.py | 323 ++- src/Mod/BIM/ArchIFC.py | 92 +- src/Mod/BIM/ArchIFCSchema.py | 21 +- src/Mod/BIM/ArchIFCView.py | 39 +- src/Mod/BIM/ArchMaterial.py | 534 +++-- src/Mod/BIM/ArchNesting.py | 287 +-- src/Mod/BIM/ArchPanel.py | 920 +++++--- src/Mod/BIM/ArchPipe.py | 403 ++-- src/Mod/BIM/ArchPrecast.py | 1208 +++++++---- src/Mod/BIM/ArchProfile.py | 658 ++++-- src/Mod/BIM/ArchProject.py | 33 +- src/Mod/BIM/ArchRebar.py | 399 ++-- src/Mod/BIM/ArchReference.py | 371 ++-- src/Mod/BIM/ArchRoof.py | 532 +++-- src/Mod/BIM/ArchSchedule.py | 467 ++-- src/Mod/BIM/ArchSectionPlane.py | 1130 ++++++---- src/Mod/BIM/ArchSite.py | 928 +++++--- src/Mod/BIM/ArchSketchObject.py | 17 +- src/Mod/BIM/ArchSpace.py | 750 ++++--- src/Mod/BIM/ArchStairs.py | 1846 +++++++++++----- src/Mod/BIM/ArchStructure.py | 1307 +++++++---- src/Mod/BIM/ArchTruss.py | 287 ++- src/Mod/BIM/ArchVRM.py | 389 ++-- src/Mod/BIM/ArchWall.py | 963 ++++++--- src/Mod/BIM/ArchWindow.py | 986 +++++---- src/Mod/BIM/ArchWindowPresets.py | 795 +++---- src/Mod/BIM/BimSelect.py | 5 +- src/Mod/BIM/BimStatus.py | 43 +- src/Mod/BIM/Dice3DS/__init__.py | 4 +- src/Mod/BIM/Dice3DS/dom3ds.py | 1460 ++++++++----- src/Mod/BIM/Dice3DS/util.py | 167 +- src/Mod/BIM/Init.py | 24 +- src/Mod/BIM/InitGui.py | 92 +- src/Mod/BIM/OfflineRenderingUtils.py | 424 ++-- src/Mod/BIM/Presets/ifc_contexts_IFC4.json | 2 +- src/Mod/BIM/Resources/create_qrc.py | 1 + .../templates/webgl_export_template.html | 2 +- src/Mod/BIM/arch.dox | 1 - src/Mod/BIM/bimcommands/BimArchUtils.py | 312 ++- src/Mod/BIM/bimcommands/BimAxis.py | 73 +- src/Mod/BIM/bimcommands/BimBeam.py | 4 +- src/Mod/BIM/bimcommands/BimBox.py | 10 +- src/Mod/BIM/bimcommands/BimBuilder.py | 4 +- src/Mod/BIM/bimcommands/BimBuildingPart.py | 32 +- src/Mod/BIM/bimcommands/BimClassification.py | 103 +- src/Mod/BIM/bimcommands/BimColumn.py | 4 +- src/Mod/BIM/bimcommands/BimCommon.py | 4 +- src/Mod/BIM/bimcommands/BimCompound.py | 4 +- src/Mod/BIM/bimcommands/BimConvert.py | 5 +- src/Mod/BIM/bimcommands/BimCopy.py | 7 +- src/Mod/BIM/bimcommands/BimCurtainwall.py | 48 +- src/Mod/BIM/bimcommands/BimCut.py | 4 +- src/Mod/BIM/bimcommands/BimCutPlane.py | 47 +- src/Mod/BIM/bimcommands/BimDiff.py | 84 +- src/Mod/BIM/bimcommands/BimDimensions.py | 16 +- src/Mod/BIM/bimcommands/BimDoor.py | 4 +- src/Mod/BIM/bimcommands/BimDrawingView.py | 32 +- src/Mod/BIM/bimcommands/BimEquipment.py | 36 +- src/Mod/BIM/bimcommands/BimExamples.py | 1 + src/Mod/BIM/bimcommands/BimFence.py | 23 +- src/Mod/BIM/bimcommands/BimFrame.py | 26 +- src/Mod/BIM/bimcommands/BimGlue.py | 1 + src/Mod/BIM/bimcommands/BimHelp.py | 1 + src/Mod/BIM/bimcommands/BimIfcElements.py | 53 +- src/Mod/BIM/bimcommands/BimIfcExplorer.py | 34 +- src/Mod/BIM/bimcommands/BimIfcProperties.py | 110 +- src/Mod/BIM/bimcommands/BimIfcQuantities.py | 68 +- src/Mod/BIM/bimcommands/BimImagePlane.py | 8 +- src/Mod/BIM/bimcommands/BimLayers.py | 62 +- src/Mod/BIM/bimcommands/BimLeader.py | 7 +- src/Mod/BIM/bimcommands/BimLibrary.py | 69 +- src/Mod/BIM/bimcommands/BimMaterial.py | 139 +- src/Mod/BIM/bimcommands/BimMoveView.py | 4 +- src/Mod/BIM/bimcommands/BimNudge.py | 14 +- src/Mod/BIM/bimcommands/BimOffset.py | 9 +- src/Mod/BIM/bimcommands/BimPanel.py | 262 ++- src/Mod/BIM/bimcommands/BimPipe.py | 68 +- src/Mod/BIM/bimcommands/BimPreflight.py | 134 +- src/Mod/BIM/bimcommands/BimProfile.py | 68 +- src/Mod/BIM/bimcommands/BimProject.py | 4 +- src/Mod/BIM/bimcommands/BimProjectManager.py | 174 +- src/Mod/BIM/bimcommands/BimRebar.py | 58 +- src/Mod/BIM/bimcommands/BimReextrude.py | 4 +- src/Mod/BIM/bimcommands/BimReference.py | 15 +- src/Mod/BIM/bimcommands/BimReorder.py | 11 +- src/Mod/BIM/bimcommands/BimRewire.py | 14 +- src/Mod/BIM/bimcommands/BimRoof.py | 29 +- src/Mod/BIM/bimcommands/BimSchedule.py | 16 +- src/Mod/BIM/bimcommands/BimSectionPlane.py | 29 +- src/Mod/BIM/bimcommands/BimSetup.py | 242 +-- src/Mod/BIM/bimcommands/BimShape2DView.py | 27 +- src/Mod/BIM/bimcommands/BimSimpleCopy.py | 4 +- src/Mod/BIM/bimcommands/BimSite.py | 15 +- src/Mod/BIM/bimcommands/BimSketch.py | 1 + src/Mod/BIM/bimcommands/BimSlab.py | 18 +- src/Mod/BIM/bimcommands/BimSpace.py | 20 +- src/Mod/BIM/bimcommands/BimStairs.py | 40 +- src/Mod/BIM/bimcommands/BimTDPage.py | 12 +- src/Mod/BIM/bimcommands/BimTDView.py | 11 +- src/Mod/BIM/bimcommands/BimText.py | 4 +- src/Mod/BIM/bimcommands/BimTogglePanels.py | 13 +- src/Mod/BIM/bimcommands/BimTrash.py | 5 +- src/Mod/BIM/bimcommands/BimTruss.py | 35 +- src/Mod/BIM/bimcommands/BimTutorial.py | 18 +- src/Mod/BIM/bimcommands/BimUnclone.py | 4 +- src/Mod/BIM/bimcommands/BimUngroup.py | 6 +- src/Mod/BIM/bimcommands/BimViews.py | 108 +- src/Mod/BIM/bimcommands/BimWPCommands.py | 12 +- src/Mod/BIM/bimcommands/BimWall.py | 221 +- src/Mod/BIM/bimcommands/BimWelcome.py | 12 +- src/Mod/BIM/bimcommands/BimWindow.py | 301 ++- src/Mod/BIM/bimcommands/BimWindows.py | 12 +- src/Mod/BIM/bimcommands/__init__.py | 4 +- src/Mod/BIM/bimtests/TestArchAxis.py | 3 +- src/Mod/BIM/bimtests/TestArchBase.py | 8 +- src/Mod/BIM/bimtests/TestArchBaseGui.py | 5 +- src/Mod/BIM/bimtests/TestArchBuildingPart.py | 13 +- .../BIM/bimtests/TestArchBuildingPartGui.py | 50 +- src/Mod/BIM/bimtests/TestArchComponent.py | 81 +- src/Mod/BIM/bimtests/TestArchCurtainWall.py | 7 +- src/Mod/BIM/bimtests/TestArchEquipment.py | 3 +- src/Mod/BIM/bimtests/TestArchFence.py | 1 + src/Mod/BIM/bimtests/TestArchFrame.py | 9 +- src/Mod/BIM/bimtests/TestArchGrid.py | 3 +- src/Mod/BIM/bimtests/TestArchImportersGui.py | 5 +- src/Mod/BIM/bimtests/TestArchMaterial.py | 15 +- src/Mod/BIM/bimtests/TestArchPanel.py | 3 +- src/Mod/BIM/bimtests/TestArchPipe.py | 7 +- src/Mod/BIM/bimtests/TestArchProfile.py | 5 +- src/Mod/BIM/bimtests/TestArchProject.py | 3 +- src/Mod/BIM/bimtests/TestArchRebar.py | 87 +- src/Mod/BIM/bimtests/TestArchReference.py | 3 +- src/Mod/BIM/bimtests/TestArchRoof.py | 103 +- src/Mod/BIM/bimtests/TestArchSchedule.py | 3 +- src/Mod/BIM/bimtests/TestArchSectionPlane.py | 33 +- src/Mod/BIM/bimtests/TestArchSiteGui.py | 47 +- src/Mod/BIM/bimtests/TestArchSpace.py | 77 +- src/Mod/BIM/bimtests/TestArchStairs.py | 6 +- src/Mod/BIM/bimtests/TestArchStructure.py | 7 +- src/Mod/BIM/bimtests/TestArchTruss.py | 3 +- src/Mod/BIM/bimtests/TestArchWall.py | 48 +- src/Mod/BIM/bimtests/TestArchWindow.py | 417 ++-- src/Mod/BIM/bimtests/TestWebGLExport.py | 20 +- src/Mod/BIM/bimtests/TestWebGLExportGui.py | 36 +- src/Mod/BIM/ifc_objects.py | 5 + src/Mod/BIM/ifc_viewproviders.py | 15 +- src/Mod/BIM/importers/exportIFC.py | 1904 +++++++++-------- src/Mod/BIM/importers/exportIFCHelper.py | 338 ++- .../BIM/importers/exportIFCStructuralTools.py | 189 +- src/Mod/BIM/importers/import3DS.py | 16 +- src/Mod/BIM/importers/importDAE.py | 203 +- src/Mod/BIM/importers/importGBXML.py | 203 +- src/Mod/BIM/importers/importIFC.py | 715 ++++--- src/Mod/BIM/importers/importIFCHelper.py | 377 ++-- src/Mod/BIM/importers/importIFClegacy.py | 1685 +++++++++------ src/Mod/BIM/importers/importIFCmulticore.py | 159 +- src/Mod/BIM/importers/importJSON.py | 39 +- src/Mod/BIM/importers/importOBJ.py | 223 +- src/Mod/BIM/importers/importSH3D.py | 8 +- src/Mod/BIM/importers/importSH3DHelper.py | 1508 ++++++++----- src/Mod/BIM/importers/importSHP.py | 87 +- src/Mod/BIM/importers/importWebGL.py | 39 +- src/Mod/BIM/nativeifc/ifc_classification.py | 35 +- src/Mod/BIM/nativeifc/ifc_commands.py | 18 +- src/Mod/BIM/nativeifc/ifc_diff.py | 12 +- src/Mod/BIM/nativeifc/ifc_export.py | 138 +- src/Mod/BIM/nativeifc/ifc_generator.py | 22 +- src/Mod/BIM/nativeifc/ifc_geometry.py | 16 +- src/Mod/BIM/nativeifc/ifc_import.py | 5 +- src/Mod/BIM/nativeifc/ifc_materials.py | 8 +- src/Mod/BIM/nativeifc/ifc_objects.py | 49 +- src/Mod/BIM/nativeifc/ifc_observer.py | 15 +- src/Mod/BIM/nativeifc/ifc_openshell.py | 32 +- src/Mod/BIM/nativeifc/ifc_performance_test.py | 15 +- src/Mod/BIM/nativeifc/ifc_psets.py | 35 +- src/Mod/BIM/nativeifc/ifc_selftest.py | 24 +- src/Mod/BIM/nativeifc/ifc_status.py | 105 +- src/Mod/BIM/nativeifc/ifc_tools.py | 174 +- src/Mod/BIM/nativeifc/ifc_tree.py | 8 +- src/Mod/BIM/nativeifc/ifc_types.py | 23 +- src/Mod/BIM/nativeifc/ifc_viewproviders.py | 38 +- src/Mod/BIM/utils/buildPsets.py | 12 +- src/Mod/BIM/utils/ifctree.py | 1 - 197 files changed, 21982 insertions(+), 13836 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 101f719e16..b2e2b4e758 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,7 @@ files: | src/Tools| tests/src| src/Mod/Assembly| + src/Mod/BIM| src/Mod/CAM| src/Mod/Cloud| src/Mod/Fem| @@ -50,7 +51,9 @@ exclude: | src/Mod/Mesh/App/TestData| src/Mod/Mesh/App/WildMagic4| src/Mod/Robot/App/kdl_cp| - src/Mod/Robot/Lib + src/Mod/Robot/Lib| + .*\.ts$| + .*\.brep$ ) repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/src/Mod/BIM/Arch.py b/src/Mod/BIM/Arch.py index c8b50ac2c2..008daf13f5 100644 --- a/src/Mod/BIM/Arch.py +++ b/src/Mod/BIM/Arch.py @@ -47,15 +47,16 @@ wall = Arch.makeWall(length=5000, width=200, height=3000) # mm units wall.recompute() ``` """ -__title__ = "FreeCAD Arch API" +__title__ = "FreeCAD Arch API" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" import FreeCAD from typing import Optional if FreeCAD.GuiUp: import FreeCADGui + FreeCADGui.updateLocale() QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP @@ -74,6 +75,7 @@ from ArchStructure import * # make functions + def makeAxis(num=1, size=1000, name=None): """ Creates an axis set in the active document. @@ -93,6 +95,7 @@ def makeAxis(num=1, size=1000, name=None): The created axis object. """ import ArchAxis + if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return @@ -133,6 +136,7 @@ def makeAxisSystem(axes, name=None): The created axis system object. """ import ArchAxisSystem + if not isinstance(axes, list): axes = [axes] obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "AxisSystem") @@ -164,8 +168,9 @@ def makeBuildingPart(objectslist=None, baseobj=None, name=None): The created building part object. """ import ArchBuildingPart + obj = FreeCAD.ActiveDocument.addObject("App::GeometryPython", "BuildingPart") - #obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","BuildingPart") + # obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","BuildingPart") obj.Label = name if name else translate("Arch", "BuildingPart") ArchBuildingPart.BuildingPart(obj) obj.IfcType = "Building Element Part" @@ -223,6 +228,7 @@ def makeBuilding(objectslist=None, baseobj=None, name=None): The created building object. """ import ArchBuildingPart + obj = makeBuildingPart(objectslist) obj.Label = name if name else translate("Arch", "Building") obj.IfcType = "Building" @@ -285,6 +291,7 @@ def convertFloors(floor=None): """ import Draft import ArchBuildingPart + todel = [] if floor: objset = [floor] @@ -309,9 +316,9 @@ def convertFloors(floor=None): if hasattr(parent, "Group"): if obj in parent.Group: parent.addObject(nobj) - #g = parent.Group - #g.append(nobj) - #parent.Group = g + # g = parent.Group + # g.append(nobj) + # parent.Group = g todel.append(obj.Name) if obj.ViewObject: # some bug makes this trigger even efter the object has been deleted... @@ -321,6 +328,7 @@ def convertFloors(floor=None): nobj.Label = label for n in todel: from draftutils import todo + todo.ToDo.delay(FreeCAD.ActiveDocument.removeObject, n) @@ -425,6 +433,7 @@ def makeFence(section, post, path): fence.Path = path if FreeCAD.GuiUp: import ArchFence + ArchFence.hide(section) ArchFence.hide(post) ArchFence.hide(path) @@ -804,7 +813,7 @@ def makePipeConnector(pipes, radius=0, name=None): return pipeConnector -def makeProfile(profile=[0, 'REC', 'REC100x100', 'R', 100, 100]): +def makeProfile(profile=[0, "REC", "REC100x100", "R", 100, 100]): """ Creates a profile object based on the given profile data. @@ -836,6 +845,7 @@ def makeProfile(profile=[0, 'REC', 'REC100x100', 'R', 100, 100]): The created profile object. """ import ArchProfile + if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return @@ -910,13 +920,14 @@ def makeProject(sites=None, name=None): return project + def makeRebar( baseobj: Optional[FreeCAD.DocumentObject] = None, sketch: Optional[FreeCAD.DocumentObject] = None, diameter: Optional[float] = None, amount: int = 1, offset: Optional[float] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> Optional[FreeCAD.DocumentObject]: """ Creates a reinforcement bar (rebar) object. @@ -1071,19 +1082,22 @@ def makeReference(filepath=None, partname=None, name=None): reference.Part = partname import Draft + Draft.select(reference) return reference -def makeRoof(baseobj=None, - facenr=0, - angles=[45.0], - run=[250.0], - idrel=[-1], - thickness=[50.0], - overhang=[100.0], - name=None): +def makeRoof( + baseobj=None, + facenr=0, + angles=[45.0], + run=[250.0], + idrel=[-1], + thickness=[50.0], + overhang=[100.0], + name=None, +): """ Creates a roof object based on a closed wire or an object. @@ -1139,7 +1153,7 @@ def makeRoof(baseobj=None, if FreeCAD.GuiUp: roof.Base.ViewObject.hide() else: - if (roof.Base.Shape.Faces and roof.Face): + if roof.Base.Shape.Faces and roof.Face: baseWire = roof.Base.Shape.Faces[roof.Face - 1].Wires[0] if FreeCAD.GuiUp: roof.Base.ViewObject.hide() @@ -1153,11 +1167,11 @@ def makeRoof(baseobj=None, roof.Base.ViewObject.hide() edges = Part.__sortEdges__(baseWire.Edges) ln = len(edges) - roof.Angles = ArchRoof.adjust_list_len(angles, ln, angles[0]) - roof.Runs = ArchRoof.adjust_list_len(run, ln, run[0]) - roof.IdRel = ArchRoof.adjust_list_len(idrel, ln, idrel[0]) + roof.Angles = ArchRoof.adjust_list_len(angles, ln, angles[0]) + roof.Runs = ArchRoof.adjust_list_len(run, ln, run[0]) + roof.IdRel = ArchRoof.adjust_list_len(idrel, ln, idrel[0]) roof.Thickness = ArchRoof.adjust_list_len(thickness, ln, thickness[0]) - roof.Overhang = ArchRoof.adjust_list_len(overhang, ln, overhang[0]) + roof.Overhang = ArchRoof.adjust_list_len(overhang, ln, overhang[0]) roof.Face = facenr @@ -1260,6 +1274,7 @@ def makeSite(objectslist=None, baseobj=None, name=None): site.Group = objectslist if baseobj: import Part + if isinstance(baseobj, Part.Shape): site.Shape = baseobj else: @@ -1329,12 +1344,13 @@ def makeSpace(objects=None, baseobj=None, name=None): # object will determine the type of the set. # Input to this function can come into three different formats. First convert it # to a common format: [ (, ["Face1", ...]), ... ] - if (hasattr(objects[0], "isDerivedFrom") and - objects[0].isDerivedFrom("Gui::SelectionObject")): + if hasattr(objects[0], "isDerivedFrom") and objects[0].isDerivedFrom( + "Gui::SelectionObject" + ): # Selection set: convert to common format # [, ...] objects = [(obj.Object, obj.SubElementNames) for obj in objects] - elif (isinstance(objects[0], tuple) or isinstance(objects[0], list)): + elif isinstance(objects[0], tuple) or isinstance(objects[0], list): # Tuple or list of object with subobjects: pass unmodified # [ (, ["Face1", ...]), ... ] pass @@ -1359,6 +1375,7 @@ def makeSpace(objects=None, baseobj=None, name=None): space.Proxy.addSubobjects(space, boundaries) return space + def addSpaceBoundaries(space, subobjects): """Adds the given subobjects as defining boundaries of the given space. @@ -1384,9 +1401,11 @@ def addSpaceBoundaries(space, subobjects): subobjects = [(obj, ("Face1", "Face2", "Face3", "Face4"))] """ import Draft + if Draft.getType(space) == "Space": space.Proxy.addSubobjects(space, subobjects) + def removeSpaceBoundaries(space, subobjects): """Remove the given subobjects as defining boundaries of the given space. @@ -1412,9 +1431,11 @@ def removeSpaceBoundaries(space, subobjects): subobjects = [(obj, ("Face1", "Face2", "Face3", "Face4"))] """ import Draft + if Draft.getType(space) == "Space": space.Proxy.removeSubobjects(space, subobjects) + def makeStairs(baseobj=None, length=None, width=None, height=None, steps=None, name=None): """ Creates a stairs object with the given attributes. @@ -1440,6 +1461,7 @@ def makeStairs(baseobj=None, length=None, width=None, height=None, steps=None, n The created stairs object. """ import ArchStairs + if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return @@ -1449,8 +1471,7 @@ def makeStairs(baseobj=None, length=None, width=None, height=None, steps=None, n label = name if name else translate("Arch", "Stairs") def setProperty(obj, length, width, height, steps): - """setProperty(obj,length,width,height,steps): sets up the basic properties for this stair - """ + """setProperty(obj,length,width,height,steps): sets up the basic properties for this stair""" obj.Length = length if length else params.get_param_arch("StairsLength") obj.Width = width if width else params.get_param_arch("StairsWidth") obj.Height = height if height else params.get_param_arch("StairsHeight") @@ -1502,16 +1523,16 @@ def makeStairs(baseobj=None, length=None, width=None, height=None, steps=None, n additions.append(stairs[i]) if i > 1: stairs[i].LastSegment = stairs[i - 1] - #else: - # Below made '1st segment' of a complex stairs went to Base - # Remarked below out, 2025.8.31. - # Seems no other Arch object create an Arch object as its Base - # and use a 'master' Arch(Stairs) object like Stairs. Base is - # not moved together with host upon onChanged(), unlike - # behaviour in objects of Additions. - # - #if len(stairs) > 1: # i.e. length >1, have a 'master' staircase created - # stairs[0].Base = stairs[1] + # else: + # Below made '1st segment' of a complex stairs went to Base + # Remarked below out, 2025.8.31. + # Seems no other Arch object create an Arch object as its Base + # and use a 'master' Arch(Stairs) object like Stairs. Base is + # not moved together with host upon onChanged(), unlike + # behaviour in objects of Additions. + # + # if len(stairs) > 1: # i.e. length >1, have a 'master' staircase created + # stairs[0].Base = stairs[1] i += 1 if lenSelection > 1: @@ -1554,6 +1575,7 @@ def makeRailing(stairs): ------- None """ + def makeRailingLorR(stairs, side="L"): """makeRailingLorR(stairs,side="L"): Creates a railing on the given side of the stairs, L or R""" @@ -1568,8 +1590,11 @@ def makeRailing(stairs): stairRailingLR = "RailingRight" if outlineLR or outlineLRAll: lrRail = makePipe( - baseobj=None, diameter=0, length=0, placement=None, - name=translate("Arch", "Railing") + baseobj=None, + diameter=0, + length=0, + placement=None, + name=translate("Arch", "Railing"), ) # All semgments in a complex stairs moved together by default # regardless Move With Host setting in system setting @@ -1638,8 +1663,14 @@ def makeTruss(baseobj=None, name=None): def makeWall( - baseobj=None, height=None, length=None, width=None, align=None, offset=None, - face=None, name=None + baseobj=None, + height=None, + length=None, + width=None, + align=None, + offset=None, + face=None, + name=None, ): """Create a wall based on a given object, and returns the generated wall. @@ -1692,7 +1723,7 @@ def makeWall( # Initialize all relevant properties if baseobj: - if hasattr(baseobj, 'Shape') or baseobj.isDerivedFrom("Mesh::Feature"): + if hasattr(baseobj, "Shape") or baseobj.isDerivedFrom("Mesh::Feature"): wall.Base = baseobj else: FreeCAD.Console.PrintWarning( @@ -1743,6 +1774,7 @@ def joinWalls(walls, delete=False, deletebase=False): import Part import Draft import ArchWall + if not walls: return None if not isinstance(walls, list): @@ -1760,6 +1792,7 @@ def joinWalls(walls, delete=False, deletebase=False): else: try: import ArchSketchObject + newSk = ArchSketchObject.makeArchSketch() except: if Draft.getType(base.Base) != "Sketcher::SketchObject": @@ -2069,16 +2102,17 @@ def makeWindow( # previously hardcoded. With the normal set to 'auto', window object would not suffer weird # shape if the Base Sketch is rotated by some reason. Keep the property be 'auto' (0,0,0) # here. - #obj.Normal = baseobj.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) + # obj.Normal = baseobj.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) window.Base = baseobj if parts is not None: window.WindowParts = parts else: if baseobj: linked_obj = baseobj.getLinkedObject(True) - if (linked_obj.isDerivedFrom("Part::Part2DObject") - or Draft.getType(linked_obj) in ["BezCurve", "BSpline", "Wire"]) \ - and DraftGeomUtils.isPlanar(baseobj.Shape): + if ( + linked_obj.isDerivedFrom("Part::Part2DObject") + or Draft.getType(linked_obj) in ["BezCurve", "BSpline", "Wire"] + ) and DraftGeomUtils.isPlanar(baseobj.Shape): # "BezCurve", "BSpline" and "Wire" objects created with < v1.1 are # "Part::Part2DObject" objects. In all versions these objects need not be planar. if baseobj.Shape.Wires: @@ -2091,10 +2125,14 @@ def makeWindow( wires.append(f"Wire{i}") wires_str = ",".join(wires) part_name = "Default" - part_frame_thickness = "1" # mm - part_offset = "0" # mm + part_frame_thickness = "1" # mm + part_offset = "0" # mm window.WindowParts = [ - part_name, part_type, wires_str, part_frame_thickness, part_offset + part_name, + part_type, + wires_str, + part_frame_thickness, + part_offset, ] else: # Bind properties from base obj if they exist @@ -2105,6 +2143,7 @@ def makeWindow( if window.Base and FreeCAD.GuiUp: from ArchWindow import recolorize + window.Base.ViewObject.DisplayMode = "Wireframe" window.Base.ViewObject.hide() todo.ToDo.delay(recolorize, [window.Document.Name, window.Name]) diff --git a/src/Mod/BIM/ArchAxis.py b/src/Mod/BIM/ArchAxis.py index 3f1d2a6384..8a92a3b2c3 100644 --- a/src/Mod/BIM/ArchAxis.py +++ b/src/Mod/BIM/ArchAxis.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Axis System" +__title__ = "FreeCAD Axis System" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchAxis # \ingroup ARCH @@ -52,50 +52,90 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _Axis: - "The Axis object" - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "Axis" self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Distances" in pl: - obj.addProperty("App::PropertyFloatList","Distances","Axis", QT_TRANSLATE_NOOP("App::Property","The intervals between axes"), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Distances", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The intervals between axes"), + locked=True, + ) if not "Angles" in pl: - obj.addProperty("App::PropertyFloatList","Angles","Axis", QT_TRANSLATE_NOOP("App::Property","The angles of each axis"), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Angles", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The angles of each axis"), + locked=True, + ) if not "Labels" in pl: - obj.addProperty("App::PropertyStringList","Labels","Axis", QT_TRANSLATE_NOOP("App::Property","The label of each axis"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Labels", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The label of each axis"), + locked=True, + ) if not "CustomNumber" in pl: - obj.addProperty("App::PropertyString","CustomNumber","Axis", QT_TRANSLATE_NOOP("App::Property","An optional custom bubble number"), locked=True) + obj.addProperty( + "App::PropertyString", + "CustomNumber", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "An optional custom bubble number"), + locked=True, + ) if not "Length" in pl: - obj.addProperty("App::PropertyLength","Length","Axis", QT_TRANSLATE_NOOP("App::Property","The length of the axes"), locked=True) - obj.Length=3000 + obj.addProperty( + "App::PropertyLength", + "Length", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The length of the axes"), + locked=True, + ) + obj.Length = 3000 if not "Placement" in pl: - obj.addProperty("App::PropertyPlacement","Placement","Base","", locked=True) + obj.addProperty("App::PropertyPlacement", "Placement", "Base", "", locked=True) if not "Shape" in pl: - obj.addProperty("Part::PropertyPartShape","Shape","Base","", locked=True) + obj.addProperty("Part::PropertyPartShape", "Shape", "Base", "", locked=True) if not "Limit" in pl: - obj.addProperty("App::PropertyLength","Limit","Axis", QT_TRANSLATE_NOOP("App::Property","If not zero, the axes are not represented as one full line but as two lines of the given length"), locked=True) - obj.Limit=0 + obj.addProperty( + "App::PropertyLength", + "Limit", + "Axis", + QT_TRANSLATE_NOOP( + "App::Property", + "If not zero, the axes are not represented as one full line but as two lines of the given length", + ), + locked=True, + ) + obj.Limit = 0 - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): pl = obj.Placement geoms = [] @@ -116,7 +156,7 @@ class _Axis: unitvec = Vector(math.sin(ang), math.cos(ang), 0) p1 = Vector(dist, 0, 0) p2 = p1 + unitvec * ln - if hasattr(obj,"Limit") and obj.Limit.Value: + if hasattr(obj, "Limit") and obj.Limit.Value: p3 = unitvec * obj.Limit.Value p4 = unitvec * -obj.Limit.Value geoms.append(Part.LineSegment(p1, p1 + p3).toShape()) @@ -128,21 +168,20 @@ class _Axis: obj.Shape = sh obj.Placement = pl - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): - if prop in ["Angles","Distances","Placement"]: + if prop in ["Angles", "Distances", "Placement"]: obj.touch() def dumps(self): return None - def loads(self,state): + def loads(self, state): self.Type = "Axis" - def getPoints(self,obj): - + def getPoints(self, obj): "returns the gridpoints of linked axes" pts = [] @@ -150,7 +189,7 @@ class _Axis: pts.append(e.Vertexes[0].Point) return pts - def getAxisData(self,obj): + def getAxisData(self, obj): data = [] num = 0 for e in obj.Shape.Edges: @@ -158,7 +197,7 @@ class _Axis: axdata.append(e.Vertexes[0].Point) axdata.append(e.Vertexes[-1].Point) if obj.ViewObject: - axdata.append(obj.ViewObject.Proxy.getNumber(obj.ViewObject,num)) + axdata.append(obj.ViewObject.Proxy.getNumber(obj.ViewObject, num)) else: axdata.append(str(num)) data.append(axdata) @@ -167,59 +206,143 @@ class _Axis: class _ViewProviderAxis: - "A View Provider for the Axis object" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self self.setProperties(vobj) - def setProperties(self,vobj): + def setProperties(self, vobj): ts = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") pl = vobj.PropertiesList if not "BubbleSize" in pl: - vobj.addProperty("App::PropertyLength","BubbleSize","Axis", QT_TRANSLATE_NOOP("App::Property","The size of the axis bubbles"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "BubbleSize", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The size of the axis bubbles"), + locked=True, + ) vobj.BubbleSize = ts * 1.42 if not "NumberingStyle" in pl: - vobj.addProperty("App::PropertyEnumeration","NumberingStyle","Axis", QT_TRANSLATE_NOOP("App::Property","The numbering style"), locked=True) - vobj.NumberingStyle = ["1,2,3","01,02,03","001,002,003","A,B,C","a,b,c","I,II,III","L0,L1,L2"] + vobj.addProperty( + "App::PropertyEnumeration", + "NumberingStyle", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The numbering style"), + locked=True, + ) + vobj.NumberingStyle = [ + "1,2,3", + "01,02,03", + "001,002,003", + "A,B,C", + "a,b,c", + "I,II,III", + "L0,L1,L2", + ] vobj.NumberingStyle = "1,2,3" if not "DrawStyle" in pl: - vobj.addProperty("App::PropertyEnumeration","DrawStyle","Axis",QT_TRANSLATE_NOOP("App::Property","The type of line to draw this axis"), locked=True) - vobj.DrawStyle = ["Solid","Dashed","Dotted","Dashdot"] + vobj.addProperty( + "App::PropertyEnumeration", + "DrawStyle", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The type of line to draw this axis"), + locked=True, + ) + vobj.DrawStyle = ["Solid", "Dashed", "Dotted", "Dashdot"] vobj.DrawStyle = "Dashdot" if not "BubblePosition" in pl: - vobj.addProperty("App::PropertyEnumeration","BubblePosition","Axis",QT_TRANSLATE_NOOP("App::Property","Where to add bubbles to this axis: Start, end, both or none"), locked=True) - vobj.BubblePosition = ["Start","End","Both","None","Arrow left","Arrow right","Bar left","Bar right"] + vobj.addProperty( + "App::PropertyEnumeration", + "BubblePosition", + "Axis", + QT_TRANSLATE_NOOP( + "App::Property", "Where to add bubbles to this axis: Start, end, both or none" + ), + locked=True, + ) + vobj.BubblePosition = [ + "Start", + "End", + "Both", + "None", + "Arrow left", + "Arrow right", + "Bar left", + "Bar right", + ] if not "LineWidth" in pl: - vobj.addProperty("App::PropertyFloat","LineWidth","Axis",QT_TRANSLATE_NOOP("App::Property","The line width to draw this axis"), locked=True) + vobj.addProperty( + "App::PropertyFloat", + "LineWidth", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The line width to draw this axis"), + locked=True, + ) vobj.LineWidth = 1 if not "LineColor" in pl: - vobj.addProperty("App::PropertyColor","LineColor","Axis",QT_TRANSLATE_NOOP("App::Property","The color of this axis"), locked=True) + vobj.addProperty( + "App::PropertyColor", + "LineColor", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The color of this axis"), + locked=True, + ) vobj.LineColor = ArchCommands.getDefaultColor("Helpers") if not "StartNumber" in pl: - vobj.addProperty("App::PropertyInteger","StartNumber","Axis",QT_TRANSLATE_NOOP("App::Property","The number of the first axis"), locked=True) + vobj.addProperty( + "App::PropertyInteger", + "StartNumber", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The number of the first axis"), + locked=True, + ) vobj.StartNumber = 1 if not "FontName" in pl: - vobj.addProperty("App::PropertyFont","FontName","Axis",QT_TRANSLATE_NOOP("App::Property","The font to use for texts"), locked=True) + vobj.addProperty( + "App::PropertyFont", + "FontName", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The font to use for texts"), + locked=True, + ) vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: - vobj.addProperty("App::PropertyLength","FontSize","Axis",QT_TRANSLATE_NOOP("App::Property","The font size"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "FontSize", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "The font size"), + locked=True, + ) vobj.FontSize = ts if not "ShowLabel" in pl: - vobj.addProperty("App::PropertyBool","ShowLabel","Axis",QT_TRANSLATE_NOOP("App::Property","If true, show the labels")) + vobj.addProperty( + "App::PropertyBool", + "ShowLabel", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "If true, show the labels"), + ) if not "LabelOffset" in pl: - vobj.addProperty("App::PropertyPlacement","LabelOffset","Axis",QT_TRANSLATE_NOOP("App::Property","A transformation to apply to each label"), locked=True) + vobj.addProperty( + "App::PropertyPlacement", + "LabelOffset", + "Axis", + QT_TRANSLATE_NOOP("App::Property", "A transformation to apply to each label"), + locked=True, + ) - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Axis_Tree.svg" def claimChildren(self): @@ -244,14 +367,14 @@ class _ViewProviderAxis: sep.addChild(self.lineset) sep.addChild(self.bubbleset) sep.addChild(self.labelset) - vobj.addDisplayMode(sep,"Default") - self.onChanged(vobj,"BubbleSize") - self.onChanged(vobj,"ShowLabel") - self.onChanged(vobj,"LineColor") - self.onChanged(vobj,"LineWidth") - self.onChanged(vobj,"DrawStyle") + vobj.addDisplayMode(sep, "Default") + self.onChanged(vobj, "BubbleSize") + self.onChanged(vobj, "ShowLabel") + self.onChanged(vobj, "LineColor") + self.onChanged(vobj, "LineWidth") + self.onChanged(vobj, "DrawStyle") - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): return ["Default"] @@ -259,11 +382,11 @@ class _ViewProviderAxis: return "Default" - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): return mode - def updateData(self,obj,prop): + def updateData(self, obj, prop): if prop == "Shape": if obj.Shape: @@ -278,7 +401,7 @@ class _ViewProviderAxis: i += 1 vset.append(-1) self.linecoords.point.setValues(verts) - self.lineset.coordIndex.setValues(0,len(vset),vset) + self.lineset.coordIndex.setValues(0, len(vset), vset) self.lineset.coordIndex.setNum(len(vset)) elif prop in ["Placement", "Length"] and not hasattr(obj, "Distances"): # copy values from FlatLines/Wireframe nodes @@ -288,43 +411,43 @@ class _ViewProviderAxis: coords = rn.getChild(1) pts = coords.point.getValues() self.linecoords.point.setValues(pts) - #self.linecoords.point.setNum(len(pts)) + # self.linecoords.point.setNum(len(pts)) sw = rn.getChild(2) if sw.getNumChildren() < 4: return - edges = sw.getChild(sw.getNumChildren()-2) + edges = sw.getChild(sw.getNumChildren() - 2) if not edges.getNumChildren(): return if edges.getChild(0).getNumChildren() < 4: return eset = edges.getChild(0).getChild(3) vset = eset.coordIndex.getValues() - self.lineset.coordIndex.setValues(0,len(vset),vset) + self.lineset.coordIndex.setValues(0, len(vset), vset) self.lineset.coordIndex.setNum(len(vset)) - self.onChanged(obj.ViewObject,"BubbleSize") - self.onChanged(obj.ViewObject,"ShowLabel") + self.onChanged(obj.ViewObject, "BubbleSize") + self.onChanged(obj.ViewObject, "ShowLabel") def onChanged(self, vobj, prop): if prop == "LineColor": - if hasattr(vobj,"LineColor"): + if hasattr(vobj, "LineColor"): l = vobj.LineColor - self.mat.diffuseColor.setValue([l[0],l[1],l[2]]) + self.mat.diffuseColor.setValue([l[0], l[1], l[2]]) elif prop == "DrawStyle": - if hasattr(vobj,"DrawStyle"): + if hasattr(vobj, "DrawStyle"): if vobj.DrawStyle == "Solid": - self.linestyle.linePattern = 0xffff + self.linestyle.linePattern = 0xFFFF elif vobj.DrawStyle == "Dashed": - self.linestyle.linePattern = 0xf00f + self.linestyle.linePattern = 0xF00F elif vobj.DrawStyle == "Dotted": - self.linestyle.linePattern = 0x0f0f + self.linestyle.linePattern = 0x0F0F else: - self.linestyle.linePattern = 0xff88 + self.linestyle.linePattern = 0xFF88 elif prop == "LineWidth": - if hasattr(vobj,"LineWidth"): + if hasattr(vobj, "LineWidth"): self.linestyle.lineWidth = vobj.LineWidth - elif prop in ["BubbleSize","BubblePosition","FontName","FontSize"]: - if hasattr(self,"bubbleset"): + elif prop in ["BubbleSize", "BubblePosition", "FontName", "FontSize"]: + if hasattr(self, "bubbleset"): if self.bubbles: self.bubbleset.removeChild(self.bubbles) self.bubbles = None @@ -332,29 +455,44 @@ class _ViewProviderAxis: if vobj.Object.Shape.Edges: self.bubbles = coin.SoSeparator() self.bubblestyle = coin.SoDrawStyle() - self.bubblestyle.linePattern = 0xffff + self.bubblestyle.linePattern = 0xFFFF self.bubbles.addChild(self.bubblestyle) self.bubbletexts = [] self.bubbledata = [] pos = ["Start"] - if hasattr(vobj,"BubblePosition"): - if vobj.BubblePosition in ["Both","Arrow left","Arrow right","Bar left","Bar right"]: - pos = ["Start","End"] + if hasattr(vobj, "BubblePosition"): + if vobj.BubblePosition in [ + "Both", + "Arrow left", + "Arrow right", + "Bar left", + "Bar right", + ]: + pos = ["Start", "End"] elif vobj.BubblePosition == "None": pos = [] else: pos = [vobj.BubblePosition] e = len(vobj.Object.Shape.Edges) - if getattr(vobj.Object,"Limit",0): + if getattr(vobj.Object, "Limit", 0): e //= 2 - n = len(getattr(vobj.Object,"Distances",[])) - for i in range(min(e,n)): + n = len(getattr(vobj.Object, "Distances", [])) + for i in range(min(e, n)): for p in pos: - if getattr(vobj.Object,"Limit",0): - verts = [vobj.Object.Placement.inverse().multVec(vobj.Object.Shape.Edges[i*2].Vertexes[0].Point), - vobj.Object.Placement.inverse().multVec(vobj.Object.Shape.Edges[i*2+1].Vertexes[0].Point)] + if getattr(vobj.Object, "Limit", 0): + verts = [ + vobj.Object.Placement.inverse().multVec( + vobj.Object.Shape.Edges[i * 2].Vertexes[0].Point + ), + vobj.Object.Placement.inverse().multVec( + vobj.Object.Shape.Edges[i * 2 + 1].Vertexes[0].Point + ), + ] else: - verts = [vobj.Object.Placement.inverse().multVec(v.Point) for v in vobj.Object.Shape.Edges[i].Vertexes] + verts = [ + vobj.Object.Placement.inverse().multVec(v.Point) + for v in vobj.Object.Shape.Edges[i].Vertexes + ] arrow = None if p == "Start": p1 = verts[0] @@ -372,55 +510,73 @@ class _ViewProviderAxis: arrow = True dv = p2.sub(p1) dv.normalize() - if hasattr(vobj.BubbleSize,"Value"): - rad = vobj.BubbleSize.Value/2 + if hasattr(vobj.BubbleSize, "Value"): + rad = vobj.BubbleSize.Value / 2 else: - rad = vobj.BubbleSize/2 + rad = vobj.BubbleSize / 2 center = p2.add(Vector(dv).multiply(rad)) - normal = vobj.Object.Placement.Rotation.multVec(Vector(0,0,1)) + normal = vobj.Object.Placement.Rotation.multVec(Vector(0, 0, 1)) chord = dv.cross(normal) if arrow: - p3 = p2.add(Vector(chord).multiply(rad/2).negative()) + p3 = p2.add(Vector(chord).multiply(rad / 2).negative()) if vobj.BubblePosition.startswith("Arrow"): - p4 = p3.add(Vector(dv).multiply(rad*2).negative()) - p5 = p2.add(Vector(dv).multiply(rad).negative()).add(Vector(chord).multiply(rad*1.5).negative()) - pts = [tuple(p3),tuple(p5),tuple(p4),tuple(p3)] - center = p5.add(Vector(chord).multiply(rad*2.5)) + p4 = p3.add(Vector(dv).multiply(rad * 2).negative()) + p5 = p2.add(Vector(dv).multiply(rad).negative()).add( + Vector(chord).multiply(rad * 1.5).negative() + ) + pts = [tuple(p3), tuple(p5), tuple(p4), tuple(p3)] + center = p5.add(Vector(chord).multiply(rad * 2.5)) else: - p4 = p3.add(Vector(dv).multiply(rad/2).negative()) - p5 = p4.add(Vector(chord).multiply(rad*1.5).negative()) - p6 = p5.add(Vector(dv).multiply(rad/2)) - pts = [tuple(p3),tuple(p6),tuple(p5),tuple(p4),tuple(p3)] - center = p5.add(Vector(chord).multiply(rad*3)) + p4 = p3.add(Vector(dv).multiply(rad / 2).negative()) + p5 = p4.add(Vector(chord).multiply(rad * 1.5).negative()) + p6 = p5.add(Vector(dv).multiply(rad / 2)) + pts = [ + tuple(p3), + tuple(p6), + tuple(p5), + tuple(p4), + tuple(p3), + ] + center = p5.add(Vector(chord).multiply(rad * 3)) coords = coin.SoCoordinate3() - coords.point.setValues(0,len(pts),pts) + coords.point.setValues(0, len(pts), pts) line = coin.SoFaceSet() line.numVertices.setValue(-1) cir = Part.makePolygon(pts) cir.Placement = vobj.Object.Placement self.bubbledata.append(cir) elif arrow == False: - p3 = p2.add(Vector(chord).multiply(rad/2)) + p3 = p2.add(Vector(chord).multiply(rad / 2)) if vobj.BubblePosition.startswith("Arrow"): - p4 = p3.add(Vector(dv).multiply(rad*2).negative()) - p5 = p2.add(Vector(dv).multiply(rad).negative()).add(Vector(chord).multiply(rad*1.5)) - pts = [tuple(p3),tuple(p4),tuple(p5),tuple(p3)] - center = p5.add(Vector(chord).multiply(rad*2.5).negative()) + p4 = p3.add(Vector(dv).multiply(rad * 2).negative()) + p5 = p2.add(Vector(dv).multiply(rad).negative()).add( + Vector(chord).multiply(rad * 1.5) + ) + pts = [tuple(p3), tuple(p4), tuple(p5), tuple(p3)] + center = p5.add( + Vector(chord).multiply(rad * 2.5).negative() + ) else: - p4 = p3.add(Vector(dv).multiply(rad/2).negative()) - p5 = p4.add(Vector(chord).multiply(rad*1.5)) - p6 = p5.add(Vector(dv).multiply(rad/2)) - pts = [tuple(p3),tuple(p4),tuple(p5),tuple(p6),tuple(p3)] - center = p5.add(Vector(chord).multiply(rad*3).negative()) + p4 = p3.add(Vector(dv).multiply(rad / 2).negative()) + p5 = p4.add(Vector(chord).multiply(rad * 1.5)) + p6 = p5.add(Vector(dv).multiply(rad / 2)) + pts = [ + tuple(p3), + tuple(p4), + tuple(p5), + tuple(p6), + tuple(p3), + ] + center = p5.add(Vector(chord).multiply(rad * 3).negative()) coords = coin.SoCoordinate3() - coords.point.setValues(0,len(pts),pts) + coords.point.setValues(0, len(pts), pts) line = coin.SoFaceSet() line.numVertices.setValue(-1) cir = Part.makePolygon(pts) cir.Placement = vobj.Object.Placement self.bubbledata.append(cir) else: - cir = Part.makeCircle(rad,center) + cir = Part.makeCircle(rad, center) buf = cir.writeInventor() try: cin = coin.SoInput() @@ -428,15 +584,15 @@ class _ViewProviderAxis: cob = coin.SoDB.readAll(cin) except Exception: # workaround for pivy SoInput.setBuffer() bug - buf = buf.replace("\n","") - pts = re.findall(r"point \[(.*?)\]",buf)[0] + buf = buf.replace("\n", "") + pts = re.findall(r"point \[(.*?)\]", buf)[0] pts = pts.split(",") pc = [] for point in pts: v = point.strip().split() - pc.append([float(v[0]),float(v[1]),float(v[2])]) + pc.append([float(v[0]), float(v[1]), float(v[2])]) coords = coin.SoCoordinate3() - coords.point.setValues(0,len(pc),pc) + coords.point.setValues(0, len(pc), pc) line = coin.SoLineSet() line.numVertices.setValue(-1) else: @@ -448,14 +604,14 @@ class _ViewProviderAxis: self.bubbles.addChild(line) st = coin.SoSeparator() tr = coin.SoTransform() - fs = rad*1.5 - if hasattr(vobj,"FontSize"): + fs = rad * 1.5 + if hasattr(vobj, "FontSize"): fs = vobj.FontSize.Value - txpos = FreeCAD.Vector(center.x,center.y-fs/2.5,center.z) + txpos = FreeCAD.Vector(center.x, center.y - fs / 2.5, center.z) tr.translation.setValue(tuple(txpos)) fo = coin.SoFont() fn = params.get_param("textfont") - if hasattr(vobj,"FontName"): + if hasattr(vobj, "FontName"): if vobj.FontName: try: fn = str(vobj.FontName) @@ -465,47 +621,53 @@ class _ViewProviderAxis: fo.size = fs tx = coin.SoAsciiText() tx.justification = coin.SoText2.CENTER - self.bubbletexts.append((tx,vobj.Object.Placement.multVec(center))) + self.bubbletexts.append((tx, vobj.Object.Placement.multVec(center))) st.addChild(tr) st.addChild(fo) st.addChild(tx) self.bubbles.addChild(st) self.bubbleset.addChild(self.bubbles) - self.onChanged(vobj,"NumberingStyle") - if prop in ["FontName","FontSize"]: - self.onChanged(vobj,"ShowLabel") - elif prop in ["NumberingStyle","StartNumber"]: - if hasattr(self,"bubbletexts"): + self.onChanged(vobj, "NumberingStyle") + if prop in ["FontName", "FontSize"]: + self.onChanged(vobj, "ShowLabel") + elif prop in ["NumberingStyle", "StartNumber"]: + if hasattr(self, "bubbletexts"): num = 0 - if hasattr(vobj,"StartNumber"): + if hasattr(vobj, "StartNumber"): if vobj.StartNumber > 1: - num = vobj.StartNumber-1 + num = vobj.StartNumber - 1 alt = False for t in self.bubbletexts: - t[0].string = self.getNumber(vobj,num) + t[0].string = self.getNumber(vobj, num) num += 1 - if hasattr(vobj,"BubblePosition"): - if vobj.BubblePosition in ["Both","Arrow left","Arrow right","Bar left","Bar right"]: + if hasattr(vobj, "BubblePosition"): + if vobj.BubblePosition in [ + "Both", + "Arrow left", + "Arrow right", + "Bar left", + "Bar right", + ]: if not alt: num -= 1 alt = not alt elif prop in ["ShowLabel", "LabelOffset"]: - if hasattr(self,"labels"): + if hasattr(self, "labels"): if self.labels: self.labelset.removeChild(self.labels) self.labels = None - if hasattr(vobj,"ShowLabel") and hasattr(vobj.Object,"Labels"): + if hasattr(vobj, "ShowLabel") and hasattr(vobj.Object, "Labels"): if vobj.ShowLabel: self.labels = coin.SoSeparator() - if hasattr(vobj.Object,"Limit") and vobj.Object.Limit.Value: - n = len(vobj.Object.Shape.Edges)/2 + if hasattr(vobj.Object, "Limit") and vobj.Object.Limit.Value: + n = len(vobj.Object.Shape.Edges) / 2 else: n = len(vobj.Object.Shape.Edges) for i in range(n): if len(vobj.Object.Labels) > i: if vobj.Object.Labels[i]: vert = vobj.Object.Shape.Edges[i].Vertexes[0].Point - if hasattr(vobj,"LabelOffset"): + if hasattr(vobj, "LabelOffset"): pl = FreeCAD.Placement(vobj.LabelOffset) pl.Base = vert.add(pl.Base) st = coin.SoSeparator() @@ -515,16 +677,16 @@ class _ViewProviderAxis: tx.justification = coin.SoText2.LEFT t = vobj.Object.Labels[i] tx.string.setValue(t) - if hasattr(vobj,"FontSize"): + if hasattr(vobj, "FontSize"): fs = vobj.FontSize.Value - elif hasattr(vobj.BubbleSize,"Value"): - fs = vobj.BubbleSize.Value*0.75 + elif hasattr(vobj.BubbleSize, "Value"): + fs = vobj.BubbleSize.Value * 0.75 else: - fs = vobj.BubbleSize*0.75 + fs = vobj.BubbleSize * 0.75 tr.translation.setValue(tuple(pl.Base)) tr.rotation.setValue(pl.Rotation.Q) fn = params.get_param("textfont") - if hasattr(vobj,"FontName"): + if hasattr(vobj, "FontName"): if vobj.FontName: try: fn = str(vobj.FontName) @@ -538,24 +700,36 @@ class _ViewProviderAxis: self.labels.addChild(st) self.labelset.addChild(self.labels) - def getNumber(self,vobj,num): + def getNumber(self, vobj, num): chars = "abcdefghijklmnopqrstuvwxyz" - roman=(('M',1000),('CM',900),('D',500),('CD',400), - ('C',100),('XC',90),('L',50),('XL',40), - ('X',10),('IX',9),('V',5),('IV',4),('I',1)) - if hasattr(vobj.Object,"CustomNumber") and vobj.Object.CustomNumber: + roman = ( + ("M", 1000), + ("CM", 900), + ("D", 500), + ("CD", 400), + ("C", 100), + ("XC", 90), + ("L", 50), + ("XL", 40), + ("X", 10), + ("IX", 9), + ("V", 5), + ("IV", 4), + ("I", 1), + ) + if hasattr(vobj.Object, "CustomNumber") and vobj.Object.CustomNumber: return vobj.Object.CustomNumber - elif hasattr(vobj,"NumberingStyle"): + elif hasattr(vobj, "NumberingStyle"): if vobj.NumberingStyle == "1,2,3": - return str(num+1) + return str(num + 1) elif vobj.NumberingStyle == "01,02,03": - return str(num+1).zfill(2) + return str(num + 1).zfill(2) elif vobj.NumberingStyle == "001,002,003": - return str(num+1).zfill(3) + return str(num + 1).zfill(3) elif vobj.NumberingStyle == "A,B,C": result = "" - base = num//26 + base = num // 26 if base: result += chars[base].upper() remainder = num % 26 @@ -563,7 +737,7 @@ class _ViewProviderAxis: return result elif vobj.NumberingStyle == "a,b,c": result = "" - base = num//26 + base = num // 26 if base: result += chars[base] remainder = num % 26 @@ -579,13 +753,13 @@ class _ViewProviderAxis: n -= integer return result elif vobj.NumberingStyle == "L0,L1,L2": - return "L"+str(num) + return "L" + str(num) else: - return str(num+1) + return str(num + 1) def getTextData(self): - return [(t[0].string.getValues()[0],t[1]) for t in self.bubbletexts] + return [(t[0].string.getValues()[0], t[1]) for t in self.bubbletexts] def getShapeData(self): @@ -609,25 +783,22 @@ class _ViewProviderAxis: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) # The default Part::FeaturePython context menu contains a `Set colors` # option. This option makes no sense for Axis objects. We therefore # override this menu and have to add our own `Transform` item. # To override the default menu this function must return `True`. - action_transform = QtGui.QAction(FreeCADGui.getIcon("Std_TransformManip.svg"), - translate("Command", "Transform"), # Context `Command` instead of `Arch`. - menu) - QtCore.QObject.connect(action_transform, - QtCore.SIGNAL("triggered()"), - self.transform) + action_transform = QtGui.QAction( + FreeCADGui.getIcon("Std_TransformManip.svg"), + translate("Command", "Transform"), # Context `Command` instead of `Arch`. + menu, + ) + QtCore.QObject.connect(action_transform, QtCore.SIGNAL("triggered()"), self.transform) menu.addAction(action_transform) return True @@ -642,14 +813,13 @@ class _ViewProviderAxis: return None - def loads(self,state): + def loads(self, state): return None class _AxisTaskPanel: - - '''The editmode TaskPanel for Axis objects''' + """The editmode TaskPanel for Axis objects""" def __init__(self): @@ -671,9 +841,9 @@ class _AxisTaskPanel: self.tree = QtGui.QTreeWidget(self.form) self.grid.addWidget(self.tree, 1, 0, 1, 2) self.tree.setColumnCount(4) - self.tree.header().resizeSection(0,50) - self.tree.header().resizeSection(1,80) - self.tree.header().resizeSection(2,60) + self.tree.header().resizeSection(0, 50) + self.tree.header().resizeSection(1, 80) + self.tree.header().resizeSection(2, 60) # buttons self.addButton = QtGui.QPushButton(self.form) @@ -690,7 +860,9 @@ class _AxisTaskPanel: QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit) + QtCore.QObject.connect( + self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit + ) self.update() def isAllowedAlterSelection(self): @@ -706,32 +878,31 @@ class _AxisTaskPanel: return QtGui.QDialogButtonBox.Close def update(self): - - 'fills the treewidget' + "fills the treewidget" self.updating = True self.tree.clear() if self.obj and hasattr(self.obj, "Distances"): for i in range(len(self.obj.Distances)): item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,str(i+1)) + item.setText(0, str(i + 1)) if len(self.obj.Distances) > i: - item.setText(1,str(self.obj.Distances[i])) + item.setText(1, str(self.obj.Distances[i])) if len(self.obj.Angles) > i: - item.setText(2,str(self.obj.Angles[i])) - if hasattr(self.obj,"Labels"): + item.setText(2, str(self.obj.Angles[i])) + if hasattr(self.obj, "Labels"): if len(self.obj.Labels) > i: - item.setText(3,str(self.obj.Labels[i])) + item.setText(3, str(self.obj.Labels[i])) item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - item.setTextAlignment(0,QtCore.Qt.AlignLeft) + item.setTextAlignment(0, QtCore.Qt.AlignLeft) self.retranslateUi(self.form) self.updating = False def addElement(self): item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,str(self.tree.topLevelItemCount())) - item.setText(1,"1.0") - item.setText(2,"0.0") + item.setText(0, str(self.tree.topLevelItemCount())) + item.setText(1, "1.0") + item.setText(2, "0.0") item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) self.resetObject() @@ -739,24 +910,23 @@ class _AxisTaskPanel: it = self.tree.currentItem() if it: - nr = int(it.text(0))-1 + nr = int(it.text(0)) - 1 self.resetObject(remove=nr) self.update() - def edit(self,item,column): + def edit(self, item, column): if not self.updating: self.resetObject() - def resetObject(self,remove=None): - + def resetObject(self, remove=None): "transfers the values from the widget to the object" d = [] a = [] l = [] for i in range(self.tree.topLevelItemCount()): - it = self.tree.findItems(str(i+1),QtCore.Qt.MatchExactly,0)[0] + it = self.tree.findItems(str(i + 1), QtCore.Qt.MatchExactly, 0)[0] if (remove is None) or (remove != i): if it.text(1): d.append(float(it.text(1))) @@ -784,8 +954,16 @@ class _AxisTaskPanel: TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Axes", None)) self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None)) self.addButton.setText(QtGui.QApplication.translate("Arch", "Add", None)) - self.title.setText(QtGui.QApplication.translate("Arch", "Distances (mm) and angles (deg) between axes", None)) - self.tree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Axis", None), - QtGui.QApplication.translate("Arch", "Distance", None), - QtGui.QApplication.translate("Arch", "Angle", None), - QtGui.QApplication.translate("Arch", "Label", None)]) + self.title.setText( + QtGui.QApplication.translate( + "Arch", "Distances (mm) and angles (deg) between axes", None + ) + ) + self.tree.setHeaderLabels( + [ + QtGui.QApplication.translate("Arch", "Axis", None), + QtGui.QApplication.translate("Arch", "Distance", None), + QtGui.QApplication.translate("Arch", "Angle", None), + QtGui.QApplication.translate("Arch", "Label", None), + ] + ) diff --git a/src/Mod/BIM/ArchAxisSystem.py b/src/Mod/BIM/ArchAxisSystem.py index 65f513d51e..739bf16eaf 100644 --- a/src/Mod/BIM/ArchAxisSystem.py +++ b/src/Mod/BIM/ArchAxisSystem.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Axis System" +__title__ = "FreeCAD Axis System" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchAxisSystem # \ingroup ARCH @@ -45,48 +45,61 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _AxisSystem: - "The Axis System object" - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "AxisSystem" self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Axes" in pl: - obj.addProperty("App::PropertyLinkList","Axes","AxisSystem", QT_TRANSLATE_NOOP("App::Property","The axes this system is made of"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Axes", + "AxisSystem", + QT_TRANSLATE_NOOP("App::Property", "The axes this system is made of"), + locked=True, + ) if not "Placement" in pl: - obj.addProperty("App::PropertyPlacement","Placement","AxisSystem",QT_TRANSLATE_NOOP("App::Property","The placement of this axis system"), locked=True) + obj.addProperty( + "App::PropertyPlacement", + "Placement", + "AxisSystem", + QT_TRANSLATE_NOOP("App::Property", "The placement of this axis system"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): pass - def onBeforeChange(self,obj,prop): + def onBeforeChange(self, obj, prop): if prop == "Placement": self.Placement = obj.Placement - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): if prop == "Placement": - if hasattr(self,"Placement"): + if hasattr(self, "Placement"): delta = obj.Placement.multiply(self.Placement.inverse()) for o in obj.Axes: o.Placement = delta.multiply(o.Placement) @@ -95,12 +108,11 @@ class _AxisSystem: return None - def loads(self,state): + def loads(self, state): self.Type = "AxisSystem" - def getPoints(self,obj): - + def getPoints(self, obj): "returns the gridpoints of linked axes" pts = [] @@ -108,20 +120,20 @@ class _AxisSystem: 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 # X - set2 = obj.Axes[1].Shape.Edges # Y + set1 = obj.Axes[0].Shape.Edges # X + set2 = obj.Axes[1].Shape.Edges # Y for e1 in set1: for e2 in set2: - pts.extend(DraftGeomUtils.findIntersection(e1,e2)) + pts.extend(DraftGeomUtils.findIntersection(e1, e2)) elif len(obj.Axes) == 3: - set1 = obj.Axes[0].Shape.Edges # X - set2 = obj.Axes[1].Shape.Edges # Y - set3 = obj.Axes[2].Shape.Edges # Z + set1 = obj.Axes[0].Shape.Edges # X + set2 = obj.Axes[1].Shape.Edges # Y + set3 = obj.Axes[2].Shape.Edges # Z bset = [] cv = None for e1 in set1: for e2 in set2: - bset.extend(DraftGeomUtils.findIntersection(e1,e2)) + bset.extend(DraftGeomUtils.findIntersection(e1, e2)) for e3 in set3: if not cv: cv = e3.Vertexes[0].Point @@ -131,39 +143,39 @@ class _AxisSystem: pts.extend([p.add(v) for p in bset]) return pts - def getAxisData(self,obj): + def getAxisData(self, obj): data = [] for axis in obj.Axes: - if hasattr(axis,"Proxy") and hasattr(axis.Proxy,"getAxisData"): + if hasattr(axis, "Proxy") and hasattr(axis.Proxy, "getAxisData"): data.append(axis.Proxy.getAxisData(axis)) return data class _ViewProviderAxisSystem: - "A View Provider for the Axis object" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): import Arch_rc + return ":/icons/Arch_Axis_System_Tree.svg" def claimChildren(self): - if hasattr(self,"axes"): + if hasattr(self, "axes"): return self.axes return [] def attach(self, vobj): self.Object = vobj.Object self.axes = vobj.Object.Axes - vobj.addDisplayMode(coin.SoSeparator(),"Default") + vobj.addDisplayMode(coin.SoSeparator(), "Default") - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): return ["Default"] @@ -171,11 +183,11 @@ class _ViewProviderAxisSystem: return "Default" - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): return mode - def updateData(self,obj,prop): + def updateData(self, obj, prop): self.axes = obj.Axes @@ -205,13 +217,10 @@ class _ViewProviderAxisSystem: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) def edit(self): @@ -221,16 +230,15 @@ class _ViewProviderAxisSystem: return None - def loads(self,state): + def loads(self, state): return None class AxisSystemTaskPanel: + """A TaskPanel for all the section plane object""" - '''A TaskPanel for all the section plane object''' - - def __init__(self,obj): + def __init__(self, obj): self.obj = obj self.form = QtGui.QWidget() @@ -273,9 +281,9 @@ class AxisSystemTaskPanel: return QtGui.QDialogButtonBox.Ok - def getIcon(self,obj): + def getIcon(self, obj): - if hasattr(obj.ViewObject,"Proxy"): + if hasattr(obj.ViewObject, "Proxy"): return QtGui.QIcon(obj.ViewObject.Proxy.getIcon()) elif obj.isDerivedFrom("Sketcher::SketchObject"): return QtGui.QIcon(":/icons/Sketcher_Sketch.svg") @@ -291,16 +299,16 @@ class AxisSystemTaskPanel: if self.obj: for o in self.obj.Axes: item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,o.Label) - item.setToolTip(0,o.Name) - item.setIcon(0,self.getIcon(o)) + item.setText(0, o.Label) + item.setToolTip(0, o.Name) + item.setIcon(0, self.getIcon(o)) self.retranslateUi(self.form) def addElement(self): if self.obj: for o in FreeCADGui.Selection.getSelection(): - if not(o in self.obj.Axes) and (o != self.obj): + if not (o in self.obj.Axes) and (o != self.obj): g = self.obj.Axes g.append(o) self.obj.Axes = g diff --git a/src/Mod/BIM/ArchBuilding.py b/src/Mod/BIM/ArchBuilding.py index 945d6676fc..c5f8065655 100644 --- a/src/Mod/BIM/ArchBuilding.py +++ b/src/Mod/BIM/ArchBuilding.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Building" +__title__ = "FreeCAD Building" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchBuilding # \ingroup ARCH @@ -47,10 +47,12 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond @@ -198,17 +200,17 @@ BuildingTypes = ['Undefined', # fmt: on -def makeBuilding(objectslist=None,name=None): - '''Obsolete, superseded by ArchBuildingPart.makeBuilding. +def makeBuilding(objectslist=None, name=None): + """Obsolete, superseded by ArchBuildingPart.makeBuilding. makeBuilding([objectslist],[name]): creates a building including the - objects from the given list.''' + objects from the given list.""" if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return - obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython","Building") - obj.Label = name if name else translate("Arch","Building") + obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython", "Building") + obj.Label = name if name else translate("Arch", "Building") _Building(obj) if FreeCAD.GuiUp: _ViewProviderBuilding(obj.ViewObject) @@ -218,15 +220,18 @@ def makeBuilding(objectslist=None,name=None): class _CommandBuilding: - "the Arch Building command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Building', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Building","Building"), - 'Accel': "B, U", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Building","Creates a building object including selected objects.")} + return { + "Pixmap": "Arch_Building", + "MenuText": QtCore.QT_TRANSLATE_NOOP("Arch_Building", "Building"), + "Accel": "B, U", + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Arch_Building", "Creates a building object including selected objects." + ), + } def IsActive(self): @@ -238,32 +243,44 @@ class _CommandBuilding: link = params.get_param_arch("FreeLinking") buildingobj = [] warning = False - for obj in sel : - if not Draft.getType(obj) in ["Site", "Building"] : + for obj in sel: + if not Draft.getType(obj) in ["Site", "Building"]: buildingobj.append(obj) - else : - if link : + else: + if link: buildingobj.append(obj) else: warning = True - if warning : - message = translate( "Arch" , "You can put anything but Site and Building objects in a Building object.\n\ + if warning: + message = ( + translate( + "Arch", + "You can put anything but Site and Building objects in a Building object.\n\ Building object is not allowed to accept Site and Building objects.\n\ Site and Building objects will be removed from the selection.\n\ -You can change that in the preferences.") + "\n" - ArchCommands.printMessage( message ) +You can change that in the preferences.", + ) + + "\n" + ) + ArchCommands.printMessage(message) if sel and len(buildingobj) == 0: - message = translate( "Arch" , "There is no valid object in the selection.\n\ -Building creation aborted.") + "\n" - ArchCommands.printMessage( message ) - else : + message = ( + translate( + "Arch", + "There is no valid object in the selection.\n\ +Building creation aborted.", + ) + + "\n" + ) + ArchCommands.printMessage(message) + else: ss = "[ " for o in buildingobj: ss += "FreeCAD.ActiveDocument." + o.Name + ", " ss += "]" - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Building")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Building")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("obj = Arch.makeBuilding("+ss+")") + FreeCADGui.doCommand("obj = Arch.makeBuilding(" + ss + ")") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() @@ -271,63 +288,72 @@ Building creation aborted.") + "\n" class _Building(ArchFloor._Floor): - "The Building object" - def __init__(self,obj): + def __init__(self, obj): - ArchFloor._Floor.__init__(self,obj) + ArchFloor._Floor.__init__(self, obj) self.Type = "Building" self.setProperties(obj) obj.IfcType = "Building" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "BuildingType" in pl: - obj.addProperty("App::PropertyEnumeration","BuildingType","Arch",QT_TRANSLATE_NOOP("App::Property","The type of this building"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "BuildingType", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "The type of this building"), + locked=True, + ) obj.BuildingType = BuildingTypes - obj.setEditorMode('Height',2) + obj.setEditorMode("Height", 2) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchFloor._Floor.onDocumentRestored(self,obj) + ArchFloor._Floor.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Building" class _ViewProviderBuilding(ArchFloor._ViewProviderFloor): - "A View Provider for the Building object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchFloor._ViewProviderFloor.__init__(self,vobj) + ArchFloor._ViewProviderFloor.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Building_Tree.svg" - def setupContextMenu(self,vobj,menu): - from PySide import QtCore,QtGui + def setupContextMenu(self, vobj, menu): + from PySide import QtCore, QtGui import Arch_rc - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - action1 = QtGui.QAction(QtGui.QIcon(":/icons/Arch_BuildingPart.svg"),"Convert to BuildingPart",menu) - QtCore.QObject.connect(action1,QtCore.SIGNAL("triggered()"),self.convertToBuildingPart) + action1 = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_BuildingPart.svg"), "Convert to BuildingPart", menu + ) + QtCore.QObject.connect(action1, QtCore.SIGNAL("triggered()"), self.convertToBuildingPart) menu.addAction(action1) def convertToBuildingPart(self): - if hasattr(self,"Object"): + if hasattr(self, "Object"): import ArchBuildingPart from draftutils import todo - todo.ToDo.delay(ArchBuildingPart.convertFloors,self.Object) + + todo.ToDo.delay(ArchBuildingPart.convertFloors, self.Object) if FreeCAD.GuiUp: - FreeCADGui.addCommand('Arch_Building',_CommandBuilding()) + FreeCADGui.addCommand("Arch_Building", _CommandBuilding()) diff --git a/src/Mod/BIM/ArchBuildingPart.py b/src/Mod/BIM/ArchBuildingPart.py index c481bff7a7..3d3751a650 100644 --- a/src/Mod/BIM/ArchBuildingPart.py +++ b/src/Mod/BIM/ArchBuildingPart.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch BuildingPart" +__title__ = "FreeCAD Arch BuildingPart" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchBuildingPart # \ingroup ARCH @@ -52,10 +52,12 @@ if FreeCAD.GuiUp: import draftutils.units as units else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond unicode = str @@ -205,47 +207,117 @@ BuildingTypes = ['Undefined', class BuildingPart(ArchIFC.IfcProduct): - - "The BuildingPart object" - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "BuildingPart" - obj.addExtension('App::GroupExtensionPython') - #obj.addExtension('App::OriginGroupExtensionPython') + obj.addExtension("App::GroupExtensionPython") + # obj.addExtension('App::OriginGroupExtensionPython') self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): ArchIFC.IfcProduct.setProperties(self, obj) pl = obj.PropertiesList if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The height of this object"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The height of this object"), + locked=True, + ) if not "HeightPropagate" in pl: - obj.addProperty("App::PropertyBool","HeightPropagate","Children",QT_TRANSLATE_NOOP("App::Property","If true, the height value propagates to contained objects if the height of those objects is set to 0"), locked=True) + obj.addProperty( + "App::PropertyBool", + "HeightPropagate", + "Children", + QT_TRANSLATE_NOOP( + "App::Property", + "If true, the height value propagates to contained objects if the height of those objects is set to 0", + ), + locked=True, + ) obj.HeightPropagate = True if not "LevelOffset" in pl: - obj.addProperty("App::PropertyDistance","LevelOffset","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The level of the (0,0,0) point of this level"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "LevelOffset", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The level of the (0,0,0) point of this level"), + locked=True, + ) if not "Area" in pl: - obj.addProperty("App::PropertyArea","Area", "BuildingPart",QT_TRANSLATE_NOOP("App::Property","The computed floor area of this floor"), locked=True) + obj.addProperty( + "App::PropertyArea", + "Area", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The computed floor area of this floor"), + locked=True, + ) if not "Description" in pl: - obj.addProperty("App::PropertyString","Description","Component",QT_TRANSLATE_NOOP("App::Property","An optional description for this component"), locked=True) + obj.addProperty( + "App::PropertyString", + "Description", + "Component", + QT_TRANSLATE_NOOP("App::Property", "An optional description for this component"), + locked=True, + ) if not "Tag" in pl: - obj.addProperty("App::PropertyString","Tag","Component",QT_TRANSLATE_NOOP("App::Property","An optional tag for this component"), locked=True) + obj.addProperty( + "App::PropertyString", + "Tag", + "Component", + QT_TRANSLATE_NOOP("App::Property", "An optional tag for this component"), + locked=True, + ) if not "Shape" in pl: - obj.addProperty("Part::PropertyPartShape","Shape","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The shape of this object"), locked=True) + obj.addProperty( + "Part::PropertyPartShape", + "Shape", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The shape of this object"), + locked=True, + ) if not "SavedInventor" in pl: - obj.addProperty("App::PropertyFileIncluded","SavedInventor","BuildingPart",QT_TRANSLATE_NOOP("App::Property","This property stores an OpenInventor representation for this object"), locked=True) - obj.setEditorMode("SavedInventor",2) + obj.addProperty( + "App::PropertyFileIncluded", + "SavedInventor", + "BuildingPart", + QT_TRANSLATE_NOOP( + "App::Property", + "This property stores an OpenInventor representation for this object", + ), + locked=True, + ) + obj.setEditorMode("SavedInventor", 2) if not "OnlySolids" in pl: - obj.addProperty("App::PropertyBool","OnlySolids","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, only solids will be collected by this object when referenced from other files"), locked=True) + obj.addProperty( + "App::PropertyBool", + "OnlySolids", + "BuildingPart", + QT_TRANSLATE_NOOP( + "App::Property", + "If true, only solids will be collected by this object when referenced from other files", + ), + locked=True, + ) obj.OnlySolids = True if not "MaterialsTable" in pl: - obj.addProperty("App::PropertyMap","MaterialsTable","BuildingPart",QT_TRANSLATE_NOOP("App::Property","A MaterialName:SolidIndexesList map that relates material names with solid indexes to be used when referencing this object from other files"), locked=True) + obj.addProperty( + "App::PropertyMap", + "MaterialsTable", + "BuildingPart", + QT_TRANSLATE_NOOP( + "App::Property", + "A MaterialName:SolidIndexesList map that relates material names with solid indexes to be used when referencing this object from other files", + ), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) @@ -253,23 +325,23 @@ class BuildingPart(ArchIFC.IfcProduct): return None - def loads(self,state): + def loads(self, state): self.Type = "BuildingPart" - def onBeforeChange(self,obj,prop): + def onBeforeChange(self, obj, prop): if prop == "Placement": self.oldPlacement = FreeCAD.Placement(obj.Placement) - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): import math ArchIFC.IfcProduct.onChanged(self, obj, prop) # clean svg cache if needed - if prop in ["Placement","Group"]: + if prop in ["Placement", "Group"]: self.svgcache = None self.shapecache = None @@ -277,7 +349,7 @@ class BuildingPart(ArchIFC.IfcProduct): self.touchChildren(obj) elif prop == "Placement": - if hasattr(self,"oldPlacement") and self.oldPlacement != obj.Placement: + if hasattr(self, "oldPlacement") and self.oldPlacement != obj.Placement: deltap = obj.Placement.Base.sub(self.oldPlacement.Base) if deltap.Length == 0: deltap = None @@ -286,29 +358,31 @@ class BuildingPart(ArchIFC.IfcProduct): deltar = None for child in self.getMovableChildren(obj): if deltar: - child.Placement.rotate(self.oldPlacement.Base, - deltar.Axis, - math.degrees(deltar.Angle), - comp=True) + child.Placement.rotate( + self.oldPlacement.Base, + deltar.Axis, + math.degrees(deltar.Angle), + comp=True, + ) if deltap: child.Placement.move(deltap) - def execute(self,obj): - + def execute(self, obj): "gather all the child shapes into a compound" pl = obj.Placement - shapes,materialstable = self.getShapes(obj) + shapes, materialstable = self.getShapes(obj) if shapes: import Part + if obj.OnlySolids: f = [] for s in shapes: f.extend(s.Solids) - #print("faces before compound:",len(f)) + # print("faces before compound:",len(f)) obj.Shape = Part.makeCompound(f) - #print("faces after compound:",len(obj.Shape.Faces)) - #print("recomputing ",obj.Label) + # print("faces after compound:",len(obj.Shape.Faces)) + # print("recomputing ",obj.Label) else: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl @@ -316,34 +390,33 @@ class BuildingPart(ArchIFC.IfcProduct): obj.MaterialsTable = materialstable if obj.ViewObject: # update the autogroup box if needed - obj.ViewObject.Proxy.onChanged(obj.ViewObject,"AutoGroupBox") + obj.ViewObject.Proxy.onChanged(obj.ViewObject, "AutoGroupBox") def getMovableChildren(self, obj): - "recursively get movable children" result = [] for child in obj.Group: if child.isDerivedFrom("App::DocumentObjectGroup"): result.extend(self.getMovableChildren(child)) - if not hasattr(child,"MoveWithHost") or child.MoveWithHost: - if hasattr(child,"Placement"): + if not hasattr(child, "MoveWithHost") or child.MoveWithHost: + if hasattr(child, "Placement"): result.append(child) return result - def getArea(self,obj): - + def getArea(self, obj): "computes the area of this floor by adding its inner spaces" area = 0 - if hasattr(obj,"Group"): + if hasattr(obj, "Group"): for child in obj.Group: - if (Draft.get_type(child) in ["Space","BuildingPart"]) and hasattr(child,"IfcType"): + if (Draft.get_type(child) in ["Space", "BuildingPart"]) and hasattr( + child, "IfcType" + ): area += child.Area.Value return area - def getShapes(self,obj): - + def getShapes(self, obj): "recursively get the shapes of objects inside this BuildingPart" shapes = [] @@ -351,51 +424,49 @@ class BuildingPart(ArchIFC.IfcProduct): materialstable = {} for child in Draft.get_group_contents(obj, walls=True): if not Draft.get_type(child) in ["Space"]: - if hasattr(child,'Shape') and child.Shape: + if hasattr(child, "Shape") and child.Shape: shapes.append(child.Shape) for solid in child.Shape.Solids: matname = "Undefined" - if hasattr(child,"Material") and child.Material: + if hasattr(child, "Material") and child.Material: matname = child.Material.Name if matname in materialstable: - materialstable[matname] = materialstable[matname]+","+str(solidindex) + materialstable[matname] = ( + materialstable[matname] + "," + str(solidindex) + ) else: materialstable[matname] = str(solidindex) solidindex += 1 - return shapes,materialstable - - def getSpaces(self,obj): + return shapes, materialstable + def getSpaces(self, obj): "gets the list of Spaces that have this object as their Zone property" g = [] for o in obj.OutList: - if hasattr(o,"Zone"): + if hasattr(o, "Zone"): if o.Zone == obj: g.append(o) return g - def touchChildren(self,obj): - + def touchChildren(self, obj): "Touches all descendents where applicable" g = [] - if hasattr(obj,"Group"): + if hasattr(obj, "Group"): g = obj.Group - elif (Draft.getType(obj) in ["Wall","Structure"]): + elif Draft.getType(obj) in ["Wall", "Structure"]: g = obj.Additions for child in g: - if Draft.getType(child) in ["Wall","Structure"]: + if Draft.getType(child) in ["Wall", "Structure"]: if not child.Height.Value: - FreeCAD.Console.PrintLog("Auto-updating Height of "+child.Name+"\n") + FreeCAD.Console.PrintLog("Auto-updating Height of " + child.Name + "\n") self.touchChildren(child) child.Proxy.execute(child) - elif Draft.getType(child) in ["App::DocumentObjectGroup","Group","BuildingPart"]: + elif Draft.getType(child) in ["App::DocumentObjectGroup", "Group", "BuildingPart"]: self.touchChildren(child) - - def addObject(self,obj,child): - + def addObject(self, obj, child): "Adds an object to the group of this BuildingPart" if not child in obj.Group: @@ -403,13 +474,12 @@ class BuildingPart(ArchIFC.IfcProduct): g.append(child) obj.Group = g - def autogroup(self,obj,child): - + def autogroup(self, obj, child): "Adds an object to the group of this BuildingPart automatically" if obj.ViewObject: - if hasattr(obj.ViewObject.Proxy,"autobbox") and obj.ViewObject.Proxy.autobbox: - if hasattr(child,"Shape") and child.Shape: + if hasattr(obj.ViewObject.Proxy, "autobbox") and obj.ViewObject.Proxy.autobbox: + if hasattr(child, "Shape") and child.Shape: abb = obj.ViewObject.Proxy.autobbox cbb = child.Shape.BoundBox if abb.isValid(): @@ -421,17 +491,15 @@ class BuildingPart(ArchIFC.IfcProduct): print(v.Point) cbb.add(v.Point) if cbb.isValid() and abb.isInside(cbb): - self.addObject(obj,child) + self.addObject(obj, child) return True return False class ViewProviderBuildingPart: - - "A View Provider for the BuildingPart object" - def __init__(self,vobj): + def __init__(self, vobj): if vobj: vobj.addExtension("Gui::ViewProviderGroupExtensionPython") @@ -440,117 +508,326 @@ class ViewProviderBuildingPart: vobj.ShapeColor = ArchCommands.getDefaultColor("Helpers") self.Object = vobj.Object - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList if not "LineWidth" in pl: - vobj.addProperty("App::PropertyFloat","LineWidth","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The line width of this object"), locked=True) + vobj.addProperty( + "App::PropertyFloat", + "LineWidth", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The line width of this object"), + locked=True, + ) vobj.LineWidth = 1 if not "OverrideUnit" in pl: - vobj.addProperty("App::PropertyString","OverrideUnit","BuildingPart",QT_TRANSLATE_NOOP("App::Property","An optional unit to express levels"), locked=True) + vobj.addProperty( + "App::PropertyString", + "OverrideUnit", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "An optional unit to express levels"), + locked=True, + ) if not "DisplayOffset" in pl: - vobj.addProperty("App::PropertyPlacement","DisplayOffset","BuildingPart",QT_TRANSLATE_NOOP("App::Property","A transformation to apply to the level mark"), locked=True) - vobj.DisplayOffset = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(1,0,0),90)) + vobj.addProperty( + "App::PropertyPlacement", + "DisplayOffset", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "A transformation to apply to the level mark"), + locked=True, + ) + vobj.DisplayOffset = FreeCAD.Placement( + FreeCAD.Vector(0, 0, 0), FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90) + ) if not "ShowLevel" in pl: - vobj.addProperty("App::PropertyBool","ShowLevel","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, show the level"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowLevel", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "If true, show the level"), + locked=True, + ) vobj.ShowLevel = True if not "ShowUnit" in pl: - vobj.addProperty("App::PropertyBool","ShowUnit","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, show the unit on the level tag"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowUnit", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "If true, show the unit on the level tag"), + locked=True, + ) if not "OriginOffset" in pl: - vobj.addProperty("App::PropertyBool","OriginOffset","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, display offset will affect the origin mark too"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "OriginOffset", + "BuildingPart", + QT_TRANSLATE_NOOP( + "App::Property", "If true, display offset will affect the origin mark too" + ), + locked=True, + ) if not "ShowLabel" in pl: - vobj.addProperty("App::PropertyBool","ShowLabel","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, the object's label is displayed"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowLabel", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "If true, the object's label is displayed"), + locked=True, + ) vobj.ShowLabel = True if not "FontName" in pl: - vobj.addProperty("App::PropertyFont","FontName","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The font to be used for texts"), locked=True) + vobj.addProperty( + "App::PropertyFont", + "FontName", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The font to be used for texts"), + locked=True, + ) vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: - vobj.addProperty("App::PropertyLength","FontSize","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The font size of texts"), locked=True) - vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") + vobj.addProperty( + "App::PropertyLength", + "FontSize", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The font size of texts"), + locked=True, + ) + vobj.FontSize = params.get_param("textheight") * params.get_param( + "DefaultAnnoScaleMultiplier" + ) if not "DiffuseColor" in pl: - vobj.addProperty("App::PropertyColorList","DiffuseColor","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The individual face colors"), locked=True) + vobj.addProperty( + "App::PropertyColorList", + "DiffuseColor", + "BuildingPart", + QT_TRANSLATE_NOOP("App::Property", "The individual face colors"), + locked=True, + ) # Interaction properties if not "SetWorkingPlane" in pl: - vobj.addProperty("App::PropertyBool","SetWorkingPlane","Interaction",QT_TRANSLATE_NOOP("App::Property","If true, when activated, the working plane will automatically adapt to this level"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "SetWorkingPlane", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", + "If true, when activated, the working plane will automatically adapt to this level", + ), + locked=True, + ) vobj.SetWorkingPlane = True if not "AutoWorkingPlane" in pl: - vobj.addProperty("App::PropertyBool","AutoWorkingPlane","Interaction",QT_TRANSLATE_NOOP("App::Property","If set to True, the working plane will be kept on Auto mode"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "AutoWorkingPlane", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", "If set to True, the working plane will be kept on Auto mode" + ), + locked=True, + ) if not "ViewData" in pl: - vobj.addProperty("App::PropertyFloatList","ViewData","Interaction",QT_TRANSLATE_NOOP("App::Property","Camera position data associated with this object"), locked=True) - vobj.setEditorMode("ViewData",2) + vobj.addProperty( + "App::PropertyFloatList", + "ViewData", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", "Camera position data associated with this object" + ), + locked=True, + ) + vobj.setEditorMode("ViewData", 2) if not "RestoreView" in pl: - vobj.addProperty("App::PropertyBool","RestoreView","Interaction",QT_TRANSLATE_NOOP("App::Property","If set, the view stored in this object will be restored on double-click"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "RestoreView", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", + "If set, the view stored in this object will be restored on double-click", + ), + locked=True, + ) if not "DoubleClickActivates" in pl: - vobj.addProperty("App::PropertyBool","DoubleClickActivates","Interaction",QT_TRANSLATE_NOOP("App::Property","If True, double-clicking this object in the tree activates it"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "DoubleClickActivates", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", "If True, double-clicking this object in the tree activates it" + ), + locked=True, + ) vobj.DoubleClickActivates = True # inventor saving if not "SaveInventor" in pl: - vobj.addProperty("App::PropertyBool","SaveInventor","Interaction",QT_TRANSLATE_NOOP("App::Property","If this is enabled, the OpenInventor representation of this object will be saved in the FreeCAD file, allowing to reference it in other files in lightweight mode."), locked=True) + vobj.addProperty( + "App::PropertyBool", + "SaveInventor", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", + "If this is enabled, the OpenInventor representation of this object will be saved in the FreeCAD file, allowing to reference it in other files in lightweight mode.", + ), + locked=True, + ) if not "SavedInventor" in pl: - vobj.addProperty("App::PropertyFileIncluded","SavedInventor","Interaction",QT_TRANSLATE_NOOP("App::Property","A slot to save the OpenInventor representation of this object, if enabled"), locked=True) - vobj.setEditorMode("SavedInventor",2) + vobj.addProperty( + "App::PropertyFileIncluded", + "SavedInventor", + "Interaction", + QT_TRANSLATE_NOOP( + "App::Property", + "A slot to save the OpenInventor representation of this object, if enabled", + ), + locked=True, + ) + vobj.setEditorMode("SavedInventor", 2) # children properties if not "ChildrenOverride" in pl: - vobj.addProperty("App::PropertyBool","ChildrenOverride","Children",QT_TRANSLATE_NOOP("App::Property","If true, show the objects contained in this Building Part will adopt these line, color and transparency settings"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ChildrenOverride", + "Children", + QT_TRANSLATE_NOOP( + "App::Property", + "If true, show the objects contained in this Building Part will adopt these line, color and transparency settings", + ), + locked=True, + ) if not "ChildrenLineWidth" in pl: - vobj.addProperty("App::PropertyFloat","ChildrenLineWidth","Children",QT_TRANSLATE_NOOP("App::Property","The line width of child objects"), locked=True) + vobj.addProperty( + "App::PropertyFloat", + "ChildrenLineWidth", + "Children", + QT_TRANSLATE_NOOP("App::Property", "The line width of child objects"), + locked=True, + ) vobj.ChildrenLineWidth = params.get_param_view("DefaultShapeLineWidth") if not "ChildrenLineColor" in pl: - vobj.addProperty("App::PropertyColor","ChildrenLineColor","Children",QT_TRANSLATE_NOOP("App::Property","The line color of child objects"), locked=True) + vobj.addProperty( + "App::PropertyColor", + "ChildrenLineColor", + "Children", + QT_TRANSLATE_NOOP("App::Property", "The line color of child objects"), + locked=True, + ) vobj.ChildrenLineColor = params.get_param_view("DefaultShapeLineColor") & 0xFFFFFF00 if not "ChildrenShapeColor" in pl: - vobj.addProperty("App::PropertyMaterial","ChildrenShapeColor","Children",QT_TRANSLATE_NOOP("App::Property","The shape appearance of child objects"), locked=True) + vobj.addProperty( + "App::PropertyMaterial", + "ChildrenShapeColor", + "Children", + QT_TRANSLATE_NOOP("App::Property", "The shape appearance of child objects"), + locked=True, + ) vobj.ChildrenShapeColor = params.get_param_view("DefaultShapeColor") & 0xFFFFFF00 if not "ChildrenTransparency" in pl: - vobj.addProperty("App::PropertyPercent","ChildrenTransparency","Children",QT_TRANSLATE_NOOP("App::Property","The transparency of child objects"), locked=True) + vobj.addProperty( + "App::PropertyPercent", + "ChildrenTransparency", + "Children", + QT_TRANSLATE_NOOP("App::Property", "The transparency of child objects"), + locked=True, + ) vobj.ChildrenTransparency = params.get_param_view("DefaultShapeTransparency") # clip properties if not "CutView" in pl: - vobj.addProperty("App::PropertyBool","CutView","Clip",QT_TRANSLATE_NOOP("App::Property","Cut the view above this level"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "CutView", + "Clip", + QT_TRANSLATE_NOOP("App::Property", "Cut the view above this level"), + locked=True, + ) if not "CutMargin" in pl: - vobj.addProperty("App::PropertyLength","CutMargin","Clip",QT_TRANSLATE_NOOP("App::Property","The distance between the level plane and the cut line"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "CutMargin", + "Clip", + QT_TRANSLATE_NOOP( + "App::Property", "The distance between the level plane and the cut line" + ), + locked=True, + ) vobj.CutMargin = 1600 if not "AutoCutView" in pl: - vobj.addProperty("App::PropertyBool","AutoCutView","Clip",QT_TRANSLATE_NOOP("App::Property","Turn cutting on when activating this level"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "AutoCutView", + "Clip", + QT_TRANSLATE_NOOP("App::Property", "Turn cutting on when activating this level"), + locked=True, + ) # autogroup properties if not "AutogroupSize" in pl: - vobj.addProperty("App::PropertyIntegerList","AutogroupSize","AutoGroup",QT_TRANSLATE_NOOP("App::Property","The capture box for newly created objects expressed as [XMin,YMin,ZMin,XMax,YMax,ZMax]"), locked=True) + vobj.addProperty( + "App::PropertyIntegerList", + "AutogroupSize", + "AutoGroup", + QT_TRANSLATE_NOOP( + "App::Property", + "The capture box for newly created objects expressed as [XMin,YMin,ZMin,XMax,YMax,ZMax]", + ), + locked=True, + ) if not "AutogroupBox" in pl: - vobj.addProperty("App::PropertyBool","AutogroupBox","AutoGroup",QT_TRANSLATE_NOOP("App::Property","Turns auto group box on/off"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "AutogroupBox", + "AutoGroup", + QT_TRANSLATE_NOOP("App::Property", "Turns auto group box on/off"), + locked=True, + ) if not "AutogroupAutosize" in pl: - vobj.addProperty("App::PropertyBool","AutogroupAutosize","AutoGroup",QT_TRANSLATE_NOOP("App::Property","Automatically set size from contents"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "AutogroupAutosize", + "AutoGroup", + QT_TRANSLATE_NOOP("App::Property", "Automatically set size from contents"), + locked=True, + ) if not "AutogroupMargin" in pl: - vobj.addProperty("App::PropertyLength","AutogroupMargin","AutoGroup",QT_TRANSLATE_NOOP("App::Property","A margin to use when autosize is turned on"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "AutogroupMargin", + "AutoGroup", + QT_TRANSLATE_NOOP("App::Property", "A margin to use when autosize is turned on"), + locked=True, + ) - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc - if hasattr(self,"Object"): + + if hasattr(self, "Object"): if self.Object.IfcType == "Building Storey": return ":/icons/Arch_Floor_Tree.svg" elif self.Object.IfcType == "Building": return ":/icons/Arch_Building_Tree.svg" elif self.Object.IfcType == "Annotation": return ":/icons/BIM_ArchView.svg" - elif hasattr(self.Object,"IfcClass"): + elif hasattr(self.Object, "IfcClass"): from nativeifc import ifc_viewproviders + return ifc_viewproviders.get_icon(self) return ":/icons/Arch_BuildingPart_Tree.svg" - def attach(self,vobj): + def attach(self, vobj): self.Object = vobj.Object self.clip = None from pivy import coin + self.sep = coin.SoGroup() self.mat = coin.SoMaterial() self.sep.addChild(self.mat) @@ -558,10 +835,11 @@ class ViewProviderBuildingPart: self.sep.addChild(self.dst) self.lco = coin.SoCoordinate3() self.sep.addChild(self.lco) - import PartGui # Required for "SoBrepEdgeSet" (because a BuildingPart is not a Part::FeaturePython object). + import PartGui # Required for "SoBrepEdgeSet" (because a BuildingPart is not a Part::FeaturePython object). + lin = coin.SoType.fromName("SoBrepEdgeSet").createInstance() if lin: - lin.coordIndex.setValues([0,1,-1,2,3,-1,4,5,-1]) + lin.coordIndex.setValues([0, 1, -1, 2, 3, -1, 4, 5, -1]) self.sep.addChild(lin) self.bbox = coin.SoSwitch() self.bbox.whichChild = -1 @@ -570,16 +848,18 @@ class ViewProviderBuildingPart: drawstyle = coin.SoDrawStyle() drawstyle.style = coin.SoDrawStyle.LINES drawstyle.lineWidth = 3 - drawstyle.linePattern = 0x0f0f # 0xaa + drawstyle.linePattern = 0x0F0F # 0xaa bboxsep.addChild(drawstyle) self.bbco = coin.SoCoordinate3() bboxsep.addChild(self.bbco) lin = coin.SoIndexedLineSet() - lin.coordIndex.setValues([0,1,2,3,0,-1,4,5,6,7,4,-1,0,4,-1,1,5,-1,2,6,-1,3,7,-1]) + lin.coordIndex.setValues( + [0, 1, 2, 3, 0, -1, 4, 5, 6, 7, 4, -1, 0, 4, -1, 1, 5, -1, 2, 6, -1, 3, 7, -1] + ) bboxsep.addChild(lin) self.sep.addChild(self.bbox) self.tra = coin.SoTransform() - self.tra.rotation.setValue(FreeCAD.Rotation(0,0,90).Q) + self.tra.rotation.setValue(FreeCAD.Rotation(0, 0, 90).Q) self.sep.addChild(self.tra) self.fon = coin.SoFont() self.sep.addChild(self.fon) @@ -587,16 +867,16 @@ class ViewProviderBuildingPart: self.txt.justification = coin.SoText2.LEFT self.txt.string.setValue("level") self.sep.addChild(self.txt) - vobj.addDisplayMode(self.sep,"Default") - self.onChanged(vobj,"ShapeColor") - self.onChanged(vobj,"FontName") - self.onChanged(vobj,"ShowLevel") - self.onChanged(vobj,"FontSize") - self.onChanged(vobj,"AutogroupBox") + vobj.addDisplayMode(self.sep, "Default") + self.onChanged(vobj, "ShapeColor") + self.onChanged(vobj, "FontName") + self.onChanged(vobj, "ShowLevel") + self.onChanged(vobj, "FontSize") + self.onChanged(vobj, "AutogroupBox") self.setProperties(vobj) return - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): return ["Default"] @@ -604,80 +884,115 @@ class ViewProviderBuildingPart: return "Default" - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): return mode - def updateData(self,obj,prop): + def updateData(self, obj, prop): - if prop in ["Placement","LevelOffset"]: - self.onChanged(obj.ViewObject,"OverrideUnit") + if prop in ["Placement", "LevelOffset"]: + self.onChanged(obj.ViewObject, "OverrideUnit") elif prop == "Shape": # gather all the child shapes colors = self.getColors(obj) - if colors and hasattr(obj.ViewObject,"DiffuseColor"): + if colors and hasattr(obj.ViewObject, "DiffuseColor"): if len(colors) == len(obj.Shape.Faces): if colors != obj.ViewObject.DiffuseColor: obj.ViewObject.DiffuseColor = colors self.writeInventor(obj) - #else: - #print("color mismatch:",len(colors),"colors,",len(obj.Shape.Faces),"faces") + # else: + # print("color mismatch:",len(colors),"colors,",len(obj.Shape.Faces),"faces") elif prop == "Group": - self.onChanged(obj.ViewObject,"ChildrenOverride") + self.onChanged(obj.ViewObject, "ChildrenOverride") elif prop == "Label": - self.onChanged(obj.ViewObject,"ShowLabel") - - def getColors(self,obj): + self.onChanged(obj.ViewObject, "ShowLabel") + def getColors(self, obj): "recursively get the colors of objects inside this BuildingPart" colors = [] for child in Draft.get_group_contents(obj, walls=True): if not Draft.get_type(child) in ["Space"]: - if hasattr(child,'Shape') and (hasattr(child.ViewObject,"DiffuseColor") or hasattr(child.ViewObject,"ShapeColor")): - if hasattr(child.ViewObject,"DiffuseColor") and len(child.ViewObject.DiffuseColor) == len(child.Shape.Faces): + if hasattr(child, "Shape") and ( + hasattr(child.ViewObject, "DiffuseColor") + or hasattr(child.ViewObject, "ShapeColor") + ): + if hasattr(child.ViewObject, "DiffuseColor") and len( + child.ViewObject.DiffuseColor + ) == len(child.Shape.Faces): colors.extend(child.ViewObject.DiffuseColor) else: - c = child.ViewObject.ShapeColor[:3]+(1.0 - child.ViewObject.Transparency/100.0,) + c = child.ViewObject.ShapeColor[:3] + ( + 1.0 - child.ViewObject.Transparency / 100.0, + ) for i in range(len(child.Shape.Faces)): colors.append(c) return colors - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - #print(vobj.Object.Label," - ",prop) + # print(vobj.Object.Label," - ",prop) if prop == "ShapeColor": - if hasattr(vobj,"ShapeColor"): + if hasattr(vobj, "ShapeColor"): l = vobj.ShapeColor - self.mat.diffuseColor.setValue([l[0],l[1],l[2]]) + self.mat.diffuseColor.setValue([l[0], l[1], l[2]]) elif prop == "LineWidth": - if hasattr(vobj,"LineWidth"): + if hasattr(vobj, "LineWidth"): self.dst.lineWidth = vobj.LineWidth elif prop == "FontName": - if hasattr(vobj,"FontName") and hasattr(self,"fon"): + if hasattr(vobj, "FontName") and hasattr(self, "fon"): if vobj.FontName: self.fon.name = vobj.FontName - elif prop in ["FontSize","DisplayOffset","OriginOffset"]: - if hasattr(vobj,"FontSize") and hasattr(vobj,"DisplayOffset") and hasattr(vobj,"OriginOffset") and hasattr(self,"fon"): + elif prop in ["FontSize", "DisplayOffset", "OriginOffset"]: + if ( + hasattr(vobj, "FontSize") + and hasattr(vobj, "DisplayOffset") + and hasattr(vobj, "OriginOffset") + and hasattr(self, "fon") + ): fs = vobj.FontSize.Value if fs: self.fon.size = fs b = vobj.DisplayOffset.Base - self.tra.translation.setValue([b.x+fs/8,b.y,b.z+fs/8]) + self.tra.translation.setValue([b.x + fs / 8, b.y, b.z + fs / 8]) r = vobj.DisplayOffset.Rotation self.tra.rotation.setValue(r.Q) if vobj.OriginOffset: - self.lco.point.setValues([[b.x-fs,b.y,b.z],[b.x+fs,b.y,b.z],[b.x,b.y-fs,b.z],[b.x,b.y+fs,b.z],[b.x,b.y,b.z-fs],[b.x,b.y,b.z+fs]]) + self.lco.point.setValues( + [ + [b.x - fs, b.y, b.z], + [b.x + fs, b.y, b.z], + [b.x, b.y - fs, b.z], + [b.x, b.y + fs, b.z], + [b.x, b.y, b.z - fs], + [b.x, b.y, b.z + fs], + ] + ) else: - self.lco.point.setValues([[-fs,0,0],[fs,0,0],[0,-fs,0],[0,fs,0],[0,0,-fs],[0,0,fs]]) - elif prop in ["OverrideUnit","ShowUnit","ShowLevel","ShowLabel"]: - if hasattr(vobj,"OverrideUnit") and hasattr(vobj,"ShowUnit") and hasattr(vobj,"ShowLevel") and hasattr(vobj,"ShowLabel") and hasattr(self,"txt"): + self.lco.point.setValues( + [ + [-fs, 0, 0], + [fs, 0, 0], + [0, -fs, 0], + [0, fs, 0], + [0, 0, -fs], + [0, 0, fs], + ] + ) + elif prop in ["OverrideUnit", "ShowUnit", "ShowLevel", "ShowLabel"]: + if ( + hasattr(vobj, "OverrideUnit") + and hasattr(vobj, "ShowUnit") + and hasattr(vobj, "ShowLevel") + and hasattr(vobj, "ShowLabel") + and hasattr(self, "txt") + ): offset = getattr(vobj.Object, "LevelOffset", 0) if hasattr(offset, "Value"): offset = offset.Value z = vobj.Object.Placement.Base.z + offset - q = FreeCAD.Units.Quantity(z,FreeCAD.Units.Length) + q = FreeCAD.Units.Quantity(z, FreeCAD.Units.Length) txt = "" if vobj.ShowLabel: txt += vobj.Object.Label @@ -691,74 +1006,90 @@ class ViewProviderBuildingPart: else: u = q.getUserPreferred()[2] try: - txt += units.display_external(float(q),None,'Length',vobj.ShowUnit,u) + txt += units.display_external(float(q), None, "Length", vobj.ShowUnit, u) except Exception: q = q.getValueAs(q.getUserPreferred()[2]) - d = params.get_param("Decimals",path="Units") - fmt = "{0:."+ str(d) + "f}" + d = params.get_param("Decimals", path="Units") + fmt = "{0:." + str(d) + "f}" if not vobj.ShowUnit: u = "" txt += fmt.format(float(q)) + str(u) if not txt: - txt = " " # empty texts make coin crash... - if isinstance(txt,unicode): + txt = " " # empty texts make coin crash... + if isinstance(txt, unicode): txt = txt.encode("utf8") self.txt.string.setValue(txt) - elif prop in ["ChildrenOverride","ChildenLineWidth","ChildrenLineColor","ChildrenShapeColor","ChildrenTransparency"]: - if hasattr(vobj,"ChildrenOverride") and vobj.ChildrenOverride: - props = ["ChildenLineWidth","ChildrenLineColor","ChildrenShapeColor","ChildrenTransparency"] + elif prop in [ + "ChildrenOverride", + "ChildenLineWidth", + "ChildrenLineColor", + "ChildrenShapeColor", + "ChildrenTransparency", + ]: + if hasattr(vobj, "ChildrenOverride") and vobj.ChildrenOverride: + props = [ + "ChildenLineWidth", + "ChildrenLineColor", + "ChildrenShapeColor", + "ChildrenTransparency", + ] for child in vobj.Object.Group: for prop in props: - if hasattr(vobj,prop) and hasattr(child.ViewObject,prop[8:]) and not hasattr(child,"ChildrenOverride"): - setattr(child.ViewObject,prop[8:],getattr(vobj,prop)) - elif prop in ["CutView","CutMargin"]: - if hasattr(vobj, "CutView") \ - and FreeCADGui.ActiveDocument.ActiveView \ - and hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph"): + if ( + hasattr(vobj, prop) + and hasattr(child.ViewObject, prop[8:]) + and not hasattr(child, "ChildrenOverride") + ): + setattr(child.ViewObject, prop[8:], getattr(vobj, prop)) + elif prop in ["CutView", "CutMargin"]: + if ( + hasattr(vobj, "CutView") + and FreeCADGui.ActiveDocument.ActiveView + and hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph") + ): sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if vobj.CutView: from pivy import coin + if self.clip: sg.removeChild(self.clip) self.clip = None - for o in Draft.get_group_contents(vobj.Object.Group, - walls=True): - if hasattr(o.ViewObject,"Lighting"): + for o in Draft.get_group_contents(vobj.Object.Group, walls=True): + if hasattr(o.ViewObject, "Lighting"): o.ViewObject.Lighting = "One side" self.clip = coin.SoClipPlane() self.clip.on.setValue(True) - norm = vobj.Object.Placement.multVec(FreeCAD.Vector(0,0,1)) + norm = vobj.Object.Placement.multVec(FreeCAD.Vector(0, 0, 1)) mp = vobj.Object.Placement.Base - mp = DraftVecUtils.project(mp,norm) - dist = mp.Length #- 0.1 # to not clip exactly on the section object + mp = DraftVecUtils.project(mp, norm) + dist = mp.Length # - 0.1 # to not clip exactly on the section object norm = norm.negative() marg = 1 - if hasattr(vobj,"CutMargin"): + if hasattr(vobj, "CutMargin"): marg = vobj.CutMargin.Value if mp.getAngle(norm) > 1: dist += marg dist = -dist else: dist -= marg - plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) + plane = coin.SbPlane(coin.SbVec3f(norm.x, norm.y, norm.z), dist) self.clip.plane.setValue(plane) - sg.insertChild(self.clip,0) + sg.insertChild(self.clip, 0) else: - if getattr(self,"clip",None): + if getattr(self, "clip", None): sg.removeChild(self.clip) self.clip = None - for o in Draft.get_group_contents(vobj.Object.Group, - walls=True): - if hasattr(o.ViewObject,"Lighting"): + for o in Draft.get_group_contents(vobj.Object.Group, walls=True): + if hasattr(o.ViewObject, "Lighting"): o.ViewObject.Lighting = "Two side" elif prop == "Visibility": # turn clipping off when turning the object off - if hasattr(vobj,"Visibility") and not(vobj.Visibility) and hasattr(vobj,"CutView"): + if hasattr(vobj, "Visibility") and not (vobj.Visibility) and hasattr(vobj, "CutView"): vobj.CutView = False elif prop == "SaveInventor": self.writeInventor(vobj.Object) - elif prop in ["AutogroupBox","AutogroupSize"]: - if hasattr(vobj,"AutogroupBox") and hasattr(vobj,"AutogroupSize"): + elif prop in ["AutogroupBox", "AutogroupSize"]: + if hasattr(vobj, "AutogroupBox") and hasattr(vobj, "AutogroupSize"): if vobj.AutogroupBox: if len(vobj.AutogroupSize) >= 6: self.autobbox = FreeCAD.BoundBox(*vobj.AutogroupSize[0:6]) @@ -769,19 +1100,22 @@ class ViewProviderBuildingPart: else: self.autobbox = None self.bbox.whichChild = -1 - elif prop in ["AutogroupAutosize","AutogroupMargin"]: - if hasattr(vobj,"AutogroupAutosize") and vobj.AutogroupAutosize: + elif prop in ["AutogroupAutosize", "AutogroupMargin"]: + if hasattr(vobj, "AutogroupAutosize") and vobj.AutogroupAutosize: bbox = vobj.Object.Shape.BoundBox bbox.enlarge(vobj.AutogroupMargin.Value) - vobj.AutogroupSize = [int(i) for i in [bbox.XMin,bbox.YMin,bbox.ZMin,bbox.XMax,bbox.YMax,bbox.ZMax]] + vobj.AutogroupSize = [ + int(i) + for i in [bbox.XMin, bbox.YMin, bbox.ZMin, bbox.XMax, bbox.YMax, bbox.ZMax] + ] - def onDelete(self,vobj,subelements): + def onDelete(self, vobj, subelements): if self.clip: sg.removeChild(self.clip) self.clip = None for o in Draft.get_group_contents(vobj.Object.Group, walls=True): - if hasattr(o.ViewObject,"Lighting"): + if hasattr(o.ViewObject, "Lighting"): o.ViewObject.Lighting = "Two side" return True @@ -796,7 +1130,7 @@ class ViewProviderBuildingPart: return None self.activate() - return False # Return `False` as we don't want to enter edit mode. + return False # Return `False` as we don't want to enter edit mode. def unsetEdit(self, vobj, mode): if mode == 1 or mode == 2: @@ -809,9 +1143,10 @@ class ViewProviderBuildingPart: def setupContextMenu(self, vobj, menu): from PySide import QtCore, QtGui import Draft_rc - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - if (not hasattr(vobj,"DoubleClickActivates")) or vobj.DoubleClickActivates: + if (not hasattr(vobj, "DoubleClickActivates")) or vobj.DoubleClickActivates: menuTxt = translate("Arch", "Active") actionActivate = QtGui.QAction(menuTxt, menu) actionActivate.setCheckable(True) @@ -822,48 +1157,42 @@ class ViewProviderBuildingPart: actionActivate.triggered.connect(lambda _: self.activate(actionActivate)) menu.addAction(actionActivate) - actionSetWorkingPlane = QtGui.QAction(QtGui.QIcon(":/icons/Draft_SelectPlane.svg"), - translate("Arch", "Set Working Plane"), - menu) - QtCore.QObject.connect(actionSetWorkingPlane, - QtCore.SIGNAL("triggered()"), - self.setWorkingPlane) + actionSetWorkingPlane = QtGui.QAction( + QtGui.QIcon(":/icons/Draft_SelectPlane.svg"), + translate("Arch", "Set Working Plane"), + menu, + ) + QtCore.QObject.connect( + actionSetWorkingPlane, QtCore.SIGNAL("triggered()"), self.setWorkingPlane + ) menu.addAction(actionSetWorkingPlane) - actionWriteCamera = QtGui.QAction(QtGui.QIcon(":/icons/Draft_SelectPlane.svg"), - translate("Arch", "Write Camera Position"), - menu) - QtCore.QObject.connect(actionWriteCamera, - QtCore.SIGNAL("triggered()"), - self.writeCamera) + actionWriteCamera = QtGui.QAction( + QtGui.QIcon(":/icons/Draft_SelectPlane.svg"), + translate("Arch", "Write Camera Position"), + menu, + ) + QtCore.QObject.connect(actionWriteCamera, QtCore.SIGNAL("triggered()"), self.writeCamera) menu.addAction(actionWriteCamera) - actionCreateGroup = QtGui.QAction(translate("Arch", "New Group"), - menu) - QtCore.QObject.connect(actionCreateGroup, - QtCore.SIGNAL("triggered()"), - self.createGroup) + actionCreateGroup = QtGui.QAction(translate("Arch", "New Group"), menu) + QtCore.QObject.connect(actionCreateGroup, QtCore.SIGNAL("triggered()"), self.createGroup) menu.addAction(actionCreateGroup) - actionReorder = QtGui.QAction(translate("Arch", "Reorder Children Alphabetically"), - menu) - QtCore.QObject.connect(actionReorder, - QtCore.SIGNAL("triggered()"), - self.reorder) + actionReorder = QtGui.QAction(translate("Arch", "Reorder Children Alphabetically"), menu) + QtCore.QObject.connect(actionReorder, QtCore.SIGNAL("triggered()"), self.reorder) menu.addAction(actionReorder) - actionCloneUp = QtGui.QAction(translate("Arch", "Clone Level Up"), - menu) - QtCore.QObject.connect(actionCloneUp, - QtCore.SIGNAL("triggered()"), - self.cloneUp) + actionCloneUp = QtGui.QAction(translate("Arch", "Clone Level Up"), menu) + QtCore.QObject.connect(actionCloneUp, QtCore.SIGNAL("triggered()"), self.cloneUp) menu.addAction(actionCloneUp) def activate(self, action=None): from draftutils.utils import toggle_working_plane + vobj = self.Object.ViewObject - if (not hasattr(vobj,"DoubleClickActivates")) or vobj.DoubleClickActivates: + if (not hasattr(vobj, "DoubleClickActivates")) or vobj.DoubleClickActivates: if toggle_working_plane(self.Object, action, restore=True): print("Setting active working plane to: ", self.Object.Label) else: @@ -871,13 +1200,14 @@ class ViewProviderBuildingPart: FreeCADGui.Selection.clearSelection() - def setWorkingPlane(self,restore=False): + def setWorkingPlane(self, restore=False): vobj = self.Object.ViewObject import WorkingPlane + wp = WorkingPlane.get_working_plane(update=False) autoclip = False - if hasattr(vobj,"AutoCutView"): + if hasattr(vobj, "AutoCutView"): autoclip = vobj.AutoCutView if restore: if wp.label.rstrip("*") == self.Object.Label: @@ -908,34 +1238,40 @@ class ViewProviderBuildingPart: def writeCamera(self): - if hasattr(self,"Object"): + if hasattr(self, "Object"): from pivy import coin + n = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() - FreeCAD.Console.PrintMessage(QT_TRANSLATE_NOOP("Draft","Writing camera position")+"\n") + FreeCAD.Console.PrintMessage( + QT_TRANSLATE_NOOP("Draft", "Writing camera position") + "\n" + ) cdata = list(n.position.getValue().getValue()) cdata.extend(list(n.orientation.getValue().getValue())) cdata.append(n.nearDistance.getValue()) cdata.append(n.farDistance.getValue()) cdata.append(n.aspectRatio.getValue()) cdata.append(n.focalDistance.getValue()) - if isinstance(n,coin.SoOrthographicCamera): + if isinstance(n, coin.SoOrthographicCamera): cdata.append(n.height.getValue()) - cdata.append(0.0) # orthograhic camera - elif isinstance(n,coin.SoPerspectiveCamera): + cdata.append(0.0) # orthograhic camera + elif isinstance(n, coin.SoPerspectiveCamera): cdata.append(n.heightAngle.getValue()) - cdata.append(1.0) # perspective camera + cdata.append(1.0) # perspective camera self.Object.ViewObject.ViewData = cdata def createGroup(self): - if hasattr(self,"Object"): - s = "FreeCAD.ActiveDocument.getObject(\"%s\").newObject(\"App::DocumentObjectGroup\",\"Group\")" % self.Object.Name + if hasattr(self, "Object"): + s = ( + 'FreeCAD.ActiveDocument.getObject("%s").newObject("App::DocumentObjectGroup","Group")' + % self.Object.Name + ) FreeCADGui.doCommand(s) def reorder(self): - if hasattr(self,"Object"): - if hasattr(self.Object,"Group") and self.Object.Group: + if hasattr(self, "Object"): + if hasattr(self.Object, "Group") and self.Object.Group: g = self.Object.Group g.sort(key=lambda obj: obj.Label) self.Object.Group = g @@ -943,66 +1279,87 @@ class ViewProviderBuildingPart: def cloneUp(self): - if hasattr(self,"Object"): + if hasattr(self, "Object"): if not self.Object.Height.Value: - FreeCAD.Console.PrintError("This level has no height value. Define a height before using this function.\n") + FreeCAD.Console.PrintError( + "This level has no height value. Define a height before using this function.\n" + ) return height = self.Object.Height.Value ng = [] - if hasattr(self.Object,"Group") and self.Object.Group: + if hasattr(self.Object, "Group") and self.Object.Group: for o in self.Object.Group: no = Draft.clone(o) - Draft.move(no,FreeCAD.Vector(0,0,height)) + Draft.move(no, FreeCAD.Vector(0, 0, height)) ng.append(no) nobj = Arch.makeBuildingPart() - Draft.formatObject(nobj,self.Object) + Draft.formatObject(nobj, self.Object) nobj.Placement = self.Object.Placement - nobj.Placement.move(FreeCAD.Vector(0,0,height)) + nobj.Placement.move(FreeCAD.Vector(0, 0, height)) nobj.IfcType = self.Object.IfcType nobj.Height = height nobj.Label = self.Object.Label nobj.Group = ng for parent in self.Object.InList: - if hasattr(parent,"Group") and hasattr(parent,"addObject") and (self.Object in parent.Group): + if ( + hasattr(parent, "Group") + and hasattr(parent, "addObject") + and (self.Object in parent.Group) + ): parent.addObject(nobj) FreeCAD.ActiveDocument.recompute() # fix for missing IFC attributes for no in ng: - if hasattr(no,"LongName") and hasattr(no,"CloneOf") and no.CloneOf and hasattr(no.CloneOf,"LongName"): + if ( + hasattr(no, "LongName") + and hasattr(no, "CloneOf") + and no.CloneOf + and hasattr(no.CloneOf, "LongName") + ): no.LongName = no.CloneOf.LongName FreeCAD.ActiveDocument.recompute() def dumps(self): return None - def loads(self,state): + def loads(self, state): return None - def writeInventor(self,obj): + def writeInventor(self, obj): def callback(match): return next(callback.v) - if hasattr(obj.ViewObject,"SaveInventor") and obj.ViewObject.SaveInventor: - if obj.Shape and obj.Shape.Faces and hasattr(obj,"SavedInventor"): + if hasattr(obj.ViewObject, "SaveInventor") and obj.ViewObject.SaveInventor: + if obj.Shape and obj.Shape.Faces and hasattr(obj, "SavedInventor"): colors = obj.ViewObject.DiffuseColor if len(colors) != len(obj.Shape.Faces): - print("Debug: Colors mismatch in",obj.Label) + print("Debug: Colors mismatch in", obj.Label) colors = None iv = self.Object.Shape.writeInventor() import re + if colors: - if len(re.findall(r"IndexedFaceSet",iv)) == len(obj.Shape.Faces): + if len(re.findall(r"IndexedFaceSet", iv)) == len(obj.Shape.Faces): # convert colors to iv representations - colors = ["Material { diffuseColor "+str(color[0])+" "+str(color[1])+" "+str(color[2])+"}\n IndexedFaceSet" for color in colors] + colors = [ + "Material { diffuseColor " + + str(color[0]) + + " " + + str(color[1]) + + " " + + str(color[2]) + + "}\n IndexedFaceSet" + for color in colors + ] # replace - callback.v=iter(colors) - iv = re.sub(r"IndexedFaceSet",callback,iv) + callback.v = iter(colors) + iv = re.sub(r"IndexedFaceSet", callback, iv) else: - print("Debug: IndexedFaceSet mismatch in",obj.Label) + print("Debug: IndexedFaceSet mismatch in", obj.Label) # save embedded file - tf = tempfile.mkstemp(prefix=obj.Name,suffix=".iv")[1] - f = open(tf,"w") + tf = tempfile.mkstemp(prefix=obj.Name, suffix=".iv")[1] + f = open(tf, "w") f.write(iv) f.close() obj.SavedInventor = tf diff --git a/src/Mod/BIM/ArchCommands.py b/src/Mod/BIM/ArchCommands.py index 2576de135b..4bd2a12497 100644 --- a/src/Mod/BIM/ArchCommands.py +++ b/src/Mod/BIM/ArchCommands.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Commands" +__title__ = "FreeCAD Arch Commands" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchCommands # \ingroup ARCH @@ -43,21 +43,23 @@ from draftutils import params from draftutils.groups import is_group if FreeCAD.GuiUp: - from PySide import QtGui,QtCore + from PySide import QtGui, QtCore import FreeCADGui from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt + # \endcond # module functions ############################################### + def getStringList(objects): - '''getStringList(objects): returns a string defining a list - of objects''' + """getStringList(objects): returns a string defining a list + of objects""" result = "[" for o in objects: if len(result) > 1: @@ -66,9 +68,10 @@ def getStringList(objects): result += "]" return result + def getDefaultColor(objectType): - '''getDefaultColor(string): returns a color value for the given object - type (Wall, Structure, Window, WindowGlass)''' + """getDefaultColor(string): returns a color value for the given object + type (Wall, Structure, Window, WindowGlass)""" alpha = 1.0 if objectType == "Wall": c = params.get_param_arch("WallColor") @@ -93,33 +96,48 @@ def getDefaultColor(objectType): r, g, b, _ = Draft.get_rgba_tuple(c) return (r, g, b, alpha) -def _usedForAttachment(host,obj): - if not getattr(obj,"AttachmentSupport",[]): + +def _usedForAttachment(host, obj): + if not getattr(obj, "AttachmentSupport", []): return False for sub in obj.AttachmentSupport: if sub[0] == host: return True return False -def addComponents(objectsList,host): - '''addComponents(objectsList,hostObject): adds the given object or the objects + +def addComponents(objectsList, host): + """addComponents(objectsList,hostObject): adds the given object or the objects from the given list as components to the given host Object. Use this for - example to add windows to a wall, or to add walls to a cell or floor.''' - if not isinstance(objectsList,list): + example to add windows to a wall, or to add walls to a cell or floor.""" + if not isinstance(objectsList, list): objectsList = [objectsList] hostType = Draft.getType(host) - if hostType in ["Floor","Building","Site","Project","BuildingPart"]: + if hostType in ["Floor", "Building", "Site", "Project", "BuildingPart"]: for o in objectsList: host.addObject(o) - elif hostType in ["Wall","CurtainWall","Structure","Precast","Window","Roof","Stairs","StructuralSystem","Panel","Component","Pipe"]: + elif hostType in [ + "Wall", + "CurtainWall", + "Structure", + "Precast", + "Window", + "Roof", + "Stairs", + "StructuralSystem", + "Panel", + "Component", + "Pipe", + ]: import DraftGeomUtils + outList = host.OutListRecursive a = host.Additions - x = getattr(host,"Axes",[]) + x = getattr(host, "Axes", []) for o in objectsList: - if hasattr(o,"Shape"): + if hasattr(o, "Shape"): if Draft.getType(o) == "Window": - if hasattr(o,"Hosts"): + if hasattr(o, "Hosts"): if not host in o.Hosts: g = o.Hosts g.append(host) @@ -127,23 +145,23 @@ def addComponents(objectsList,host): elif o in outList: FreeCAD.Console.PrintWarning( translate( - "Arch", - "Cannot add {0} as it is already referenced by {1}." - ).format(o.Label, host.Label) + "\n" + "Arch", "Cannot add {0} as it is already referenced by {1}." + ).format(o.Label, host.Label) + + "\n" ) elif Draft.getType(o) == "Axis": if not o in x: x.append(o) - elif DraftGeomUtils.isValidPath(o.Shape) and (hostType in ["Structure","Precast"]): - if _usedForAttachment(host,o): + elif DraftGeomUtils.isValidPath(o.Shape) and (hostType in ["Structure", "Precast"]): + if _usedForAttachment(host, o): o.AttachmentSupport = None o.MapMode = "Deactivated" host.Tool = o elif not o in a: - if hasattr(o,"Shape"): + if hasattr(o, "Shape"): a.append(o) host.Additions = a - if hasattr(host,"Axes"): + if hasattr(host, "Axes"): host.Axes = x elif hostType in ["SectionPlane"]: a = host.Objects @@ -155,18 +173,31 @@ def addComponents(objectsList,host): for o in objectsList: host.addObject(o) -def removeComponents(objectsList,host=None): - '''removeComponents(objectsList,[hostObject]): removes the given component or + +def removeComponents(objectsList, host=None): + """removeComponents(objectsList,[hostObject]): removes the given component or the components from the given list from their parents. If a host object is specified, this function will try adding the components as holes to the host - object instead.''' - if not isinstance(objectsList,list): + object instead.""" + if not isinstance(objectsList, list): objectsList = [objectsList] if host: - if Draft.getType(host) in ["Wall","CurtainWall","Structure","Precast","Window","Roof","Stairs","StructuralSystem","Panel","Component","Pipe"]: - if getattr(host,"Tool",None) in objectsList: + if Draft.getType(host) in [ + "Wall", + "CurtainWall", + "Structure", + "Precast", + "Window", + "Roof", + "Stairs", + "StructuralSystem", + "Panel", + "Component", + "Pipe", + ]: + if getattr(host, "Tool", None) in objectsList: host.Tool = None - if hasattr(host,"Axes"): + if hasattr(host, "Axes"): a = host.Axes for o in objectsList[:]: if o in a: @@ -176,7 +207,7 @@ def removeComponents(objectsList,host=None): s = host.Subtractions for o in objectsList: if Draft.getType(o) == "Window": - if hasattr(o,"Hosts"): + if hasattr(o, "Hosts"): if not host in o.Hosts: g = o.Hosts g.append(host) @@ -187,18 +218,19 @@ def removeComponents(objectsList,host=None): if Draft.getType(o) != "Roof": setAsSubcomponent(o) # Avoid cyclic dependency via Attachment Support: - if hasattr(o,"Base") and o.Base: + if hasattr(o, "Base") and o.Base: objList = [o, o.Base] else: objList = [o] for i in objList: - if _usedForAttachment(host,i): + if _usedForAttachment(host, i): FreeCAD.Console.PrintMessage( translate( "Arch", - "{0} is mapped to {1}, removing the former's " + - "Attachment Support to avoid cyclic dependency." - ).format(o.Label, host.Label) + "\n" + "{0} is mapped to {1}, removing the former's " + + "Attachment Support to avoid cyclic dependency.", + ).format(o.Label, host.Label) + + "\n" ) i.AttachmentSupport = None i.MapMode = "Deactivated" @@ -214,13 +246,13 @@ def removeComponents(objectsList,host=None): if o.InList: h = o.InList[0] tp = Draft.getType(h) - if tp in ["Floor","Building","Site","BuildingPart"]: + if tp in ["Floor", "Building", "Site", "BuildingPart"]: c = h.Group if o in c: c.remove(o) h.Group = c o.ViewObject.show() - elif tp in ["Wall","Structure","Precast"]: + elif tp in ["Wall", "Structure", "Precast"]: a = h.Additions s = h.Subtractions if o in a: @@ -242,20 +274,22 @@ def removeComponents(objectsList,host=None): if hasattr(o, "Hosts") and Draft.getType(o) == "Window": o.Hosts = [] -def makeComponent(baseobj=None,name=None,delete=False): - '''makeComponent([baseobj],[name],[delete]): creates an undefined, non-parametric BIM - component from the given base object''' + +def makeComponent(baseobj=None, name=None, delete=False): + """makeComponent([baseobj],[name],[delete]): creates an undefined, non-parametric BIM + component from the given base object""" if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Component") - obj.Label = name if name else translate("Arch","Component") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Component") + obj.Label = name if name else translate("Arch", "Component") ArchComponent.Component(obj) if FreeCAD.GuiUp: ArchComponent.ViewProviderComponent(obj.ViewObject) if baseobj: import Part - if hasattr(baseobj,'Shape'): + + if hasattr(baseobj, "Shape"): obj.Shape = baseobj.Shape obj.Placement = baseobj.Placement if delete: @@ -264,61 +298,66 @@ def makeComponent(baseobj=None,name=None,delete=False): obj.Base = baseobj if FreeCAD.GuiUp: baseobj.ViewObject.hide() - elif isinstance(baseobj,Part.Shape): + elif isinstance(baseobj, Part.Shape): obj.Shape = baseobj Draft.select(obj) return obj + def cloneComponent(obj): - '''cloneComponent(obj): Creates a clone of an object as an undefined component''' + """cloneComponent(obj): Creates a clone of an object as an undefined component""" c = makeComponent() c.CloneOf = obj c.Placement = obj.Placement c.Label = obj.Label - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: c.Material = obj.Material - if hasattr(obj,"IfcAttributes"): + if hasattr(obj, "IfcAttributes"): if obj.IfcAttributes: c.IfcAttributes = obj.IfcAttributes Draft.select(c) return c + def setAsSubcomponent(obj): - '''Sets the given object properly to become a subcomponent (addition, subtraction) - of an Arch component''' + """Sets the given object properly to become a subcomponent (addition, subtraction) + of an Arch component""" Draft.ungroup(obj) if params.get_param_arch("applyConstructionStyle"): if FreeCAD.GuiUp: color = getDefaultColor("Construction") - if hasattr(obj.ViewObject,"LineColor"): + if hasattr(obj.ViewObject, "LineColor"): obj.ViewObject.LineColor = color if hasattr(obj.ViewObject, "PointColor"): obj.ViewObject.PointColor = color - if hasattr(obj.ViewObject,"ShapeColor"): + if hasattr(obj.ViewObject, "ShapeColor"): obj.ViewObject.ShapeColor = color - if hasattr(obj.ViewObject,"Transparency"): - obj.ViewObject.Transparency = int(color[3]*100) + if hasattr(obj.ViewObject, "Transparency"): + obj.ViewObject.Transparency = int(color[3] * 100) obj.ViewObject.hide() -def copyProperties(obj1,obj2): - '''copyProperties(obj1,obj2): Copies properties values from obj1 to obj2, - when that property exists in both objects''' + +def copyProperties(obj1, obj2): + """copyProperties(obj1,obj2): Copies properties values from obj1 to obj2, + when that property exists in both objects""" for prop in obj1.PropertiesList: if prop in obj2.PropertiesList: - if not prop in ["Proxy","Shape"]: - setattr(obj2,prop,getattr(obj1,prop)) + if not prop in ["Proxy", "Shape"]: + setattr(obj2, prop, getattr(obj1, prop)) if obj1.ViewObject and obj2.ViewObject: for prop in obj1.ViewObject.PropertiesList: if prop in obj2.ViewObject.PropertiesList: - if not prop in ["Proxy","Shape"]: - setattr(obj2.ViewObject,prop,getattr(obj1.ViewObject,prop)) + if not prop in ["Proxy", "Shape"]: + setattr(obj2.ViewObject, prop, getattr(obj1.ViewObject, prop)) -def splitMesh(obj,mark=True): - '''splitMesh(object,[mark]): splits the given mesh object into separated components. + +def splitMesh(obj, mark=True): + """splitMesh(object,[mark]): splits the given mesh object into separated components. If mark is False, nothing else is done. If True (default), non-manifold components - will be painted in red.''' - if not obj.isDerivedFrom("Mesh::Feature"): return [] + will be painted in red.""" + if not obj.isDerivedFrom("Mesh::Feature"): + return [] basemesh = obj.Mesh comps = basemesh.getSeparateComponents() nlist = [] @@ -326,46 +365,47 @@ def splitMesh(obj,mark=True): basename = obj.Name FreeCAD.ActiveDocument.removeObject(basename) for c in comps: - newobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",basename) + newobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature", basename) newobj.Mesh = c - if mark and (not(c.isSolid()) or c.hasNonManifolds()): - newobj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) + if mark and (not (c.isSolid()) or c.hasNonManifolds()): + newobj.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) nlist.append(newobj) return nlist return [obj] -def makeFace(wires,method=2,cleanup=False): - '''makeFace(wires): makes a face from a list of wires, finding which ones are holes''' - #print("makeFace: start:", wires) + +def makeFace(wires, method=2, cleanup=False): + """makeFace(wires): makes a face from a list of wires, finding which ones are holes""" + # print("makeFace: start:", wires) import Part - if not isinstance(wires,list): + if not isinstance(wires, list): if len(wires.Vertexes) < 3: raise return Part.Face(wires) elif len(wires) == 1: - #import Draft;Draft.printShape(wires[0]) + # import Draft;Draft.printShape(wires[0]) if len(wires[0].Vertexes) < 3: raise return Part.Face(wires[0]) wires = wires[:] - #print("makeFace: inner wires found") + # print("makeFace: inner wires found") ext = None max_length = 0 # cleaning up rubbish in wires if cleanup: for i in range(len(wires)): wires[i] = DraftGeomUtils.removeInterVertices(wires[i]) - #print("makeFace: garbage removed") + # print("makeFace: garbage removed") for w in wires: # we assume that the exterior boundary is that one with # the biggest bounding box if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength ext = w - #print("makeFace: exterior wire", ext) + # print("makeFace: exterior wire", ext) wires.remove(ext) if method == 1: @@ -373,28 +413,30 @@ def makeFace(wires,method=2,cleanup=False): # all interior wires mark a hole and must reverse # their orientation, otherwise Part.Face fails for w in wires: - #print("makeFace: reversing", w) + # print("makeFace: reversing", w) w.reverse() # make sure that the exterior wires comes as first in the list wires.insert(0, ext) - #print("makeFace: done sorting", wires) + # print("makeFace: done sorting", wires) if wires: return Part.Face(wires) else: # method 2: use the cut method mf = Part.Face(ext) - #print("makeFace: external face:", mf) + # print("makeFace: external face:", mf) for w in wires: f = Part.Face(w) - #print("makeFace: internal face:", f) + # print("makeFace: internal face:", f) mf = mf.cut(f) - #print("makeFace: final face:", mf.Faces) + # print("makeFace: final face:", mf.Faces) return mf.Faces[0] + def closeHole(shape): - '''closeHole(shape): closes a hole in an open shape''' + """closeHole(shape): closes a hole in an open shape""" import DraftGeomUtils import Part + # creating an edges lookup table lut = {} for face in shape.Faces: @@ -412,24 +454,26 @@ def closeHole(shape): bound = Part.__sortEdges__(bound) try: nface = Part.Face(Part.Wire(bound)) - shell = Part.makeShell(shape.Faces+[nface]) + shell = Part.makeShell(shape.Faces + [nface]) solid = Part.Solid(shell) except Part.OCCError: raise else: return solid -def getCutVolume(cutplane,shapes,clip=False,depth=None): + +def getCutVolume(cutplane, shapes, clip=False, depth=None): """getCutVolume(cutplane,shapes,[clip,depth]): returns a cut face and a cut volume from the given shapes and the given cutting plane. If clip is True, the cutvolume will also cut off everything outside the cutplane projection. If depth is non-zero, geometry further than this distance will be clipped off""" if not shapes: - return None,None,None + return None, None, None if not cutplane.Faces: - return None,None,None + return None, None, None import Part - if not isinstance(shapes,list): + + if not isinstance(shapes, list): shapes = [shapes] # building boundbox bb = shapes[0].BoundBox @@ -439,49 +483,51 @@ def getCutVolume(cutplane,shapes,clip=False,depth=None): # building cutplane space um = vm = wm = 0 try: - if hasattr(cutplane,"Shape"): + if hasattr(cutplane, "Shape"): p = cutplane.Shape.copy().Faces[0] else: p = cutplane.copy().Faces[0] except Part.OCCError: - FreeCAD.Console.PrintMessage(translate("Arch","Invalid cut plane")+"\n") - return None,None,None + FreeCAD.Console.PrintMessage(translate("Arch", "Invalid cut plane") + "\n") + return None, None, None ce = p.CenterOfMass - ax = p.normalAt(0,0) - prm_range = p.ParameterRange # (uMin, uMax, vMin, vMax) + ax = p.normalAt(0, 0) + prm_range = p.ParameterRange # (uMin, uMax, vMin, vMax) u = p.valueAt(prm_range[0], 0).sub(p.valueAt(prm_range[1], 0)).normalize() v = u.cross(ax) - if not bb.isCutPlane(ce,ax): - #FreeCAD.Console.PrintMessage(translate("Arch","No objects are cut by the plane)+"\n") - return None,None,None + if not bb.isCutPlane(ce, ax): + # FreeCAD.Console.PrintMessage(translate("Arch","No objects are cut by the plane)+"\n") + return None, None, None else: - corners = [FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin), - FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMin), - FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMin), - FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMin), - FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMax), - FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMax), - FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMax), - FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMax)] + corners = [ + FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), + FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), + FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), + FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), + FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), + FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), + FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), + FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax), + ] for c in corners: dv = c.sub(ce) - um1 = DraftVecUtils.project(dv,u).Length - um = max(um,um1) - vm1 = DraftVecUtils.project(dv,v).Length - vm = max(vm,vm1) - wm1 = DraftVecUtils.project(dv,ax).Length - wm = max(wm,wm1) - vu = DraftVecUtils.scaleTo(u,um) + um1 = DraftVecUtils.project(dv, u).Length + um = max(um, um1) + vm1 = DraftVecUtils.project(dv, v).Length + vm = max(vm, vm1) + wm1 = DraftVecUtils.project(dv, ax).Length + wm = max(wm, wm1) + vu = DraftVecUtils.scaleTo(u, um) vui = vu.negative() - vv = DraftVecUtils.scaleTo(v,vm) + vv = DraftVecUtils.scaleTo(v, vm) vvi = vv.negative() p1 = ce.add(vu.add(vvi)) p2 = ce.add(vu.add(vv)) p3 = ce.add(vui.add(vv)) p4 = ce.add(vui.add(vvi)) - cutface = Part.makePolygon([p1,p2,p3,p4,p1]) + cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) - cutnormal = DraftVecUtils.scaleTo(ax,wm) + cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = cutnormal.negative() invcutvolume = cutface.extrude(cutnormal) @@ -493,30 +539,32 @@ def getCutVolume(cutplane,shapes,clip=False,depth=None): invcutvolume = extrudedplane cutface = p if depth: - depthnormal = DraftVecUtils.scaleTo(cutnormal,depth) + depthnormal = DraftVecUtils.scaleTo(cutnormal, depth) depthvolume = cutface.extrude(depthnormal) depthclipvolume = invcutvolume.cut(depthvolume) cutvolume = cutvolume.fuse(depthclipvolume) cutvolume = cutvolume.removeSplitter() - return cutface,cutvolume,invcutvolume + return cutface, cutvolume, invcutvolume -def getShapeFromMesh(mesh,fast=True,tolerance=0.001,flat=False,cut=True): + +def getShapeFromMesh(mesh, fast=True, tolerance=0.001, flat=False, cut=True): import Part import MeshPart import DraftGeomUtils + if mesh.isSolid() and (mesh.countComponents() == 1) and fast: # use the best method faces = [] for f in mesh.Facets: - p=f.Points+[f.Points[0]] + p = f.Points + [f.Points[0]] pts = [] for pp in p: - pts.append(FreeCAD.Vector(pp[0],pp[1],pp[2])) + pts.append(FreeCAD.Vector(pp[0], pp[1], pp[2])) try: f = Part.Face(Part.makePolygon(pts)) except Exception: print("getShapeFromMesh: error building face from polygon") - #pass + # pass else: faces.append(f) shell = Part.makeShell(faces) @@ -529,14 +577,14 @@ def getShapeFromMesh(mesh,fast=True,tolerance=0.001,flat=False,cut=True): solid = solid.removeSplitter() except Part.OCCError: print("getShapeFromMesh: error removing splitter") - #pass + # pass return solid - #if not mesh.isSolid(): + # if not mesh.isSolid(): # print "getShapeFromMesh: non-solid mesh, using slow method" faces = [] segments = mesh.getPlanarSegments(tolerance) - #print(len(segments)) + # print(len(segments)) for i in segments: if len(i) > 0: wires = MeshPart.wireFromSegment(mesh, i) @@ -547,7 +595,7 @@ def getShapeFromMesh(mesh,fast=True,tolerance=0.001,flat=False,cut=True): nwires.append(DraftGeomUtils.flattenWire(w)) wires = nwires try: - faces.append(makeFace(wires,method=int(cut)+1)) + faces.append(makeFace(wires, method=int(cut) + 1)) except Exception: return None try: @@ -576,14 +624,15 @@ def getShapeFromMesh(mesh,fast=True,tolerance=0.001,flat=False,cut=True): else: return se -def projectToVector(shape,vector): - '''projectToVector(shape,vector): projects the given shape on the given - vector''' + +def projectToVector(shape, vector): + """projectToVector(shape,vector): projects the given shape on the given + vector""" projpoints = [] minl = 10000000000 maxl = -10000000000 for v in shape.Vertexes: - p = DraftVecUtils.project(v.Point,vector) + p = DraftVecUtils.project(v.Point, vector) projpoints.append(p) l = p.Length if p.getAngle(vector) > 1: @@ -592,51 +641,57 @@ def projectToVector(shape,vector): maxl = l if l < minl: minl = l - return DraftVecUtils.scaleTo(vector,maxl-minl) + return DraftVecUtils.scaleTo(vector, maxl - minl) -def meshToShape(obj,mark=True,fast=True,tol=0.001,flat=False,cut=True): - '''meshToShape(object,[mark,fast,tol,flat,cut]): turns a mesh into a shape, joining coplanar facets. If + +def meshToShape(obj, mark=True, fast=True, tol=0.001, flat=False, cut=True): + """meshToShape(object,[mark,fast,tol,flat,cut]): turns a mesh into a shape, joining coplanar facets. If mark is True (default), non-solid objects will be marked in red. Fast uses a faster algorithm by building a shell from the facets then removing splitter, tol is the tolerance used when converting mesh segments to wires, flat will force the wires to be perfectly planar, to be sure they can be turned into faces, but this might leave gaps in the final shell. If cut is true, holes in faces are - made by subtraction (default)''' + made by subtraction (default)""" name = obj.Name if "Mesh" in obj.PropertiesList: mesh = obj.Mesh - #plac = obj.Placement - solid = getShapeFromMesh(mesh,fast,tol,flat,cut) + # plac = obj.Placement + solid = getShapeFromMesh(mesh, fast, tol, flat, cut) if solid: if solid.isClosed() and solid.isValid(): FreeCAD.ActiveDocument.removeObject(name) - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature", name) newobj.Shape = solid - #newobj.Placement = plac #the placement is already computed in the mesh + # newobj.Placement = plac #the placement is already computed in the mesh if (not solid.isClosed()) or (not solid.isValid()): if mark: - newobj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) + newobj.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) return newobj return None -def removeCurves(shape,dae=False,tolerance=5): - '''removeCurves(shape,dae,tolerance=5): replaces curved faces in a shape - with faceted segments. If dae is True, DAE triangulation options are used''' + +def removeCurves(shape, dae=False, tolerance=5): + """removeCurves(shape,dae,tolerance=5): replaces curved faces in a shape + with faceted segments. If dae is True, DAE triangulation options are used""" import Mesh + if dae: from importers import importDAE + t = importDAE.triangulate(shape.cleaned()) else: t = shape.cleaned().tessellate(tolerance) m = Mesh.Mesh(t) return getShapeFromMesh(m) -def removeShape(objs,mark=True): - '''removeShape(objs,mark=True): takes an arch object (wall or structure) built on a cubic shape, and removes + +def removeShape(objs, mark=True): + """removeShape(objs,mark=True): takes an arch object (wall or structure) built on a cubic shape, and removes the inner shape, keeping its length, width and height as parameters. If mark is True, objects that cannot - be processed by this function will become red.''' + be processed by this function will become red.""" import DraftGeomUtils - if not isinstance(objs,list): + + if not isinstance(objs, list): objs = [objs] for obj in objs: if DraftGeomUtils.isCubic(obj.Shape): @@ -648,35 +703,40 @@ def removeShape(objs,mark=True): if tp == "Structure": FreeCAD.ActiveDocument.removeObject(name) import ArchStructure - str = ArchStructure.makeStructure(length=dims[1],width=dims[2],height=dims[3],name=name) + + str = ArchStructure.makeStructure( + length=dims[1], width=dims[2], height=dims[3], name=name + ) str.Placement = dims[0] elif tp == "Wall": FreeCAD.ActiveDocument.removeObject(name) import ArchWall + length = dims[1] width = dims[2] - v1 = Vector(length/2,0,0) + v1 = Vector(length / 2, 0, 0) v2 = v1.negative() v1 = dims[0].multVec(v1) v2 = dims[0].multVec(v2) - line = Draft.makeLine(v1,v2) - ArchWall.makeWall(line,width=width,height=dims[3],name=name) + line = Draft.makeLine(v1, v2) + ArchWall.makeWall(line, width=width, height=dims[3], name=name) else: if mark: - obj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) + obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) + def mergeCells(objectslist): - '''mergeCells(objectslist): merges the objects in the given list + """mergeCells(objectslist): merges the objects in the given list into one. All objects must be of the same type and based on the Cell - object (cells, floors, buildings, or sites).''' + object (cells, floors, buildings, or sites).""" if not objectslist: return None - if not isinstance(objectslist,list): + if not isinstance(objectslist, list): return None if len(objectslist) < 2: return None typ = Draft.getType(objectslist[0]) - if not(typ in ["Cell","Floor","Building","Site"]): + if not (typ in ["Cell", "Floor", "Building", "Site"]): return None for o in objectslist: if Draft.getType(o) != typ: @@ -692,25 +752,27 @@ def mergeCells(objectslist): FreeCAD.ActiveDocument.recompute() return base -def download(url,force=False): - '''download(url,force=False): downloads a file from the given URL and saves it in the + +def download(url, force=False): + """download(url,force=False): downloads a file from the given URL and saves it in the macro path. Returns the path to the saved file. If force is True, the file will be - downloaded again evn if it already exists.''' + downloaded again evn if it already exists.""" try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen import os - name = url.split('/')[-1] + + name = url.split("/")[-1] macropath = FreeCAD.getUserMacroDir(True) - filepath = os.path.join(macropath,name) - if os.path.exists(filepath) and not(force): + filepath = os.path.join(macropath, name) + if os.path.exists(filepath) and not (force): return filepath try: - FreeCAD.Console.PrintMessage("Downloading "+url+" …\n") + FreeCAD.Console.PrintMessage("Downloading " + url + " …\n") response = urlopen(url) s = response.read() - f = open(filepath,'wb') + f = open(filepath, "wb") f.write(s) f.close() except Exception: @@ -718,38 +780,44 @@ def download(url,force=False): else: return filepath -def check(objectslist,includehidden=False): + +def check(objectslist, includehidden=False): """check(objectslist,includehidden=False): checks if the given objects contain only solids""" objs = Draft.get_group_contents(objectslist) if not includehidden: objs = Draft.removeHidden(objs) bad = [] for o in objs: - if not hasattr(o,'Shape'): - bad.append([o,"is not a Part-based object"]) + if not hasattr(o, "Shape"): + bad.append([o, "is not a Part-based object"]) else: s = o.Shape if (not s.isClosed()) and (not (Draft.getType(o) == "Axis")): - bad.append([o,translate("Arch","is not closed")]) + bad.append([o, translate("Arch", "is not closed")]) elif not s.isValid(): - bad.append([o,translate("Arch","is not valid")]) + bad.append([o, translate("Arch", "is not valid")]) elif (not s.Solids) and (not (Draft.getType(o) == "Axis")): - bad.append([o,translate("Arch","does not contain any solid")]) + bad.append([o, translate("Arch", "does not contain any solid")]) else: f = 0 for sol in s.Solids: f += len(sol.Faces) if not sol.isClosed(): - bad.append([o,translate("Arch","contains a non-closed solid")]) + bad.append([o, translate("Arch", "contains a non-closed solid")]) if len(s.Faces) != f: - bad.append([o,translate("Arch","contains faces that are not part of any solid")]) + bad.append( + [o, translate("Arch", "contains faces that are not part of any solid")] + ) return bad -def getHost(obj,strict=True): + +def getHost(obj, strict=True): """getHost(obj,[strict]): returns the host of the current object. If strict is true (default), the host can only be an object of a higher level than the given one, or in other words, if a wall - is contained in another wall which is part of a floor, the floor is returned instead of the parent wall""" + is contained in another wall which is part of a floor, the floor is returned instead of the parent wall + """ import Draft + t = Draft.getType(obj) for par in obj.InList: if par.isDerivedFrom("Part::Feature") or par.isDerivedFrom("App::DocumentObjectGroup"): @@ -757,21 +825,23 @@ def getHost(obj,strict=True): if Draft.getType(par) != t: return par else: - return getHost(par,strict) + return getHost(par, strict) else: return par return None -def pruneIncluded(objectslist,strict=False,silent=False): + +def pruneIncluded(objectslist, strict=False, silent=False): """pruneIncluded(objectslist,[strict]): removes from a list of Arch objects, those that are subcomponents of another shape-based object, leaving only the top-level shapes. If strict is True, the object is removed only if the parent is also part of the selection.""" import Draft + newlist = [] for obj in objectslist: toplevel = True if obj.isDerivedFrom("Part::Feature"): - if Draft.getType(obj) not in ["Window","Clone","Pipe","Rebar","Roof"]: + if Draft.getType(obj) not in ["Window", "Clone", "Pipe", "Rebar", "Roof"]: for parent in obj.InList: if not parent.isDerivedFrom("Part::Feature"): pass @@ -779,9 +849,17 @@ def pruneIncluded(objectslist,strict=False,silent=False): # don't consider 2D objects based on arch elements pass elif Draft.getType(parent) in [ - "BezCurve", "BSpline", "Clone", "Facebinder", "Wire", - "Project", "Roof", "Site", "Space", "Window" - ]: + "BezCurve", + "BSpline", + "Clone", + "Facebinder", + "Wire", + "Project", + "Roof", + "Site", + "Space", + "Window", + ]: pass elif parent.isDerivedFrom("PartDesign::FeatureBase"): # don't consider a PartDesign_Clone that references obj @@ -789,16 +867,18 @@ def pruneIncluded(objectslist,strict=False,silent=False): elif parent.isDerivedFrom("PartDesign::Body") and obj == parent.BaseFeature: # don't consider a PartDesign_Body with a PartDesign_Clone that references obj pass - elif parent.isDerivedFrom("PartDesign::SubShapeBinder") or (hasattr(parent, "TypeId") and parent.TypeId == "PartDesign::ShapeBinder"): + elif parent.isDerivedFrom("PartDesign::SubShapeBinder") or ( + hasattr(parent, "TypeId") and parent.TypeId == "PartDesign::ShapeBinder" + ): # don't consider a PartDesign_SubShapeBinder or PartDesign_ShapeBinder referencing this object from another object pass - elif hasattr(parent,"Host") and parent.Host == obj: + elif hasattr(parent, "Host") and parent.Host == obj: pass - elif hasattr(parent,"Hosts") and obj in parent.Hosts: + elif hasattr(parent, "Hosts") and obj in parent.Hosts: pass - elif hasattr(parent,"TypeId") and parent.TypeId == "Part::Mirroring": + elif hasattr(parent, "TypeId") and parent.TypeId == "Part::Mirroring": pass - elif hasattr(parent,"CloneOf"): + elif hasattr(parent, "CloneOf"): if parent.CloneOf: if parent.CloneOf.Name != obj.Name: toplevel = False @@ -813,9 +893,10 @@ def pruneIncluded(objectslist,strict=False,silent=False): if toplevel: newlist.append(obj) elif not silent: - FreeCAD.Console.PrintWarning("pruning "+obj.Label+"\n") + FreeCAD.Console.PrintWarning("pruning " + obj.Label + "\n") return newlist + def getAllChildren(objectlist): "getAllChildren(objectlist): returns all the children of all the object sin the list" obs = [] @@ -829,12 +910,13 @@ def getAllChildren(objectlist): obs.append(c) return obs + def get_architectural_contents( initial_objects, recursive=True, discover_hosted_elements=True, include_components_from_additions=False, - include_initial_objects_in_result=True + include_initial_objects_in_result=True, ): """ Retrieves a flat list of unique architectural objects that are considered "contents" of or are @@ -879,18 +961,19 @@ def get_architectural_contents( if not isinstance(initial_objects, list): initial_objects_list = [initial_objects] else: - initial_objects_list = list(initial_objects) # Make a copy + initial_objects_list = list(initial_objects) # Make a copy queue.extend(initial_objects_list) # Set to keep track of object names already added to the queue or fully processed # This prevents duplicates in the queue and reprocessing. processed_or_queued_names = set() - for item in initial_objects_list: # Pre-populate for initial items if they are to be added later + for ( + item + ) in initial_objects_list: # Pre-populate for initial items if they are to be added later processed_or_queued_names.add(item.Name) - - idx = 0 # Use an index for iterating the queue, as it can grow + idx = 0 # Use an index for iterating the queue, as it can grow while idx < len(queue): obj = queue[idx] idx += 1 @@ -910,7 +993,7 @@ def get_architectural_contents( for child in obj.Group: if child.Name not in processed_or_queued_names: children_to_add_to_queue_next.append(child) - processed_or_queued_names.add(child.Name) # Mark as queued + processed_or_queued_names.add(child.Name) # Mark as queued # 2. Architecturally-hosted elements (if discover_hosted_elements) if discover_hosted_elements: @@ -919,7 +1002,7 @@ def get_architectural_contents( # Hosted elements are typically in the host's InList for item_in_inlist in obj.InList: element_to_check = item_in_inlist - if hasattr(item_in_inlist, "getLinkedObject"): # Resolve App::Link + if hasattr(item_in_inlist, "getLinkedObject"): # Resolve App::Link linked = item_in_inlist.getLinkedObject() if linked: element_to_check = linked @@ -930,7 +1013,7 @@ def get_architectural_contents( is_confirmed_hosted = False element_type = Draft.getType(element_to_check) - if element_type == "Window": # This covers Arch Windows and Arch Doors + if element_type == "Window": # This covers Arch Windows and Arch Doors if hasattr(element_to_check, "Hosts") and obj in element_to_check.Hosts: is_confirmed_hosted = True elif element_type == "Rebar": @@ -939,20 +1022,20 @@ def get_architectural_contents( if is_confirmed_hosted: children_to_add_to_queue_next.append(element_to_check) - processed_or_queued_names.add(element_to_check.Name) # Mark as queued + processed_or_queued_names.add(element_to_check.Name) # Mark as queued # 3. Components from .Additions list (e.g., walls added to a main wall) if include_components_from_additions and hasattr(obj, "Additions") and obj.Additions: for addition_comp in obj.Additions: actual_addition = addition_comp - if hasattr(addition_comp, "getLinkedObject"): # Resolve if Addition is an App::Link + if hasattr(addition_comp, "getLinkedObject"): # Resolve if Addition is an App::Link linked_add = addition_comp.getLinkedObject() if linked_add: actual_addition = linked_add if actual_addition.Name not in processed_or_queued_names: children_to_add_to_queue_next.append(actual_addition) - processed_or_queued_names.add(actual_addition.Name) # Mark as queued + processed_or_queued_names.add(actual_addition.Name) # Mark as queued if children_to_add_to_queue_next: # Add newly-discovered children to the end of the queue. This function uses an index @@ -964,17 +1047,18 @@ def get_architectural_contents( return final_contents_list + def survey(callback=False): """survey(): starts survey mode, where you can click edges and faces to get their lengths or area. Clicking on no object (on an empty area) resets the count.""" if not callback: - if hasattr(FreeCAD,"SurveyObserver"): + if hasattr(FreeCAD, "SurveyObserver"): for label in FreeCAD.SurveyObserver.labels: FreeCAD.ActiveDocument.removeObject(label) FreeCADGui.Selection.removeObserver(FreeCAD.SurveyObserver) del FreeCAD.SurveyObserver FreeCADGui.Control.closeDialog() - if hasattr(FreeCAD,"SurveyDialog"): + if hasattr(FreeCAD, "SurveyDialog"): del FreeCAD.SurveyDialog else: FreeCAD.SurveyObserver = _SurveyObserver(callback=survey) @@ -983,7 +1067,7 @@ def survey(callback=False): FreeCADGui.Control.showDialog(FreeCAD.SurveyDialog) else: sel = FreeCADGui.Selection.getSelectionEx() - if hasattr(FreeCAD,"SurveyObserver"): + if hasattr(FreeCAD, "SurveyObserver"): if not sel: if FreeCAD.SurveyObserver.labels: for label in FreeCAD.SurveyObserver.labels: @@ -998,13 +1082,13 @@ def survey(callback=False): if not FreeCAD.SurveyObserver.cancellable: FreeCAD.Console.PrintMessage("\n---- Reset ----\n\n") FreeCAD.SurveyObserver.cancellable = True - if hasattr(FreeCAD,"SurveyDialog"): - FreeCAD.SurveyDialog.newline(tl,ta) + if hasattr(FreeCAD, "SurveyDialog"): + FreeCAD.SurveyDialog.newline(tl, ta) else: FreeCADGui.Selection.removeObserver(FreeCAD.SurveyObserver) del FreeCAD.SurveyObserver FreeCADGui.Control.closeDialog() - if hasattr(FreeCAD,"SurveyDialog"): + if hasattr(FreeCAD, "SurveyDialog"): del FreeCAD.SurveyDialog else: FreeCAD.SurveyObserver.cancellable = False @@ -1020,43 +1104,57 @@ def survey(callback=False): newsels.append(o) if newsels: for o in newsels: - if hasattr(o.Object, 'Shape'): + if hasattr(o.Object, "Shape"): n = o.Object.Label showUnit = params.get_param_arch("surveyUnits") t = "" u = FreeCAD.Units.Quantity() if not o.HasSubObjects: # entire object - anno = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel","surveyLabel") - if hasattr(o.Object.Shape,"CenterOfMass"): + anno = FreeCAD.ActiveDocument.addObject( + "App::AnnotationLabel", "surveyLabel" + ) + if hasattr(o.Object.Shape, "CenterOfMass"): anno.BasePosition = o.Object.Shape.CenterOfMass else: anno.BasePosition = o.Object.Shape.BoundBox.Center FreeCAD.SurveyObserver.labels.append(anno.Name) if o.Object.Shape.Solids: - u = FreeCAD.Units.Quantity(o.Object.Shape.Volume,FreeCAD.Units.Volume) + u = FreeCAD.Units.Quantity( + o.Object.Shape.Volume, FreeCAD.Units.Volume + ) t = u.getUserPreferred()[0] - t = t.replace("^3","³") + t = t.replace("^3", "³") anno.LabelText = "v " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Volume: " + t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + n + ", Element: Whole, Volume: " + t + "\n" + ) FreeCAD.SurveyObserver.totalVolume += u.Value elif o.Object.Shape.Faces: - u = FreeCAD.Units.Quantity(o.Object.Shape.Area,FreeCAD.Units.Area) + u = FreeCAD.Units.Quantity( + o.Object.Shape.Area, FreeCAD.Units.Area + ) t = u.getUserPreferred()[0] - t = t.replace("^2","²") + t = t.replace("^2", "²") anno.LabelText = "a " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Area: " + t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + n + ", Element: Whole, Area: " + t + "\n" + ) FreeCAD.SurveyObserver.totalArea += u.Value - if hasattr(FreeCAD,"SurveyDialog"): - FreeCAD.SurveyDialog.update(2,t) + if hasattr(FreeCAD, "SurveyDialog"): + FreeCAD.SurveyDialog.update(2, t) else: - u = FreeCAD.Units.Quantity(o.Object.Shape.Length,FreeCAD.Units.Length) + u = FreeCAD.Units.Quantity( + o.Object.Shape.Length, FreeCAD.Units.Length + ) t = u.getUserPreferred()[0] anno.LabelText = "l " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Length: " + t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + n + ", Element: Whole, Length: " + t + "\n" + ) FreeCAD.SurveyObserver.totalLength += u.Value - if hasattr(FreeCAD,"SurveyDialog"): - FreeCAD.SurveyDialog.update(1,t) + if hasattr(FreeCAD, "SurveyDialog"): + FreeCAD.SurveyDialog.update(1, t) if FreeCAD.GuiUp and t: if showUnit: QtGui.QApplication.clipboard().setText(t) @@ -1065,38 +1163,64 @@ def survey(callback=False): else: # single element(s) for el in o.SubElementNames: - e = getattr(o.Object.Shape,el) - anno = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel","surveyLabel") + e = getattr(o.Object.Shape, el) + anno = FreeCAD.ActiveDocument.addObject( + "App::AnnotationLabel", "surveyLabel" + ) if "Vertex" in el: anno.BasePosition = e.Point else: - if hasattr(e,"CenterOfMass"): + if hasattr(e, "CenterOfMass"): anno.BasePosition = e.CenterOfMass else: anno.BasePosition = e.BoundBox.Center FreeCAD.SurveyObserver.labels.append(anno.Name) if "Face" in el: - u = FreeCAD.Units.Quantity(e.Area,FreeCAD.Units.Area) + u = FreeCAD.Units.Quantity(e.Area, FreeCAD.Units.Area) t = u.getUserPreferred()[0] - t = t.replace("^2","²") + t = t.replace("^2", "²") anno.LabelText = "a " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Area: "+ t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + + n + + ", Element: " + + el + + ", Area: " + + t + + "\n" + ) FreeCAD.SurveyObserver.totalArea += u.Value - if hasattr(FreeCAD,"SurveyDialog"): - FreeCAD.SurveyDialog.update(2,t) + if hasattr(FreeCAD, "SurveyDialog"): + FreeCAD.SurveyDialog.update(2, t) elif "Edge" in el: - u= FreeCAD.Units.Quantity(e.Length,FreeCAD.Units.Length) + u = FreeCAD.Units.Quantity(e.Length, FreeCAD.Units.Length) t = u.getUserPreferred()[0] anno.LabelText = "l " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Length: " + t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + + n + + ", Element: " + + el + + ", Length: " + + t + + "\n" + ) FreeCAD.SurveyObserver.totalLength += u.Value - if hasattr(FreeCAD,"SurveyDialog"): - FreeCAD.SurveyDialog.update(1,t) + if hasattr(FreeCAD, "SurveyDialog"): + FreeCAD.SurveyDialog.update(1, t) elif "Vertex" in el: - u = FreeCAD.Units.Quantity(e.Z,FreeCAD.Units.Length) + u = FreeCAD.Units.Quantity(e.Z, FreeCAD.Units.Length) t = u.getUserPreferred()[0] anno.LabelText = "z " + t - FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Zcoord: " + t + "\n") + FreeCAD.Console.PrintMessage( + "Object: " + + n + + ", Element: " + + el + + ", Zcoord: " + + t + + "\n" + ) if FreeCAD.GuiUp and t: if showUnit: QtGui.QApplication.clipboard().setText(t) @@ -1104,28 +1228,40 @@ def survey(callback=False): QtGui.QApplication.clipboard().setText(str(u.Value)) FreeCAD.SurveyObserver.selection.extend(newsels) - if hasattr(FreeCAD,"SurveyObserver"): - if FreeCAD.SurveyObserver.totalLength or FreeCAD.SurveyObserver.totalArea or FreeCAD.SurveyObserver.totalVolume: + if hasattr(FreeCAD, "SurveyObserver"): + if ( + FreeCAD.SurveyObserver.totalLength + or FreeCAD.SurveyObserver.totalArea + or FreeCAD.SurveyObserver.totalVolume + ): msg = " Total:" if FreeCAD.SurveyObserver.totalLength: - u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalLength,FreeCAD.Units.Length) + u = FreeCAD.Units.Quantity( + FreeCAD.SurveyObserver.totalLength, FreeCAD.Units.Length + ) t = u.getUserPreferred()[0] msg += " Length: " + t if FreeCAD.SurveyObserver.totalArea: - u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalArea,FreeCAD.Units.Area) + u = FreeCAD.Units.Quantity( + FreeCAD.SurveyObserver.totalArea, FreeCAD.Units.Area + ) t = u.getUserPreferred()[0] - t = t.replace("^2","²") + t = t.replace("^2", "²") msg += " Area: " + t if FreeCAD.SurveyObserver.totalVolume: - u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalVolume,FreeCAD.Units.Volume) + u = FreeCAD.Units.Quantity( + FreeCAD.SurveyObserver.totalVolume, FreeCAD.Units.Volume + ) t = u.getUserPreferred()[0] - t = t.replace("^3","³") + t = t.replace("^3", "³") msg += " Volume: " + t - FreeCAD.Console.PrintMessage(msg+"\n") + FreeCAD.Console.PrintMessage(msg + "\n") + class _SurveyObserver: "an observer for the survey() function" - def __init__(self,callback): + + def __init__(self, callback): self.callback = callback self.selection = [] self.labels = [] @@ -1135,16 +1271,17 @@ class _SurveyObserver: self.cancellable = False self.doubleclear = False - def addSelection(self,document, object, element, position): + def addSelection(self, document, object, element, position): self.doubleclear = False self.callback(True) - def clearSelection(self,document): + def clearSelection(self, document): if not self.doubleclear: self.doubleclear = True else: self.callback(True) + class SurveyTaskPanel: "A task panel for the survey tool" @@ -1176,21 +1313,27 @@ class SurveyTaskPanel: QtCore.QObject.connect(self.copyLength, QtCore.SIGNAL("clicked()"), self.clipLength) QtCore.QObject.connect(self.copyArea, QtCore.SIGNAL("clicked()"), self.clipArea) QtCore.QObject.connect(self.export, QtCore.SIGNAL("clicked()"), self.exportCSV) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.setDescr) + QtCore.QObject.connect( + self.tree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.setDescr + ) self.retranslateUi(self) item = QtGui.QTreeWidgetItem(self.tree) self.tree.setCurrentItem(item) - def retranslateUi(self,dlg): + def retranslateUi(self, dlg): self.form.setWindowTitle(QtGui.QApplication.translate("Arch", "Survey", None)) self.addButton.setText(QtGui.QApplication.translate("Arch", "Set Description", None)) self.clearButton.setText(QtGui.QApplication.translate("Arch", "Clear", None)) self.copyLength.setText(QtGui.QApplication.translate("Arch", "Copy Total Length", None)) self.copyArea.setText(QtGui.QApplication.translate("Arch", "Copy Total Area", None)) self.export.setText(QtGui.QApplication.translate("Arch", "Export CSV", None)) - self.tree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Description", None), - QtGui.QApplication.translate("Arch", "Length", None), - QtGui.QApplication.translate("Arch", "Area", None)]) + self.tree.setHeaderLabels( + [ + QtGui.QApplication.translate("Arch", "Description", None), + QtGui.QApplication.translate("Arch", "Length", None), + QtGui.QApplication.translate("Arch", "Area", None), + ] + ) def isAllowedAlterSelection(self): return True @@ -1202,7 +1345,7 @@ class SurveyTaskPanel: return QtGui.QDialogButtonBox.Close def reject(self): - if hasattr(FreeCAD,"SurveyObserver"): + if hasattr(FreeCAD, "SurveyObserver"): for label in FreeCAD.SurveyObserver.labels: FreeCAD.ActiveDocument.removeObject(label) FreeCADGui.Selection.removeObserver(FreeCAD.SurveyObserver) @@ -1213,74 +1356,80 @@ class SurveyTaskPanel: FreeCADGui.Selection.clearSelection() def clipLength(self): - if hasattr(FreeCAD,"SurveyObserver"): - u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalLength,FreeCAD.Units.Length) + if hasattr(FreeCAD, "SurveyObserver"): + u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalLength, FreeCAD.Units.Length) t = u.getUserPreferred()[0] if params.get_param_arch("surveyUnits"): QtGui.QApplication.clipboard().setText(t) else: - QtGui.QApplication.clipboard().setText(str(u.Value/u.getUserPreferred()[1])) + QtGui.QApplication.clipboard().setText(str(u.Value / u.getUserPreferred()[1])) def clipArea(self): - if hasattr(FreeCAD,"SurveyObserver"): - u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalArea,FreeCAD.Units.Area) + if hasattr(FreeCAD, "SurveyObserver"): + u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalArea, FreeCAD.Units.Area) t = u.getUserPreferred()[0] - t = t.replace("^2","²") + t = t.replace("^2", "²") if params.get_param_arch("surveyUnits"): QtGui.QApplication.clipboard().setText(t) else: - QtGui.QApplication.clipboard().setText(str(u.Value/u.getUserPreferred()[1])) + QtGui.QApplication.clipboard().setText(str(u.Value / u.getUserPreferred()[1])) - def newline(self,length=0,area=0): + def newline(self, length=0, area=0): FreeCADGui.Selection.clearSelection() item = QtGui.QTreeWidgetItem(self.tree) if length or area: - item.setText(0,QtGui.QApplication.translate("Arch", "Total", None)) - item.setToolTip(0,"total") + item.setText(0, QtGui.QApplication.translate("Arch", "Total", None)) + item.setToolTip(0, "total") f = QtGui.QFont() f.setBold(True) - item.setFont(0,f) - item.setFont(1,f) - item.setFont(2,f) + item.setFont(0, f) + item.setFont(1, f) + item.setFont(2, f) else: - item.setText(0,self.descr.text()) + item.setText(0, self.descr.text()) self.descr.setText("") self.tree.setCurrentItem(item) if length: - u = FreeCAD.Units.Quantity(length,FreeCAD.Units.Length) + u = FreeCAD.Units.Quantity(length, FreeCAD.Units.Length) t = u.getUserPreferred()[0] - item.setText(1,t) + item.setText(1, t) if area: - u = FreeCAD.Units.Quantity(area,FreeCAD.Units.Area) + u = FreeCAD.Units.Quantity(area, FreeCAD.Units.Area) t = u.getUserPreferred()[0] - t = t.replace(u"^2",u"²") - item.setText(2,t) + t = t.replace("^2", "²") + item.setText(2, t) if length or area: item = QtGui.QTreeWidgetItem(self.tree) self.tree.setCurrentItem(item) - def update(self,column,txt): + def update(self, column, txt): item = QtGui.QTreeWidgetItem(self.tree) self.tree.setCurrentItem(item) - item.setText(column,txt) + item.setText(column, txt) - def setDescr(self,item,col): + def setDescr(self, item, col): self.descr.setText(item.text(0)) def setText(self): item = self.tree.currentItem() if item: - item.setText(0,self.descr.text()) + item.setText(0, self.descr.text()) self.descr.setText("") def exportCSV(self): import csv + rows = self.tree.topLevelItemCount() if rows: - filename = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), translate("Arch","Export CSV File"), None, "CSV file (*.csv)") + filename = QtGui.QFileDialog.getSaveFileName( + QtGui.QApplication.activeWindow(), + translate("Arch", "Export CSV File"), + None, + "CSV file (*.csv)", + ) if filename: with open(filename[0].encode("utf8"), "w") as csvfile: - csvfile = csv.writer(csvfile,delimiter="\t") + csvfile = csv.writer(csvfile, delimiter="\t") suml = 0 for i in range(rows): item = self.tree.topLevelItem(i) @@ -1289,45 +1438,59 @@ class SurveyTaskPanel: if item.text(1): u = FreeCAD.Units.Quantity(item.text(1)) if item.toolTip(0) == "total": - row.append("=SUM(B"+str(suml+1)+":B"+str(i)+")") + row.append("=SUM(B" + str(suml + 1) + ":B" + str(i) + ")") else: - row.append(u.Value/u.getUserPreferred()[1]) + row.append(u.Value / u.getUserPreferred()[1]) row.append(u.getUserPreferred()[2]) else: - row.extend(["",""]) + row.extend(["", ""]) if item.text(2): - t = item.text(2).replace(u"²",u"^2") + t = item.text(2).replace("²", "^2") u = FreeCAD.Units.Quantity(t) if item.toolTip(0) == "total": - row.append("=SUM(D"+str(suml+1)+":D"+str(i)+")") + row.append("=SUM(D" + str(suml + 1) + ":D" + str(i) + ")") else: - row.append(u.Value/u.getUserPreferred()[1]) + row.append(u.Value / u.getUserPreferred()[1]) row.append(u.getUserPreferred()[2]) else: - row.extend(["",""]) + row.extend(["", ""]) csvfile.writerow(row) if item.toolTip(0) == "total": - suml = i+1 - print("successfully exported ",filename[0]) + suml = i + 1 + print("successfully exported ", filename[0]) def toggleIfcBrepFlag(obj): """toggleIfcBrepFlag(obj): toggles the IFC brep flag of the given object, forcing it to be exported as brep geometry or not.""" - if not hasattr(obj,"IfcData"): - FreeCAD.Console.PrintMessage(translate("Arch","Object does not have settable IFC attributes")) + if not hasattr(obj, "IfcData"): + FreeCAD.Console.PrintMessage( + translate("Arch", "Object does not have settable IFC attributes") + ) else: d = obj.IfcData if "FlagForceBrep" in d: if d["FlagForceBrep"] == "True": d["FlagForceBrep"] = "False" - FreeCAD.Console.PrintMessage(translate("Arch","Disabling B-rep force flag of object")+" "+obj.Label+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Disabling B-rep force flag of object") + + " " + + obj.Label + + "\n" + ) else: d["FlagForceBrep"] = "True" - FreeCAD.Console.PrintMessage(translate("Arch","Enabling B-rep force flag of object")+" "+obj.Label+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Enabling B-rep force flag of object") + + " " + + obj.Label + + "\n" + ) else: d["FlagForceBrep"] = "True" - FreeCAD.Console.PrintMessage(translate("Arch","Enabling B-rep force flag of object")+" "+obj.Label+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Enabling B-rep force flag of object") + " " + obj.Label + "\n" + ) obj.IfcData = d @@ -1336,10 +1499,11 @@ def makeCompoundFromSelected(objects=None): subobjects (faces, edges) or from the selection if objects is None""" import FreeCADGui import Part + so = [] if not objects: objects = FreeCADGui.Selection.getSelectionEx() - if not isinstance(objects,list): + if not isinstance(objects, list): objects = [objects] for o in objects: so.extend(o.SubObjects) @@ -1353,13 +1517,14 @@ def cleanArchSplitter(objects=None): of the given Arch objects or selected Arch objects if objects is None""" import FreeCAD import FreeCADGui + if not objects: objects = FreeCADGui.Selection.getSelection() - if not isinstance(objects,list): + if not isinstance(objects, list): objects = [objects] for obj in objects: - if hasattr(obj,'Shape'): - if hasattr(obj,"Base"): + if hasattr(obj, "Shape"): + if hasattr(obj, "Base"): if obj.Base: print("Attempting to clean splitters from ", obj.Label) base = obj.Base.getLinkedObject() @@ -1374,14 +1539,15 @@ def rebuildArchShape(objects=None): if objects is None) Arch objects, and tries to rebuild a valid solid from them.""" import FreeCAD import Part + if not objects and FreeCAD.GuiUp: objects = FreeCADGui.Selection.getSelection() - if not isinstance(objects,list): + if not isinstance(objects, list): objects = [objects] for obj in objects: success = False - if hasattr(obj,'Shape'): - if hasattr(obj,"Base"): + if hasattr(obj, "Shape"): + if hasattr(obj, "Base"): if obj.Base: try: print("Attempting to rebuild ", obj.Label) @@ -1391,29 +1557,29 @@ def rebuildArchShape(objects=None): faces = [] for f in base.Shape.Faces: f2 = Part.Face(f.Wires) - #print("rebuilt face: isValid is ", f2.isValid()) + # print("rebuilt face: isValid is ", f2.isValid()) faces.append(f2) if faces: shell = Part.Shell(faces) if shell: - #print("rebuilt shell: isValid is ", shell.isValid()) + # print("rebuilt shell: isValid is ", shell.isValid()) solid = Part.Solid(shell) if solid: if not solid.isValid(): solid.sewShape() solid = Part.Solid(solid) - #print("rebuilt solid: isValid is ",solid.isValid()) + # print("rebuilt solid: isValid is ",solid.isValid()) if solid.isValid(): base.Shape = solid success = True except Exception: pass if not success: - print ("Failed to rebuild a valid solid for object ",obj.Name) + print("Failed to rebuild a valid solid for object ", obj.Name) FreeCAD.ActiveDocument.recompute() -def getExtrusionData(shape,sortmethod="area"): +def getExtrusionData(shape, sortmethod="area"): """If a shape has been extruded, returns the base face, and extrusion vector. Determines if a shape appears to have been extruded from some base face, and @@ -1455,9 +1621,10 @@ def getExtrusionData(shape,sortmethod="area"): # build faces list with normals faces = [] import Part + for f in shape.Faces: try: - faces.append([f,f.normalAt(0,0)]) + faces.append([f, f.normalAt(0, 0)]) except Part.OCCError: return None # find opposite normals pairs @@ -1465,25 +1632,35 @@ def getExtrusionData(shape,sortmethod="area"): for i1, f1 in enumerate(faces): for i2, f2 in enumerate(faces): if f1[0].hashCode() != f2[0].hashCode(): - if round(f1[1].getAngle(f2[1]),4) == 3.1416: - pairs.append([i1,i2]) + if round(f1[1].getAngle(f2[1]), 4) == 3.1416: + pairs.append([i1, i2]) if not pairs: return None valids = [] for pair in pairs: - hc = [faces[pair[0]][0].hashCode(),faces[pair[1]][0].hashCode()] + hc = [faces[pair[0]][0].hashCode(), faces[pair[1]][0].hashCode()] # check if other normals are all at 90 degrees ok = True for f in faces: if f[0].hashCode() not in hc: - if round(f[1].getAngle(faces[pair[0]][1]),4) != 1.5708: + if round(f[1].getAngle(faces[pair[0]][1]), 4) != 1.5708: ok = False if ok: # prefer the face with the lowest z if faces[pair[0]][0].CenterOfMass.z < faces[pair[1]][0].CenterOfMass.z: - valids.append([faces[pair[0]][0],faces[pair[1]][0].CenterOfMass.sub(faces[pair[0]][0].CenterOfMass)]) + valids.append( + [ + faces[pair[0]][0], + faces[pair[1]][0].CenterOfMass.sub(faces[pair[0]][0].CenterOfMass), + ] + ) else: - valids.append([faces[pair[1]][0],faces[pair[0]][0].CenterOfMass.sub(faces[pair[1]][0].CenterOfMass)]) + valids.append( + [ + faces[pair[1]][0], + faces[pair[0]][0].CenterOfMass.sub(faces[pair[1]][0].CenterOfMass), + ] + ) if valids: if sortmethod == "z": valids.sort(key=lambda v: v[0].CenterOfMass.z) @@ -1495,37 +1672,50 @@ def getExtrusionData(shape,sortmethod="area"): return valids[0] return None -def printMessage( message ): - FreeCAD.Console.PrintMessage( message ) - if FreeCAD.GuiUp : - QtGui.QMessageBox.information( None , "" , message ) -def printWarning( message ): - FreeCAD.Console.PrintMessage( message ) - if FreeCAD.GuiUp : - QtGui.QMessageBox.warning( None , "" , message ) +def printMessage(message): + FreeCAD.Console.PrintMessage(message) + if FreeCAD.GuiUp: + QtGui.QMessageBox.information(None, "", message) + + +def printWarning(message): + FreeCAD.Console.PrintMessage(message) + if FreeCAD.GuiUp: + QtGui.QMessageBox.warning(None, "", message) + def makeIfcSpreadsheet(archobj=None): ifc_container = None - for obj in FreeCAD.ActiveDocument.Objects : - if obj.Name == "IfcPropertiesContainer" : + for obj in FreeCAD.ActiveDocument.Objects: + if obj.Name == "IfcPropertiesContainer": ifc_container = obj - if not ifc_container : - ifc_container = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup','IfcPropertiesContainer') + if not ifc_container: + ifc_container = FreeCAD.ActiveDocument.addObject( + "App::DocumentObjectGroup", "IfcPropertiesContainer" + ) import Spreadsheet - ifc_spreadsheet = FreeCAD.ActiveDocument.addObject('Spreadsheet::Sheet','IfcProperties') - ifc_spreadsheet.set('A1', translate("Arch","Category")) - ifc_spreadsheet.set('B1', translate("Arch","Key")) - ifc_spreadsheet.set('C1', translate("Arch","Type")) - ifc_spreadsheet.set('D1', translate("Arch","Value")) - ifc_spreadsheet.set('E1', translate("Arch","Unit")) + + ifc_spreadsheet = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet", "IfcProperties") + ifc_spreadsheet.set("A1", translate("Arch", "Category")) + ifc_spreadsheet.set("B1", translate("Arch", "Key")) + ifc_spreadsheet.set("C1", translate("Arch", "Type")) + ifc_spreadsheet.set("D1", translate("Arch", "Value")) + ifc_spreadsheet.set("E1", translate("Arch", "Unit")) ifc_container.addObject(ifc_spreadsheet) - if archobj : - if hasattr(obj,"IfcProperties") : + if archobj: + if hasattr(obj, "IfcProperties"): archobj.IfcProperties = ifc_spreadsheet return ifc_spreadsheet - else : - FreeCAD.Console.PrintWarning(translate("Arch", "The object does not have an IfcProperties attribute. Cancel spreadsheet creation for object:")+ ' ' + archobj.Label) + else: + FreeCAD.Console.PrintWarning( + translate( + "Arch", + "The object does not have an IfcProperties attribute. Cancel spreadsheet creation for object:", + ) + + " " + + archobj.Label + ) FreeCAD.ActiveDocument.removeObject(ifc_spreadsheet) - else : + else: return ifc_spreadsheet diff --git a/src/Mod/BIM/ArchComponent.py b/src/Mod/BIM/ArchComponent.py index b04bcaadf5..f6820f5a5a 100644 --- a/src/Mod/BIM/ArchComponent.py +++ b/src/Mod/BIM/ArchComponent.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Component" +__title__ = "FreeCAD Arch Component" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchComponent # \ingroup ARCH @@ -49,20 +49,22 @@ import Draft from draftutils import params if FreeCAD.GuiUp: - from PySide import QtGui,QtCore + from PySide import QtGui, QtCore from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCADGui from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -def addToComponent(compobject,addobject,mod=None): +def addToComponent(compobject, addobject, mod=None): """Add an object to a component's properties. Does not run if the addobject already exists in the component's properties. @@ -81,48 +83,50 @@ def addToComponent(compobject,addobject,mod=None): """ import Draft - if compobject == addobject: return + + if compobject == addobject: + return # first check zis already there found = False - attribs = ["Additions","Objects","Components","Subtractions","Base","Group","Hosts"] + attribs = ["Additions", "Objects", "Components", "Subtractions", "Base", "Group", "Hosts"] for a in attribs: - if hasattr(compobject,a): + if hasattr(compobject, a): if a == "Base": - if addobject == getattr(compobject,a): + if addobject == getattr(compobject, a): found = True else: - if addobject in getattr(compobject,a): + if addobject in getattr(compobject, a): found = True if not found: if mod: - if hasattr(compobject,mod): + if hasattr(compobject, mod): if mod == "Base": - setattr(compobject,mod,addobject) + setattr(compobject, mod, addobject) addobject.ViewObject.hide() elif mod == "Axes": if Draft.getType(addobject) == "Axis": - l = getattr(compobject,mod) + l = getattr(compobject, mod) l.append(addobject) - setattr(compobject,mod,l) + setattr(compobject, mod, l) else: - l = getattr(compobject,mod) + l = getattr(compobject, mod) l.append(addobject) - setattr(compobject,mod,l) + setattr(compobject, mod, l) if mod != "Objects": addobject.ViewObject.hide() if Draft.getType(compobject) == "PanelSheet": addobject.Placement.move(compobject.Placement.Base.negative()) else: for a in attribs[:3]: - if hasattr(compobject,a): - l = getattr(compobject,a) + if hasattr(compobject, a): + l = getattr(compobject, a) l.append(addobject) - setattr(compobject,a,l) + setattr(compobject, a, l) addobject.ViewObject.hide() break -def removeFromComponent(compobject,subobject): +def removeFromComponent(compobject, subobject): """Remove the object from the given component. Try to find the object in the component's properties. If found, remove the @@ -139,35 +143,47 @@ def removeFromComponent(compobject,subobject): The object to remove from the component. """ - if compobject == subobject: return + if compobject == subobject: + return found = False - attribs = ["Additions","Subtractions","Objects","Components","Base","Axes","Fixtures","Group","Hosts"] + attribs = [ + "Additions", + "Subtractions", + "Objects", + "Components", + "Base", + "Axes", + "Fixtures", + "Group", + "Hosts", + ] for a in attribs: - if hasattr(compobject,a): + if hasattr(compobject, a): if a == "Base": - if subobject == getattr(compobject,a): - setattr(compobject,a,None) + if subobject == getattr(compobject, a): + setattr(compobject, a, None) subobject.ViewObject.show() found = True else: - if subobject in getattr(compobject,a): - l = getattr(compobject,a) + if subobject in getattr(compobject, a): + l = getattr(compobject, a) l.remove(subobject) - setattr(compobject,a,l) + setattr(compobject, a, l) subobject.ViewObject.show() if Draft.getType(compobject) == "PanelSheet": subobject.Placement.move(compobject.Placement.Base) found = True if not found: - if hasattr(compobject,"Subtractions"): + if hasattr(compobject, "Subtractions"): l = compobject.Subtractions l.append(subobject) compobject.Subtractions = l - if (Draft.getType(subobject) != "Window") and (not Draft.isClone(subobject,"Window",True)): + if (Draft.getType(subobject) != "Window") and ( + not Draft.isClone(subobject, "Window", True) + ): ArchCommands.setAsSubcomponent(subobject) - class Component(ArchIFC.IfcProduct): """The Arch Component object. @@ -186,7 +202,7 @@ class Component(ArchIFC.IfcProduct): def __init__(self, obj): obj.Proxy = self self.Type = "Component" - Component.setProperties(self,obj) + Component.setProperties(self, obj) def setProperties(self, obj): """Give the component its component specific properties, such as material. @@ -199,47 +215,156 @@ class Component(ArchIFC.IfcProduct): pl = obj.PropertiesList if not "Base" in pl: - obj.addProperty("App::PropertyLink","Base","Component",QT_TRANSLATE_NOOP("App::Property","The base object this component is built upon"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Base", + "Component", + QT_TRANSLATE_NOOP("App::Property", "The base object this component is built upon"), + locked=True, + ) if not "CloneOf" in pl: - obj.addProperty("App::PropertyLink","CloneOf","Component",QT_TRANSLATE_NOOP("App::Property","The object this component is cloning"), locked=True) + obj.addProperty( + "App::PropertyLink", + "CloneOf", + "Component", + QT_TRANSLATE_NOOP("App::Property", "The object this component is cloning"), + locked=True, + ) if not "Additions" in pl: - obj.addProperty("App::PropertyLinkList","Additions","Component",QT_TRANSLATE_NOOP("App::Property","Other shapes that are appended to this object"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Additions", + "Component", + QT_TRANSLATE_NOOP("App::Property", "Other shapes that are appended to this object"), + locked=True, + ) if not "Subtractions" in pl: - obj.addProperty("App::PropertyLinkList","Subtractions","Component",QT_TRANSLATE_NOOP("App::Property","Other shapes that are subtracted from this object"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Subtractions", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", "Other shapes that are subtracted from this object" + ), + locked=True, + ) if not "Description" in pl: - obj.addProperty("App::PropertyString","Description","Component",QT_TRANSLATE_NOOP("App::Property","An optional description for this component"), locked=True) + obj.addProperty( + "App::PropertyString", + "Description", + "Component", + QT_TRANSLATE_NOOP("App::Property", "An optional description for this component"), + locked=True, + ) if not "Tag" in pl: - obj.addProperty("App::PropertyString","Tag","Component",QT_TRANSLATE_NOOP("App::Property","An optional tag for this component"), locked=True) + obj.addProperty( + "App::PropertyString", + "Tag", + "Component", + QT_TRANSLATE_NOOP("App::Property", "An optional tag for this component"), + locked=True, + ) if not "StandardCode" in pl: - obj.addProperty("App::PropertyString","StandardCode","Component",QT_TRANSLATE_NOOP("App::Property","An optional standard (OmniClass, etc…) code for this component"), locked=True) + obj.addProperty( + "App::PropertyString", + "StandardCode", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional standard (OmniClass, etc…) code for this component", + ), + locked=True, + ) if not "Material" in pl: - obj.addProperty("App::PropertyLink","Material","Component",QT_TRANSLATE_NOOP("App::Property","A material for this object"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Material", + "Component", + QT_TRANSLATE_NOOP("App::Property", "A material for this object"), + locked=True, + ) if "BaseMaterial" in pl: obj.Material = obj.BaseMaterial obj.removeProperty("BaseMaterial") - FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" BaseMaterial property to Material\n") + FreeCAD.Console.PrintMessage( + "Upgrading " + obj.Label + " BaseMaterial property to Material\n" + ) if not "MoveBase" in pl: - obj.addProperty("App::PropertyBool","MoveBase","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if moving this object moves its base instead"), locked=True) + obj.addProperty( + "App::PropertyBool", + "MoveBase", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", "Specifies if moving this object moves its base instead" + ), + locked=True, + ) obj.MoveBase = params.get_param_arch("MoveBase") if not "MoveWithHost" in pl: - obj.addProperty("App::PropertyBool","MoveWithHost","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if this object must move together when its host is moved"), locked=True) + obj.addProperty( + "App::PropertyBool", + "MoveWithHost", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", + "Specifies if this object must move together when its host is moved", + ), + locked=True, + ) obj.MoveWithHost = params.get_param_arch("MoveWithHost") if not "VerticalArea" in pl: - obj.addProperty("App::PropertyArea","VerticalArea","Component",QT_TRANSLATE_NOOP("App::Property","The area of all vertical faces of this object"), locked=True) - obj.setEditorMode("VerticalArea",1) + obj.addProperty( + "App::PropertyArea", + "VerticalArea", + "Component", + QT_TRANSLATE_NOOP("App::Property", "The area of all vertical faces of this object"), + locked=True, + ) + obj.setEditorMode("VerticalArea", 1) if not "HorizontalArea" in pl: - obj.addProperty("App::PropertyArea","HorizontalArea","Component",QT_TRANSLATE_NOOP("App::Property","The area of the projection of this object onto the XY plane"), locked=True) - obj.setEditorMode("HorizontalArea",1) + obj.addProperty( + "App::PropertyArea", + "HorizontalArea", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", "The area of the projection of this object onto the XY plane" + ), + locked=True, + ) + obj.setEditorMode("HorizontalArea", 1) if not "PerimeterLength" in pl: - obj.addProperty("App::PropertyLength","PerimeterLength","Component",QT_TRANSLATE_NOOP("App::Property","The perimeter length of the horizontal area"), locked=True) - obj.setEditorMode("PerimeterLength",1) + obj.addProperty( + "App::PropertyLength", + "PerimeterLength", + "Component", + QT_TRANSLATE_NOOP("App::Property", "The perimeter length of the horizontal area"), + locked=True, + ) + obj.setEditorMode("PerimeterLength", 1) if not "HiRes" in pl: - obj.addProperty("App::PropertyLink","HiRes","Component",QT_TRANSLATE_NOOP("App::Property","An optional higher-resolution mesh or shape for this object"), locked=True) + obj.addProperty( + "App::PropertyLink", + "HiRes", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", "An optional higher-resolution mesh or shape for this object" + ), + locked=True, + ) if not "Axis" in pl: - obj.addProperty("App::PropertyLink","Axis","Component",QT_TRANSLATE_NOOP("App::Property","An optional axis or axis system on which this object should be duplicated"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Axis", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional axis or axis system on which this object should be duplicated", + ), + locked=True, + ) self.Subvolume = None - #self.MoveWithHost = False + # self.MoveWithHost = False def onDocumentRestored(self, obj): """Method run when the document is restored. Re-add the Arch component properties. @@ -251,7 +376,7 @@ class Component(ArchIFC.IfcProduct): """ Component.setProperties(self, obj) - def execute(self,obj): + def execute(self, obj): """Method run when the object is recomputed. If the object is a clone, just copy the shape it's cloned from. @@ -270,18 +395,18 @@ class Component(ArchIFC.IfcProduct): if not self.ensureBase(obj): return if obj.Base: - shape = self.spread(obj,obj.Base.Shape) + shape = self.spread(obj, obj.Base.Shape) if obj.Additions or obj.Subtractions: - shape = self.processSubShapes(obj,shape) + shape = self.processSubShapes(obj, shape) obj.Shape = shape def dumps(self): return None - def loads(self,state): + def loads(self, state): self.Type = "Component" - def onBeforeChange(self,obj,prop): + def onBeforeChange(self, obj, prop): """Method called before the object has a property changed. Specifically, this method is called before the value changes. @@ -322,7 +447,7 @@ class Component(ArchIFC.IfcProduct): ArchIFC.IfcProduct.onChanged(self, obj, prop) if prop == "Placement": - if hasattr(self,"oldPlacement") and self.oldPlacement != obj.Placement: + if hasattr(self, "oldPlacement") and self.oldPlacement != obj.Placement: deltap = obj.Placement.Base.sub(self.oldPlacement.Base) if deltap.Length == 0: deltap = None @@ -331,14 +456,16 @@ class Component(ArchIFC.IfcProduct): deltar = None for child in self.getMovableChildren(obj): if deltar: - child.Placement.rotate(self.oldPlacement.Base, - deltar.Axis, - math.degrees(deltar.Angle), - comp=True) + child.Placement.rotate( + self.oldPlacement.Base, + deltar.Axis, + math.degrees(deltar.Angle), + comp=True, + ) if deltap: child.Placement.move(deltap) - def getMovableChildren(self,obj): + def getMovableChildren(self, obj): """Find the component's children set to move with their host. In this case, children refer to Additions, Subtractions, and objects @@ -359,10 +486,10 @@ class Component(ArchIFC.IfcProduct): ilist = obj.Additions + obj.Subtractions for o in obj.InList: - if hasattr(o,"Hosts"): + if hasattr(o, "Hosts"): if obj in o.Hosts: ilist.append(o) - elif hasattr(o,"Host"): + elif hasattr(o, "Host"): if obj == o.Host: ilist.append(o) @@ -375,14 +502,14 @@ class Component(ArchIFC.IfcProduct): ilist2 = [] for o in ilist: - if hasattr(o,"MoveWithHost"): + if hasattr(o, "MoveWithHost"): if o.MoveWithHost: ilist2.append(o) else: ilist2.append(o) return ilist2 - def getParentHeight(self,obj): + def getParentHeight(self, obj): """Get a height value from hosts. Recursively crawl hosts until a Floor or BuildingPart is found, then @@ -400,24 +527,24 @@ class Component(ArchIFC.IfcProduct): """ for parent in obj.InList: - if Draft.getType(parent) in ["Floor","BuildingPart"]: + if Draft.getType(parent) in ["Floor", "BuildingPart"]: if obj in parent.Group: if parent.HeightPropagate: if parent.Height.Value: return parent.Height.Value # not found? get one level higher for parent in obj.InList: - if hasattr(parent,"Group"): + if hasattr(parent, "Group"): if obj in parent.Group: return self.getParentHeight(parent) # still not found? check if we are embedded for parent in obj.InList: - if hasattr(parent,"Additions"): + if hasattr(parent, "Additions"): if obj in parent.Additions: return self.getParentHeight(parent) return 0 - def clone(self,obj): + def clone(self, obj): """If the object is a clone, copy the shape. If the object is a clone according to the "CloneOf" property, copy the @@ -439,20 +566,31 @@ class Component(ArchIFC.IfcProduct): True if the copy occurs, False if otherwise. """ - if hasattr(obj,"CloneOf"): + if hasattr(obj, "CloneOf"): if obj.CloneOf: - if (Draft.getType(obj.CloneOf) == Draft.getType(obj)) or (Draft.getType(obj) in ["Component","BuildingPart"]): + if (Draft.getType(obj.CloneOf) == Draft.getType(obj)) or ( + Draft.getType(obj) in ["Component", "BuildingPart"] + ): pl = obj.Placement ## TODO use Part.Shape() instead? obj.Shape = obj.CloneOf.Shape.copy() obj.Placement = pl - for prop in ["Length","Width","Height","Thickness","Area","PerimeterLength","HorizontalArea","VerticalArea"]: - if hasattr(obj,prop) and hasattr(obj.CloneOf,prop): - setattr(obj,prop,getattr(obj.CloneOf,prop)) + for prop in [ + "Length", + "Width", + "Height", + "Thickness", + "Area", + "PerimeterLength", + "HorizontalArea", + "VerticalArea", + ]: + if hasattr(obj, prop) and hasattr(obj.CloneOf, prop): + setattr(obj, prop, getattr(obj.CloneOf, prop)) return True return False - def getSiblings(self,obj): + def getSiblings(self, obj): """Find objects that have the same Base object, and type. Look to base object, and find other objects that are based off this @@ -469,13 +607,13 @@ class Component(ArchIFC.IfcProduct): List of objects that have the same Base and type as this component. """ - if not hasattr(obj,"Base"): + if not hasattr(obj, "Base"): return [] if not obj.Base: return [] siblings = [] for o in obj.Base.InList: - if hasattr(o,"Base"): + if hasattr(o, "Base"): if o.Base: if o.Base.Name == obj.Base.Name: if o.Name != obj.Name: @@ -483,7 +621,7 @@ class Component(ArchIFC.IfcProduct): siblings.append(o) return siblings - def getExtrusionData(self,obj): + def getExtrusionData(self, obj): """Get the object's extrusion data. Recursively scrape the Bases of the object, until a Base that is @@ -514,17 +652,22 @@ class Component(ArchIFC.IfcProduct): 3) The of the extrusion. """ - if hasattr(obj,"CloneOf"): + if hasattr(obj, "CloneOf"): if obj.CloneOf: - if hasattr(obj.CloneOf,"Proxy"): - if hasattr(obj.CloneOf.Proxy,"getExtrusionData"): + if hasattr(obj.CloneOf, "Proxy"): + if hasattr(obj.CloneOf.Proxy, "getExtrusionData"): data = obj.CloneOf.Proxy.getExtrusionData(obj.CloneOf) if data: return data if obj.Base: # the base is another arch object which can provide extrusion data - if hasattr(obj.Base,"Proxy") and hasattr(obj.Base.Proxy,"getExtrusionData") and (not obj.Additions) and (not obj.Subtractions): + if ( + hasattr(obj.Base, "Proxy") + and hasattr(obj.Base.Proxy, "getExtrusionData") + and (not obj.Additions) + and (not obj.Subtractions) + ): if obj.Base.Base: if obj.Placement.Rotation.Angle < 0.0001: # if the final obj is rotated, this will screw all our IFC orientation. Better leave it like that then... @@ -534,32 +677,32 @@ class Component(ArchIFC.IfcProduct): # TODO above doesn't work if underlying shape is not at (0,0,0). But code below doesn't work well yet # add the displacement of the final object disp = obj.Shape.CenterOfMass.sub(obj.Base.Shape.CenterOfMass) - if isinstance(data[2],(list,tuple)): + if isinstance(data[2], (list, tuple)): ndata2 = [] for p in data[2]: p.move(disp) ndata2.append(p) - return (data[0],data[1],ndata2) + return (data[0], data[1], ndata2) else: ndata2 = data[2] ndata2.move(disp) - return (data[0],data[1],ndata2) + return (data[0], data[1], ndata2) # the base is a Part Extrusion elif obj.Base.isDerivedFrom("Part::Extrusion"): if obj.Base.Base and len(obj.Base.Base.Shape.Wires) == 1: - base,placement = self.rebase(obj.Base.Base.Shape) + base, placement = self.rebase(obj.Base.Base.Shape) extrusion = FreeCAD.Vector(obj.Base.Dir).normalize() if extrusion.Length == 0: - extrusion = FreeCAD.Vector(0,0,1) + extrusion = FreeCAD.Vector(0, 0, 1) else: extrusion = placement.inverse().Rotation.multVec(extrusion) - if hasattr(obj.Base,"LengthFwd"): + if hasattr(obj.Base, "LengthFwd"): if obj.Base.LengthFwd.Value: extrusion = extrusion.multiply(obj.Base.LengthFwd.Value) if not self.isIdentity(obj.Base.Placement): placement = placement.multiply(obj.Base.Placement) - return (base,extrusion,placement) + return (base, extrusion, placement) elif obj.Base.isDerivedFrom("Part::MultiFuse"): rshapes = [] @@ -568,13 +711,13 @@ class Component(ArchIFC.IfcProduct): for sub in obj.Base.Shapes: if sub.isDerivedFrom("Part::Extrusion"): if sub.Base: - base,placement = self.rebase(sub.Base.Shape) + base, placement = self.rebase(sub.Base.Shape) extrusion = FreeCAD.Vector(sub.Dir).normalize() if extrusion.Length == 0: - extrusion = FreeCAD.Vector(0,0,1) + extrusion = FreeCAD.Vector(0, 0, 1) else: extrusion = placement.inverse().Rotation.multVec(extrusion) - if hasattr(sub,"LengthFwd"): + if hasattr(sub, "LengthFwd"): if sub.LengthFwd.Value: extrusion = extrusion.multiply(sub.LengthFwd.Value) placement = obj.Placement.multiply(placement) @@ -584,17 +727,17 @@ class Component(ArchIFC.IfcProduct): else: exdata = ArchCommands.getExtrusionData(sub.Shape) if exdata: - base,placement = self.rebase(exdata[0]) + base, placement = self.rebase(exdata[0]) extrusion = placement.inverse().Rotation.multVec(exdata[1]) placement = obj.Placement.multiply(placement) rshapes.append(base) revs.append(extrusion) rpls.append(placement) if rshapes and revs and rpls: - return (rshapes,revs,rpls) + return (rshapes, revs, rpls) return None - def rebase(self,shape,hint=None): + def rebase(self, shape, hint=None): """Copy a shape to the (0,0,0) origin. Create a copy of a shape, such that its center of mass is in the @@ -619,9 +762,9 @@ class Component(ArchIFC.IfcProduct): import DraftGeomUtils # Get the object's center. - if not isinstance(shape,list): + if not isinstance(shape, list): shape = [shape] - if hasattr(shape[0],"CenterOfMass"): + if hasattr(shape[0], "CenterOfMass"): v = shape[0].CenterOfMass else: v = shape[0].BoundBox.Center @@ -637,7 +780,7 @@ class Component(ArchIFC.IfcProduct): n = n.negative() r = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), n) - if round(abs(r.Angle),8) == round(math.pi,8): + if round(abs(r.Angle), 8) == round(math.pi, 8): r = FreeCAD.Rotation() shapes = [] @@ -645,19 +788,17 @@ class Component(ArchIFC.IfcProduct): ## TODO use Part.Shape() instead? s = s.copy() s.translate(v.negative()) - s.rotate(FreeCAD.Vector(0, 0, 0), - r.Axis, - math.degrees(-r.Angle)) + s.rotate(FreeCAD.Vector(0, 0, 0), r.Axis, math.degrees(-r.Angle)) shapes.append(s) p = FreeCAD.Placement() p.Base = v p.Rotation = r if len(shapes) == 1: - return (shapes[0],p) + return (shapes[0], p) else: - return(shapes,p) + return (shapes, p) - def hideSubobjects(self,obj,prop): + def hideSubobjects(self, obj, prop): """Hides Additions and Subtractions of this Component when that list changes. Intended to be used in conjunction with the .onChanged() method, to @@ -678,22 +819,23 @@ class Component(ArchIFC.IfcProduct): """ if FreeCAD.GuiUp: - if prop in ["Additions","Subtractions"]: - if hasattr(obj,prop): - for o in getattr(obj,prop): - if (Draft.getType(o) != "Window") and (not Draft.isClone(o,"Window",True)): - if (Draft.getType(obj) == "Wall"): - if (Draft.getType(o) == "Roof"): + if prop in ["Additions", "Subtractions"]: + if hasattr(obj, prop): + for o in getattr(obj, prop): + if (Draft.getType(o) != "Window") and ( + not Draft.isClone(o, "Window", True) + ): + if Draft.getType(obj) == "Wall": + if Draft.getType(o) == "Roof": continue o.ViewObject.hide() elif prop in ["Mesh"]: - if hasattr(obj,prop): - o = getattr(obj,prop) + if hasattr(obj, prop): + o = getattr(obj, prop) if o: o.ViewObject.hide() - - def processSubShapes(self,obj,base,placement=None): + def processSubShapes(self, obj, base, placement=None): """Add Additions and Subtractions to a base shape. If Additions exist, fuse them to the base shape. If no base is @@ -723,7 +865,8 @@ class Component(ArchIFC.IfcProduct): import Draft import Part - #print("Processing subshapes of ",obj.Label, " : ",obj.Additions) + + # print("Processing subshapes of ",obj.Label, " : ",obj.Additions) if placement: if self.isIdentity(placement): @@ -738,10 +881,10 @@ class Component(ArchIFC.IfcProduct): # Arch Objects can have no Base, but Additions only # If there is no base/base isNull, 1st Addition becomes 'base', # placement should be treated as rest of Additions. - #if not base: + # if not base: if not base or base.isNull(): - if hasattr(o,'Shape'): - base = Part.Shape(o.Shape) #base = o.Shape + if hasattr(o, "Shape"): + base = Part.Shape(o.Shape) # base = o.Shape # Base is first Addition, treat placement as other Additions if placement: # see https://forum.freecad.org/viewtopic.php?p=579754#p579754 @@ -750,42 +893,49 @@ class Component(ArchIFC.IfcProduct): # base.isNull() case grouped into if condition above, no need # if/else below. Remarked out 2025.9.2 # - #if base.isNull(): + # if base.isNull(): # if hasattr(o,'Shape'): # base = o.Shape - #else: - # special case, both walls with coinciding endpoints - import ArchWall - js = ArchWall.mergeShapes(o,obj) - if js: - add = js.cut(base) + # else: + # special case, both walls with coinciding endpoints + import ArchWall + + js = ArchWall.mergeShapes(o, obj) + if js: + add = js.cut(base) + if placement: + # see https://forum.freecad.org/viewtopic.php?p=579754#p579754 + add.Placement = placement.multiply(add.Placement) + base = base.fuse(add) + elif hasattr(o, "Shape"): + if o.Shape and not o.Shape.isNull() and o.Shape.Solids: + # TODO use Part.Shape() instead? + s = o.Shape.copy() if placement: # see https://forum.freecad.org/viewtopic.php?p=579754#p579754 - add.Placement = placement.multiply(add.Placement) - base = base.fuse(add) - elif hasattr(o,'Shape'): - if o.Shape and not o.Shape.isNull() and o.Shape.Solids: - # TODO use Part.Shape() instead? - s = o.Shape.copy() - if placement: - # see https://forum.freecad.org/viewtopic.php?p=579754#p579754 - s.Placement = placement.multiply(s.Placement) - if base: - if base.Solids: - try: - base = base.fuse(s) - except Part.OCCError: - print("Arch: unable to fuse object ", obj.Name, " with ", o.Name) - else: - base = s + s.Placement = placement.multiply(s.Placement) + if base: + if base.Solids: + try: + base = base.fuse(s) + except Part.OCCError: + print( + "Arch: unable to fuse object ", obj.Name, " with ", o.Name + ) + else: + base = s # treat subtractions subs = obj.Subtractions for link in obj.InListRecursive: - if hasattr(link,"Host"): - if Draft.getType(link) != "Rebar" and link.Host == obj and not self._objectInInternalLinkgroup(link): + if hasattr(link, "Host"): + if ( + Draft.getType(link) != "Rebar" + and link.Host == obj + and not self._objectInInternalLinkgroup(link) + ): subs.append(link) - elif hasattr(link,"Hosts"): + elif hasattr(link, "Hosts"): if obj in link.Hosts and not self._objectInInternalLinkgroup(link): subs.append(link) for o in subs: @@ -796,17 +946,21 @@ class Component(ArchIFC.IfcProduct): if base: subvolume = None - if (Draft.getType(o.getLinkedObject()) == "Window") or (Draft.isClone(o,"Window",True)): + if (Draft.getType(o.getLinkedObject()) == "Window") or ( + Draft.isClone(o, "Window", True) + ): # windows can be additions or subtractions, treated the same way - subvolume = o.getLinkedObject().Proxy.getSubVolume(o,host=obj) # pass host obj (mostly Wall) - elif (Draft.getType(o) == "Roof") or (Draft.isClone(o,"Roof")): + subvolume = o.getLinkedObject().Proxy.getSubVolume( + o, host=obj + ) # pass host obj (mostly Wall) + elif (Draft.getType(o) == "Roof") or (Draft.isClone(o, "Roof")): # roofs define their own special subtraction volume subvolume = o.Proxy.getSubVolume(o).copy() - elif hasattr(o,"Subvolume") and hasattr(o.Subvolume,"Shape"): + elif hasattr(o, "Subvolume") and hasattr(o.Subvolume, "Shape"): # Any other object with a Subvolume property ## TODO - Part.Shape() instead? subvolume = o.Subvolume.Shape.copy() - if hasattr(o,"Placement"): + if hasattr(o, "Placement"): # see https://forum.freecad.org/viewtopic.php?p=579754#p579754 subvolume.Placement = o.Placement.multiply(subvolume.Placement) @@ -819,7 +973,7 @@ class Component(ArchIFC.IfcProduct): base = Part.makeCompound([sol.cut(subvolume) for sol in base.Solids]) else: base = base.cut(subvolume) - elif hasattr(o,'Shape'): + elif hasattr(o, "Shape"): # no subvolume, we subtract the whole shape if o.Shape: if not o.Shape.isNull(): @@ -831,14 +985,16 @@ class Component(ArchIFC.IfcProduct): s.Placement = placement.multiply(s.Placement) try: if len(base.Solids) > 1: - base = Part.makeCompound([sol.cut(s) for sol in base.Solids]) + base = Part.makeCompound( + [sol.cut(s) for sol in base.Solids] + ) else: base = base.cut(s) except Part.OCCError: - print("Arch: unable to cut object ",o.Name, " from ", obj.Name) + print("Arch: unable to cut object ", o.Name, " from ", obj.Name) return base - def spread(self,obj,shape,placement=None): + def spread(self, obj, shape, placement=None): """Copy the object to its Axis's points. If the object has the "Axis" property assigned, create a copy of the @@ -865,13 +1021,13 @@ class Component(ArchIFC.IfcProduct): """ points = None - if hasattr(obj,"Axis"): + if hasattr(obj, "Axis"): if obj.Axis: - if hasattr(obj.Axis,"Proxy"): - if hasattr(obj.Axis.Proxy,"getPoints"): + if hasattr(obj.Axis, "Proxy"): + if hasattr(obj.Axis.Proxy, "getPoints"): points = obj.Axis.Proxy.getPoints(obj.Axis) if not points: - if hasattr(obj.Axis,'Shape'): + if hasattr(obj.Axis, "Shape"): points = [v.Point for v in obj.Axis.Shape.Vertexes] if points: shps = [] @@ -881,10 +1037,11 @@ class Component(ArchIFC.IfcProduct): sh.translate(p) shps.append(sh) import Part + shape = Part.makeCompound(shps) return shape - def isIdentity(self,placement): + def isIdentity(self, placement): """Check if a placement is *almost* zero. Check if a 's displacement from (0,0,0) is almost zero, @@ -906,7 +1063,7 @@ class Component(ArchIFC.IfcProduct): return True return False - def applyShape(self,obj,shape,placement,allowinvalid=False,allownosolid=False): + def applyShape(self, obj, shape, placement, allowinvalid=False, allownosolid=False): """Check the given shape, then assign it to the object. Check if the shape is valid, isn't null, and if it has volume. Remove @@ -940,40 +1097,51 @@ class Component(ArchIFC.IfcProduct): if shape.Volume < 0: shape.reverse() if shape.Volume < 0: - FreeCAD.Console.PrintError(translate("Arch","Error computing the shape of this object")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Error computing the shape of this object") + "\n" + ) return import Part + try: r = shape.removeSplitter() except Part.OCCError: pass else: shape = r - p = self.spread(obj,shape,placement).Placement.copy() # for some reason this gets zeroed in next line - obj.Shape = self.spread(obj,shape,placement) + p = self.spread( + obj, shape, placement + ).Placement.copy() # for some reason this gets zeroed in next line + obj.Shape = self.spread(obj, shape, placement) if not self.isIdentity(placement): obj.Placement = placement else: obj.Placement = p else: if allownosolid: - obj.Shape = self.spread(obj,shape,placement) + obj.Shape = self.spread(obj, shape, placement) if not self.isIdentity(placement): obj.Placement = placement else: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch","has no solid")+"\n") + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has no solid") + "\n" + ) else: if allowinvalid: - obj.Shape = self.spread(obj,shape,placement) + obj.Shape = self.spread(obj, shape, placement) if not self.isIdentity(placement): obj.Placement = placement else: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch","has an invalid shape")+"\n") + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has an invalid shape") + "\n" + ) else: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch","has a null shape")+"\n") + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) self.computeAreas(obj) - def computeAreas(self,obj): + def computeAreas(self, obj): """Compute the area properties of the object's shape. This function calculates and assigns the following properties to the object: @@ -992,7 +1160,7 @@ class Component(ArchIFC.IfcProduct): calculator = AreaCalculator(obj) calculator.compute() - def isStandardCase(self,obj): + def isStandardCase(self, obj): """Determine if the component is a standard case of its IFC type. Not all IFC types have a standard case. @@ -1025,7 +1193,7 @@ class Component(ArchIFC.IfcProduct): # this type has a standard case if obj.Additions or obj.Subtractions: return False - if obj.Placement.Rotation.Axis.getAngle(FreeCAD.Vector(0,0,1)) > 0.01: + if obj.Placement.Rotation.Axis.getAngle(FreeCAD.Vector(0, 0, 1)) > 0.01: # reject rotated objects return False if obj.CloneOf: @@ -1035,26 +1203,29 @@ class Component(ArchIFC.IfcProduct): # - vertically extruded # - single baseline or no baseline if (not obj.Base) or (len(obj.Base.Shape.Edges) == 1): - if hasattr(obj,"Normal"): - if obj.Normal in [FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1)]: + if hasattr(obj, "Normal"): + if obj.Normal in [FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1)]: return True - elif obj.IfcType in ["Beam","Column","Slab"]: + elif obj.IfcType in ["Beam", "Column", "Slab"]: # rules: # - have a single-wire profile or no profile # - extrusion direction is perpendicular to the profile if obj.Base and (len(obj.Base.Shape.Wires) != 1): return False - if not hasattr(obj,"Normal"): + if not hasattr(obj, "Normal"): return False - if hasattr(obj,"Tool") and obj.Tool: + if hasattr(obj, "Tool") and obj.Tool: return False - if obj.Normal == FreeCAD.Vector(0,0,0): + if obj.Normal == FreeCAD.Vector(0, 0, 0): return True elif len(obj.Base.Shape.Wires) == 1: import DraftGeomUtils + n = DraftGeomUtils.getNormal(obj.Base.Shape) if n: - if (n.getAngle(obj.Normal) < 0.01) or (abs(n.getAngle(obj.Normal)-3.14159) < 0.01): + if (n.getAngle(obj.Normal) < 0.01) or ( + abs(n.getAngle(obj.Normal) - 3.14159) < 0.01 + ): return True # TODO: Support windows and doors # rules: @@ -1064,7 +1235,7 @@ class Component(ArchIFC.IfcProduct): # - must have an IfcWindowType and IfcRelFillsElement (to be implemented in IFC exporter) return False - def getHosts(self,obj): + def getHosts(self, obj): """Return the objects that have this one as host, that is, objects with a "Host" property pointing at this object, or a "Hosts" property containing @@ -1079,10 +1250,10 @@ class Component(ArchIFC.IfcProduct): hosts = [] for link in obj.InListRecursive: - if hasattr(link,"Host"): + if hasattr(link, "Host"): if link.Host == obj and not self._objectInInternalLinkgroup(link): hosts.append(link) - elif hasattr(link,"Hosts"): + elif hasattr(link, "Hosts"): if obj in link.Hosts and not self._objectInInternalLinkgroup(link): hosts.append(link) return hosts @@ -1098,11 +1269,12 @@ class Component(ArchIFC.IfcProduct): return True else: import Part + if isinstance(getattr(obj.Base, "Shape", None), Part.Shape): return True else: - t = translate("Arch","Wrong base type") - FreeCAD.Console.PrintError(obj.Label+": "+t+"\n") + t = translate("Arch", "Wrong base type") + FreeCAD.Console.PrintError(obj.Label + ": " + t + "\n") return False def _isInternalLinkgroup(self, obj): @@ -1126,6 +1298,7 @@ class Component(ArchIFC.IfcProduct): return True return False + class AreaCalculator: """Helper class to compute vertical area, horizontal area, and perimeter length. @@ -1138,6 +1311,7 @@ class AreaCalculator: The class provides methods to validate the object's shape, identify vertical and horizontal faces, and compute the required properties. """ + def __init__(self, obj): self.obj = obj @@ -1181,12 +1355,10 @@ class AreaCalculator: from TechDraw import project try: - projectedFace = Face( - findWires(project(face, FreeCAD.Vector(0 ,0 ,1))[0].Edges)) + projectedFace = Face(findWires(project(face, FreeCAD.Vector(0, 0, 1))[0].Edges)) except OCCError: FreeCAD.Console.PrintWarning( - translate("Arch", - f"Could not project face from {self.obj.Label}\n") + translate("Arch", f"Could not project face from {self.obj.Label}\n") ) return False @@ -1197,9 +1369,11 @@ class AreaCalculator: return self.isRightAngle(angle) and isProjectedAreaZero except OCCError: FreeCAD.Console.PrintWarning( - translate("Arch", - f"Could not determine if a face from {self.obj.Label}" - " is vertical: normalAt() failed\n") + translate( + "Arch", + f"Could not determine if a face from {self.obj.Label}" + " is vertical: normalAt() failed\n", + ) ) return False @@ -1209,14 +1383,17 @@ class AreaCalculator: A face is considered horizontal if its normal vector is parallel to the Z-axis. """ from Part import OCCError + try: angle = face.normalAt(0, 0).getAngle(FreeCAD.Vector(0, 0, 1)) return not self.isRightAngle(angle) except OCCError: FreeCAD.Console.PrintWarning( - translate("Arch", - f"Could not determine if a face from {self.obj.Label}" - " is horizontal: normalAt() failed\n") + translate( + "Arch", + f"Could not determine if a face from {self.obj.Label}" + " is horizontal: normalAt() failed\n", + ) ) return False @@ -1297,7 +1474,7 @@ class AreaCalculator: "Arch", f"Error computing areas for {self.obj.Label}: unable to project or " f"make face with normal {face.normalAt(0, 0)}. " - "Area values will be reset to 0.\n" + "Area values will be reset to 0.\n", ) ) self.resetAreas() @@ -1321,6 +1498,7 @@ class AreaCalculator: if self.obj.PerimeterLength.Value != perimeterLength: self.obj.PerimeterLength = perimeterLength + class ViewProviderComponent: """A default View Provider for Component objects. @@ -1333,12 +1511,12 @@ class ViewProviderComponent: The view provider to turn into a component view provider. """ - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self self.Object = vobj.Object self.setProperties(vobj) - def setProperties(self,vobj): + def setProperties(self, vobj): """Give the component view provider its component view provider specific properties. You can learn more about properties here: @@ -1346,10 +1524,19 @@ class ViewProviderComponent: """ if not "UseMaterialColor" in vobj.PropertiesList: - vobj.addProperty("App::PropertyBool","UseMaterialColor","Component",QT_TRANSLATE_NOOP("App::Property","Use the material color as this object's shape color, if available"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "UseMaterialColor", + "Component", + QT_TRANSLATE_NOOP( + "App::Property", + "Use the material color as this object's shape color, if available", + ), + locked=True, + ) vobj.UseMaterialColor = params.get_param_arch("UseMaterialColor") - def updateData(self,obj,prop): + def updateData(self, obj, prop): """Method called when the host object has a property changed. If the object has a Material associated with it, match the view @@ -1366,12 +1553,20 @@ class ViewProviderComponent: The name of the property that has changed. """ - #print(obj.Name," : updating ",prop) + # print(obj.Name," : updating ",prop) if prop == "Material": - if obj.Material and getattr(obj.ViewObject,"UseMaterialColor",True): - if hasattr(obj.Material,"Material"): + if obj.Material and getattr(obj.ViewObject, "UseMaterialColor", True): + if hasattr(obj.Material, "Material"): if "DiffuseColor" in obj.Material.Material: - c = tuple([float(f) for f in obj.Material.Material["DiffuseColor"].strip("()").strip("[]").split(",")]) + c = tuple( + [ + float(f) + for f in obj.Material.Material["DiffuseColor"] + .strip("()") + .strip("[]") + .split(",") + ] + ) if obj.ViewObject.ShapeColor != c: obj.ViewObject.ShapeColor = c # Overwrite DiffuseColor (required if it does not match number of faces): @@ -1390,7 +1585,9 @@ class ViewProviderComponent: obj.ViewObject.update() elif prop == "CloneOf": if obj.CloneOf: - if (not getattr(obj,"Material",None)) and hasattr(obj.CloneOf.ViewObject,"DiffuseColor"): + if (not getattr(obj, "Material", None)) and hasattr( + obj.CloneOf.ViewObject, "DiffuseColor" + ): if obj.ViewObject.DiffuseColor != obj.CloneOf.ViewObject.DiffuseColor: if len(obj.CloneOf.ViewObject.DiffuseColor) > 1: obj.ViewObject.DiffuseColor = obj.CloneOf.ViewObject.DiffuseColor @@ -1410,13 +1607,14 @@ class ViewProviderComponent: """ import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + + if hasattr(self, "Object"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Component_Clone.svg" return ":/icons/Arch_Component_Tree.svg" - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): """Method called when the view provider has a property changed. If DiffuseColor changes, change DiffuseColor to copy the host object's @@ -1437,15 +1635,15 @@ class ViewProviderComponent: obj = vobj.Object if prop == "DiffuseColor": - if hasattr(obj,"CloneOf"): - if obj.CloneOf and hasattr(obj.CloneOf,"DiffuseColor"): + if hasattr(obj, "CloneOf"): + if obj.CloneOf and hasattr(obj.CloneOf, "DiffuseColor"): if len(obj.CloneOf.ViewObject.DiffuseColor) > 1: if vobj.DiffuseColor != obj.CloneOf.ViewObject.DiffuseColor: vobj.DiffuseColor = obj.CloneOf.ViewObject.DiffuseColor vobj.update() elif prop == "ShapeColor": # restore DiffuseColor after overridden by ShapeColor - if hasattr(vobj,"DiffuseColor"): + if hasattr(vobj, "DiffuseColor"): if len(vobj.DiffuseColor) > 1: d = vobj.DiffuseColor vobj.DiffuseColor = d @@ -1462,7 +1660,7 @@ class ViewProviderComponent: hostedObj.ViewObject.Visibility = vobj.Visibility return - def attach(self,vobj): + def attach(self, vobj): """Add display modes' data to the coin scenegraph. Add each display mode as a coin node, whose parent is this view @@ -1482,15 +1680,16 @@ class ViewProviderComponent: """ from pivy import coin + self.Object = vobj.Object self.hiresgroup = coin.SoSeparator() self.meshcolor = coin.SoBaseColor() self.hiresgroup.addChild(self.meshcolor) self.hiresgroup.setName("HiRes") - vobj.addDisplayMode(self.hiresgroup,"HiRes"); + vobj.addDisplayMode(self.hiresgroup, "HiRes") return - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): """Define the display modes unique to the Arch Component. Define mode HiRes, which displays the component as a mesh, intended as @@ -1507,10 +1706,10 @@ class ViewProviderComponent: List containing the names of the new display modes. """ - modes=["HiRes"] + modes = ["HiRes"] return modes - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): """Method called when the display mode changes. Called when the display mode changes, this method can be used to set @@ -1536,24 +1735,25 @@ class ViewProviderComponent: The name of the display mode the view provider has switched to. """ - if hasattr(self,"meshnode"): + if hasattr(self, "meshnode"): if self.meshnode: self.hiresgroup.removeChild(self.meshnode) del self.meshnode if mode == "HiRes": from pivy import coin + m = None - if hasattr(self,"Object"): - if hasattr(self.Object,"HiRes"): + if hasattr(self, "Object"): + if hasattr(self.Object, "HiRes"): if self.Object.HiRes: # if the file was recently loaded, the node is not present yet self.Object.HiRes.ViewObject.show() self.Object.HiRes.ViewObject.hide() m = self.Object.HiRes.ViewObject.RootNode if not m: - if hasattr(self.Object,"CloneOf"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: - if hasattr(self.Object.CloneOf,"HiRes"): + if hasattr(self.Object.CloneOf, "HiRes"): if self.Object.CloneOf.HiRes: # if the file was recently loaded, the node is not present yet self.Object.CloneOf.HiRes.ViewObject.show() @@ -1563,12 +1763,12 @@ class ViewProviderComponent: self.meshnode = m.copy() for c in self.meshnode.getChildren(): # switch the first found SoSwitch on - if isinstance(c,coin.SoSwitch): + if isinstance(c, coin.SoSwitch): num = 0 if c.getNumChildren() > 0: if c.getChild(0).getName() == "HiRes": num = 1 - #print "getting node ",num," for ",self.Object.Label + # print "getting node ",num," for ",self.Object.Label c.whichChild = num break self.hiresgroup.addChild(self.meshnode) @@ -1580,7 +1780,7 @@ class ViewProviderComponent: return None - def loads(self,state): + def loads(self, state): return None @@ -1601,27 +1801,30 @@ class ViewProviderComponent: The objects claimed as children. """ - if hasattr(self,"Object"): + if hasattr(self, "Object"): c = [] - if hasattr(self.Object,"Base"): - if not (Draft.getType(self.Object) == "Wall" and Draft.getType(self.Object.Base) == "Space"): + if hasattr(self.Object, "Base"): + if not ( + Draft.getType(self.Object) == "Wall" + and Draft.getType(self.Object.Base) == "Space" + ): c = [self.Object.Base] - if hasattr(self.Object,"Additions"): + if hasattr(self.Object, "Additions"): c.extend(self.Object.Additions) - if hasattr(self.Object,"Subtractions"): + if hasattr(self.Object, "Subtractions"): for s in self.Object.Subtractions: if Draft.getType(self.Object) == "Wall": if Draft.getType(s) == "Roof": continue c.append(s) - for link in ["Armatures","Group"]: - if hasattr(self.Object,link): - objlink = getattr(self.Object,link) + for link in ["Armatures", "Group"]: + if hasattr(self.Object, link): + objlink = getattr(self.Object, link) c.extend(objlink) - for link in ["Tool","Subvolume","Mesh","HiRes"]: - if hasattr(self.Object,link): - objlink = getattr(self.Object,link) + for link in ["Tool", "Subvolume", "Mesh", "HiRes"]: + if hasattr(self.Object, link): + objlink = getattr(self.Object, link) if objlink: c.append(objlink) if params.get_param_arch("ClaimHosted"): @@ -1662,26 +1865,25 @@ class ViewProviderComponent: The context menu already assembled prior to this method being called. """ - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return self.contextMenuAddEdit(menu) self.contextMenuAddToggleSubcomponents(menu) def contextMenuAddEdit(self, menu): - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) def contextMenuAddToggleSubcomponents(self, menu): - actionToggleSubcomponents = QtGui.QAction(QtGui.QIcon(":/icons/Arch_ToggleSubs.svg"), - translate("Arch", "Toggle Subcomponents"), - menu) - QtCore.QObject.connect(actionToggleSubcomponents, - QtCore.SIGNAL("triggered()"), - self.toggleSubcomponents) + actionToggleSubcomponents = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_ToggleSubs.svg"), + translate("Arch", "Toggle Subcomponents"), + menu, + ) + QtCore.QObject.connect( + actionToggleSubcomponents, QtCore.SIGNAL("triggered()"), self.toggleSubcomponents + ) menu.addAction(actionToggleSubcomponents) def edit(self): @@ -1690,7 +1892,7 @@ class ViewProviderComponent: def toggleSubcomponents(self): FreeCADGui.runCommand("Arch_ToggleSubs") - def areDifferentColors(self,a,b): + def areDifferentColors(self, a, b): """Check if two diffuse colors are almost the same. Parameters @@ -1713,7 +1915,7 @@ class ViewProviderComponent: return True return False - def colorize(self,obj,force=False): + def colorize(self, obj, force=False): """If an object is a clone, set it to copy the color of its parent. Only change the color of the clone if the clone and its parent have @@ -1729,9 +1931,12 @@ class ViewProviderComponent: """ if obj.CloneOf: - if (self.areDifferentColors(obj.ViewObject.DiffuseColor, - obj.CloneOf.ViewObject.DiffuseColor) - or force): + if ( + self.areDifferentColors( + obj.ViewObject.DiffuseColor, obj.CloneOf.ViewObject.DiffuseColor + ) + or force + ): obj.ViewObject.DiffuseColor = obj.CloneOf.ViewObject.DiffuseColor @@ -1768,13 +1973,13 @@ class ArchSelectionObserver: object is specified). """ - def __init__(self,origin=None,watched=None,hide=True,nextCommand=None): + def __init__(self, origin=None, watched=None, hide=True, nextCommand=None): self.origin = origin self.watched = watched self.hide = hide self.nextCommand = nextCommand - def addSelection(self,document, object, element, position): + def addSelection(self, document, object, element, position): """Method called when a selection is made on the Gui. When a nextCommand is specified, fire a Gui command when anything is @@ -1803,14 +2008,14 @@ class ArchSelectionObserver: del FreeCAD.ArchObserver elif object == self.watched.Name: if not element: - FreeCAD.Console.PrintMessage(translate("Arch","Closing Sketch edit")) + FreeCAD.Console.PrintMessage(translate("Arch", "Closing Sketch edit")) if self.hide: if self.origin: self.origin.ViewObject.Transparency = 0 self.origin.ViewObject.Selectable = True self.watched.ViewObject.hide() FreeCADGui.activateWorkbench("BIMWorkbench") - if hasattr(FreeCAD,"ArchObserver"): + if hasattr(FreeCAD, "ArchObserver"): FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver) del FreeCAD.ArchObserver if self.nextCommand: @@ -1818,6 +2023,7 @@ class ArchSelectionObserver: FreeCADGui.Selection.addSelection(self.watched) FreeCADGui.runCommand(self.nextCommand) + class SelectionTaskPanel: """A simple TaskPanel to wait for a selection. @@ -1835,7 +2041,7 @@ class SelectionTaskPanel: def reject(self): """The method run when the user selects the cancel button.""" - if hasattr(FreeCAD,"ArchObserver"): + if hasattr(FreeCAD, "ArchObserver"): FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver) del FreeCAD.ArchObserver return True @@ -1853,7 +2059,17 @@ class ComponentTaskPanel: # the categories are shown only if they are not empty. self.obj = None - self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes","Fixtures","Group","Hosts"] + self.attribs = [ + "Base", + "Additions", + "Subtractions", + "Objects", + "Components", + "Axes", + "Fixtures", + "Group", + "Hosts", + ] self.baseform = QtGui.QWidget() self.baseform.setObjectName("TaskPanel") self.grid = QtGui.QGridLayout(self.baseform) @@ -1896,17 +2112,30 @@ class ComponentTaskPanel: self.classButton.hide() else: import os + # the BIM_Classification command needs to be added before it can be used if not "BIM_Classification" in FreeCADGui.listCommands(): FreeCADGui.activateWorkbench("BIMWorkbench") - self.classButton.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(BimClassification.__file__),"icons","BIM_Classification.svg"))) + self.classButton.setIcon( + QtGui.QIcon( + os.path.join( + os.path.dirname(BimClassification.__file__), + "icons", + "BIM_Classification.svg", + ) + ) + ) QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) QtCore.QObject.connect(self.ifcButton, QtCore.SIGNAL("clicked()"), self.editIfcProperties) QtCore.QObject.connect(self.classButton, QtCore.SIGNAL("clicked()"), self.editClass) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem *,int)"), self.editObject) + QtCore.QObject.connect( + self.tree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check + ) + QtCore.QObject.connect( + self.tree, QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem *,int)"), self.editObject + ) self.update() def isAllowedAlterSelection(self): @@ -1938,7 +2167,7 @@ class ComponentTaskPanel: return QtGui.QDialogButtonBox.Ok - def check(self,wid,col): + def check(self, wid, col): """This method is run as the callback when the user selects an item in the tree. Enable and disable the add and remove buttons depending on what the @@ -1964,13 +2193,13 @@ class ComponentTaskPanel: if self.obj: sel = FreeCADGui.Selection.getSelection() if sel: - if not(self.obj in sel): + if not (self.obj in sel): self.addButton.setEnabled(True) else: self.delButton.setEnabled(True) self.addButton.setEnabled(False) - def getIcon(self,obj): + def getIcon(self, obj): """Get the path to the icons, of the items that fill the tree widget. Parameters @@ -1979,8 +2208,8 @@ class ComponentTaskPanel: The object being edited. """ - if hasattr(obj.ViewObject,"Proxy"): - if hasattr(obj.ViewObject.Proxy,"getIcon"): + if hasattr(obj.ViewObject, "Proxy"): + if hasattr(obj.ViewObject.Proxy, "getIcon"): return QtGui.QIcon(obj.ViewObject.Proxy.getIcon()) elif obj.isDerivedFrom("Sketcher::SketchObject"): return QtGui.QIcon(":/icons/Sketcher_Sketch.svg") @@ -2005,32 +2234,32 @@ class ComponentTaskPanel: self.tree.clear() dirIcon = QtGui.QApplication.style().standardIcon(QtGui.QStyle.SP_DirIcon) for a in self.attribs: - setattr(self,"tree"+a,QtGui.QTreeWidgetItem(self.tree)) - c = getattr(self,"tree"+a) - c.setIcon(0,dirIcon) + setattr(self, "tree" + a, QtGui.QTreeWidgetItem(self.tree)) + c = getattr(self, "tree" + a) + c.setIcon(0, dirIcon) c.ChildIndicatorPolicy = 2 if self.obj: - if not hasattr(self.obj,a): - c.setHidden(True) + if not hasattr(self.obj, a): + c.setHidden(True) else: c.setHidden(True) if self.obj: for attrib in self.attribs: - if hasattr(self.obj,attrib): - Oattrib = getattr(self.obj,attrib) - Tattrib = getattr(self,"tree"+attrib) + if hasattr(self.obj, attrib): + Oattrib = getattr(self.obj, attrib) + Tattrib = getattr(self, "tree" + attrib) if Oattrib: if attrib == "Base": Oattrib = [Oattrib] for o in Oattrib: item = QtGui.QTreeWidgetItem() - item.setText(0,o.Label) - item.setToolTip(0,o.Name) - item.setIcon(0,self.getIcon(o)) + item.setText(0, o.Label) + item.setToolTip(0, o.Name) + item.setIcon(0, self.getIcon(o)) Tattrib.addChild(item) self.tree.expandItem(Tattrib) - if hasattr(self.obj,"IfcProperties"): - if isinstance(self.obj.IfcProperties,dict): + if hasattr(self.obj, "IfcProperties"): + if isinstance(self.obj.IfcProperties, dict): self.ifcButton.show() self.retranslateUi(self.baseform) @@ -2048,10 +2277,10 @@ class ComponentTaskPanel: if it: mod = None for a in self.attribs: - if it.text(0) == getattr(self,"tree"+a).text(0): + if it.text(0) == getattr(self, "tree" + a).text(0): mod = a for o in FreeCADGui.Selection.getSelection(): - addToComponent(self.obj,o,mod) + addToComponent(self.obj, o, mod) self.update() def removeElement(self): @@ -2066,7 +2295,7 @@ class ComponentTaskPanel: it = self.tree.currentItem() if it: comp = FreeCAD.ActiveDocument.getObject(str(it.toolTip(0))) - removeFromComponent(self.obj,comp) + removeFromComponent(self.obj, comp) self.update() def accept(self): @@ -2079,7 +2308,7 @@ class ComponentTaskPanel: FreeCADGui.ActiveDocument.resetEdit() return True - def editObject(self,wid,col): + def editObject(self, wid, col): """This method is run when the user double clicks on an item in the tree widget. If the item in the tree has a corresponding object in the document, @@ -2103,27 +2332,26 @@ class ComponentTaskPanel: self.accept() if obj.isDerivedFrom("Sketcher::SketchObject"): FreeCADGui.activateWorkbench("SketcherWorkbench") - FreeCAD.ArchObserver = ArchSelectionObserver(self.obj,obj) + FreeCAD.ArchObserver = ArchSelectionObserver(self.obj, obj) FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) - FreeCADGui.ActiveDocument.setEdit(obj.Name,0) + FreeCADGui.ActiveDocument.setEdit(obj.Name, 0) def retranslateUi(self, TaskPanel): - """Add the text of the task panel, in translated form. - """ + """Add the text of the task panel, in translated form.""" self.baseform.setWindowTitle(QtGui.QApplication.translate("Arch", "Component", None)) self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None)) self.addButton.setText(QtGui.QApplication.translate("Arch", "Add", None)) self.title.setText(QtGui.QApplication.translate("Arch", "Components of This Object", None)) - self.treeBase.setText(0,QtGui.QApplication.translate("Arch", "Base component", None)) - self.treeAdditions.setText(0,QtGui.QApplication.translate("Arch", "Additions", None)) - self.treeSubtractions.setText(0,QtGui.QApplication.translate("Arch", "Subtractions", None)) - self.treeObjects.setText(0,QtGui.QApplication.translate("Arch", "Objects", None)) - self.treeAxes.setText(0,QtGui.QApplication.translate("Arch", "Axes", None)) - self.treeComponents.setText(0,QtGui.QApplication.translate("Arch", "Components", None)) - self.treeFixtures.setText(0,QtGui.QApplication.translate("Arch", "Fixtures", None)) - self.treeGroup.setText(0,QtGui.QApplication.translate("Arch", "Group", None)) - self.treeHosts.setText(0,QtGui.QApplication.translate("Arch", "Hosts", None)) + self.treeBase.setText(0, QtGui.QApplication.translate("Arch", "Base component", None)) + self.treeAdditions.setText(0, QtGui.QApplication.translate("Arch", "Additions", None)) + self.treeSubtractions.setText(0, QtGui.QApplication.translate("Arch", "Subtractions", None)) + self.treeObjects.setText(0, QtGui.QApplication.translate("Arch", "Objects", None)) + self.treeAxes.setText(0, QtGui.QApplication.translate("Arch", "Axes", None)) + self.treeComponents.setText(0, QtGui.QApplication.translate("Arch", "Components", None)) + self.treeFixtures.setText(0, QtGui.QApplication.translate("Arch", "Fixtures", None)) + self.treeGroup.setText(0, QtGui.QApplication.translate("Arch", "Group", None)) + self.treeHosts.setText(0, QtGui.QApplication.translate("Arch", "Hosts", None)) self.ifcButton.setText(QtGui.QApplication.translate("Arch", "Edit IFC Properties", None)) self.classButton.setText(QtGui.QApplication.translate("Arch", "Edit Standard Code", None)) @@ -2137,15 +2365,15 @@ class ComponentTaskPanel: the buttons and other interactions, and show it. """ - if hasattr(self,"ifcEditor"): + if hasattr(self, "ifcEditor"): if self.ifcEditor: self.ifcEditor.hide() del self.ifcEditor if not self.obj: return - if not hasattr(self.obj,"IfcProperties"): + if not hasattr(self.obj, "IfcProperties"): return - if not isinstance(self.obj.IfcProperties,dict): + if not isinstance(self.obj.IfcProperties, dict): return import csv import os @@ -2154,34 +2382,56 @@ class ComponentTaskPanel: # get presets self.ptypes = list(ArchIFCSchema.IfcTypes) - self.plabels = [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in self.ptypes] + self.plabels = [ + "".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:] for t in self.ptypes + ] self.psetdefs = {} - psetspath = os.path.join(FreeCAD.getResourceDir(),"Mod","Arch","Presets","pset_definitions.csv") + psetspath = os.path.join( + FreeCAD.getResourceDir(), "Mod", "Arch", "Presets", "pset_definitions.csv" + ) if os.path.exists(psetspath): with open(psetspath, "r") as csvfile: - reader = csv.reader(csvfile, delimiter=';') + reader = csv.reader(csvfile, delimiter=";") for row in reader: self.psetdefs[row[0]] = row[1:] - self.psetkeys = [''.join(map(lambda x: x if x.islower() else " "+x, t[5:]))[1:] for t in self.psetdefs.keys()] + self.psetkeys = [ + "".join(map(lambda x: x if x.islower() else " " + x, t[5:]))[1:] + for t in self.psetdefs.keys() + ] self.psetkeys.sort() self.ifcEditor = FreeCADGui.PySideUic.loadUi(":/ui/dialogIfcPropertiesRedux.ui") # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() - self.ifcEditor.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.ifcEditor.rect().center()) + self.ifcEditor.move( + mw.frameGeometry().topLeft() + mw.rect().center() - self.ifcEditor.rect().center() + ) self.ifcModel = QtGui.QStandardItemModel() self.ifcEditor.treeProperties.setModel(self.ifcModel) - #self.ifcEditor.treeProperties.setDragDropMode(QtGui.QAbstractItemView.InternalMove) + # self.ifcEditor.treeProperties.setDragDropMode(QtGui.QAbstractItemView.InternalMove) self.ifcEditor.treeProperties.setUniformRowHeights(True) - self.ifcEditor.treeProperties.setItemDelegate(IfcEditorDelegate(dialog=self,ptypes=self.ptypes,plabels=self.plabels)) - self.ifcModel.setHorizontalHeaderLabels([QtGui.QApplication.translate("Arch", "Property", None), - QtGui.QApplication.translate("Arch", "Type", None), - QtGui.QApplication.translate("Arch", "Value", None)]) + self.ifcEditor.treeProperties.setItemDelegate( + IfcEditorDelegate(dialog=self, ptypes=self.ptypes, plabels=self.plabels) + ) + self.ifcModel.setHorizontalHeaderLabels( + [ + QtGui.QApplication.translate("Arch", "Property", None), + QtGui.QApplication.translate("Arch", "Type", None), + QtGui.QApplication.translate("Arch", "Value", None), + ] + ) # set combos - self.ifcEditor.comboProperty.addItems([QtGui.QApplication.translate("Arch", "Add property", None)]+self.plabels) - self.ifcEditor.comboPset.addItems([QtGui.QApplication.translate("Arch", "Add property set", None), - QtGui.QApplication.translate("Arch", "New...", None)]+self.psetkeys) + self.ifcEditor.comboProperty.addItems( + [QtGui.QApplication.translate("Arch", "Add property", None)] + self.plabels + ) + self.ifcEditor.comboPset.addItems( + [ + QtGui.QApplication.translate("Arch", "Add property set", None), + QtGui.QApplication.translate("Arch", "New...", None), + ] + + self.psetkeys + ) # set UUID if "IfcUID" in self.obj.IfcData: @@ -2189,7 +2439,7 @@ class ComponentTaskPanel: # fill the tree psets = {} - for pname,value in self.obj.IfcProperties.items(): + for pname, value in self.obj.IfcProperties.items(): # properties in IfcProperties dict are stored as "key":"pset;;type;;value" or "key":"type;;value" value = value.split(";;") if len(value) == 3: @@ -2205,12 +2455,12 @@ class ComponentTaskPanel: plabel = ptype if ptype in self.ptypes: plabel = self.plabels[self.ptypes.index(ptype)] - psets.setdefault(pset,[]).append([pname,plabel,pvalue]) - for pset,plists in psets.items(): + psets.setdefault(pset, []).append([pname, plabel, pvalue]) + for pset, plists in psets.items(): top = QtGui.QStandardItem(pset) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.ifcModel.appendRow([top,QtGui.QStandardItem(),QtGui.QStandardItem()]) + self.ifcModel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) for plist in plists: it1 = QtGui.QStandardItem(plist[0]) it1.setDropEnabled(False) @@ -2218,21 +2468,31 @@ class ComponentTaskPanel: it2.setDropEnabled(False) it3 = QtGui.QStandardItem(plist[2]) it3.setDropEnabled(False) - top.appendRow([it1,it2,it3]) + top.appendRow([it1, it2, it3]) top.sortChildren(0) # span top levels idx = self.ifcModel.invisibleRootItem().index() for i in range(self.ifcModel.rowCount()): - if self.ifcModel.item(i,0).hasChildren(): + if self.ifcModel.item(i, 0).hasChildren(): self.ifcEditor.treeProperties.setFirstColumnSpanned(i, idx, True) self.ifcEditor.treeProperties.expandAll() # Add callbacks - QtCore.QObject.connect(self.ifcEditor.buttonBox, QtCore.SIGNAL("accepted()"), self.acceptIfcProperties) - QtCore.QObject.connect(self.ifcEditor.comboProperty, QtCore.SIGNAL("currentIndexChanged(int)"), self.addIfcProperty) - QtCore.QObject.connect(self.ifcEditor.comboPset, QtCore.SIGNAL("currentIndexChanged(int)"), self.addIfcPset) - QtCore.QObject.connect(self.ifcEditor.buttonDelete, QtCore.SIGNAL("clicked()"), self.removeIfcProperty) + QtCore.QObject.connect( + self.ifcEditor.buttonBox, QtCore.SIGNAL("accepted()"), self.acceptIfcProperties + ) + QtCore.QObject.connect( + self.ifcEditor.comboProperty, + QtCore.SIGNAL("currentIndexChanged(int)"), + self.addIfcProperty, + ) + QtCore.QObject.connect( + self.ifcEditor.comboPset, QtCore.SIGNAL("currentIndexChanged(int)"), self.addIfcPset + ) + QtCore.QObject.connect( + self.ifcEditor.buttonDelete, QtCore.SIGNAL("clicked()"), self.removeIfcProperty + ) self.ifcEditor.treeProperties.setSortingEnabled(True) # set checkboxes @@ -2250,19 +2510,19 @@ class ComponentTaskPanel: the object's .IfcData to match the editor's items. """ - if hasattr(self,"ifcEditor") and self.ifcEditor: + if hasattr(self, "ifcEditor") and self.ifcEditor: self.ifcEditor.hide() ifcdict = {} for row in range(self.ifcModel.rowCount()): - pset = self.ifcModel.item(row,0).text() - if self.ifcModel.item(row,0).hasChildren(): - for childrow in range(self.ifcModel.item(row,0).rowCount()): - prop = self.ifcModel.item(row,0).child(childrow,0).text() - ptype = self.ifcModel.item(row,0).child(childrow,1).text() + pset = self.ifcModel.item(row, 0).text() + if self.ifcModel.item(row, 0).hasChildren(): + for childrow in range(self.ifcModel.item(row, 0).rowCount()): + prop = self.ifcModel.item(row, 0).child(childrow, 0).text() + ptype = self.ifcModel.item(row, 0).child(childrow, 1).text() if not ptype.startswith("Ifc"): ptype = self.ptypes[self.plabels.index(ptype)] - pvalue = self.ifcModel.item(row,0).child(childrow,2).text() - ifcdict[prop] = pset+";;"+ptype+";;"+pvalue + pvalue = self.ifcModel.item(row, 0).child(childrow, 2).text() + ifcdict[prop] = pset + ";;" + ptype + ";;" + pvalue ifcData = self.obj.IfcData ifcData["IfcUID"] = self.ifcEditor.labelUUID.text() ifcData["FlagForceBrep"] = str(self.ifcEditor.checkBrep.isChecked()) @@ -2276,7 +2536,7 @@ class ComponentTaskPanel: FreeCAD.ActiveDocument.commitTransaction() del self.ifcEditor - def addIfcProperty(self,idx=0,pset=None,prop=None,ptype=None): + def addIfcProperty(self, idx=0, pset=None, prop=None, ptype=None): """Add an IFC property to the object, within the IFC editor. This method runs as a callback when the user selects an option in the @@ -2309,7 +2569,7 @@ class ComponentTaskPanel: idx parameter. """ - if hasattr(self,"ifcEditor") and self.ifcEditor: + if hasattr(self, "ifcEditor") and self.ifcEditor: if not pset: sel = self.ifcEditor.treeProperties.selectedIndexes() if sel: @@ -2321,7 +2581,7 @@ class ComponentTaskPanel: prop = QtGui.QApplication.translate("Arch", "New property", None) if not ptype: if idx > 0: - ptype = self.plabels[idx-1] + ptype = self.plabels[idx - 1] if prop and ptype: if ptype in self.ptypes: ptype = self.plabels[self.ptypes.index(ptype)] @@ -2331,11 +2591,11 @@ class ComponentTaskPanel: it2.setDropEnabled(False) it3 = QtGui.QStandardItem() it3.setDropEnabled(False) - pset.appendRow([it1,it2,it3]) + pset.appendRow([it1, it2, it3]) if idx != 0: self.ifcEditor.comboProperty.setCurrentIndex(0) - def addIfcPset(self,idx=0): + def addIfcPset(self, idx=0): """Add an IFC property set to the object, within the IFC editor. This method runs as a callback when the user selects a property set @@ -2352,27 +2612,33 @@ class ComponentTaskPanel: dropdown. """ - if hasattr(self,"ifcEditor") and self.ifcEditor: + if hasattr(self, "ifcEditor") and self.ifcEditor: if idx == 1: - top = QtGui.QStandardItem(QtGui.QApplication.translate("Arch", "New property set", None)) + top = QtGui.QStandardItem( + QtGui.QApplication.translate("Arch", "New property set", None) + ) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.ifcModel.appendRow([top,QtGui.QStandardItem(),QtGui.QStandardItem()]) + self.ifcModel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) elif idx > 1: - psetlabel = self.psetkeys[idx-2] - psetdef = "Pset_"+psetlabel.replace(" ","") + psetlabel = self.psetkeys[idx - 2] + psetdef = "Pset_" + psetlabel.replace(" ", "") if psetdef in self.psetdefs: top = QtGui.QStandardItem(psetdef) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.ifcModel.appendRow([top,QtGui.QStandardItem(),QtGui.QStandardItem()]) - for i in range(0,len(self.psetdefs[psetdef]),2): - self.addIfcProperty(pset=top,prop=self.psetdefs[psetdef][i],ptype=self.psetdefs[psetdef][i+1]) + self.ifcModel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) + for i in range(0, len(self.psetdefs[psetdef]), 2): + self.addIfcProperty( + pset=top, + prop=self.psetdefs[psetdef][i], + ptype=self.psetdefs[psetdef][i + 1], + ) if idx != 0: # span top levels idx = self.ifcModel.invisibleRootItem().index() for i in range(self.ifcModel.rowCount()): - if self.ifcModel.item(i,0).hasChildren(): + if self.ifcModel.item(i, 0).hasChildren(): self.ifcEditor.treeProperties.setFirstColumnSpanned(i, idx, True) self.ifcEditor.treeProperties.expandAll() self.ifcEditor.comboPset.setCurrentIndex(0) @@ -2387,7 +2653,7 @@ class ComponentTaskPanel: delete the children properties as well. """ - if hasattr(self,"ifcEditor") and self.ifcEditor: + if hasattr(self, "ifcEditor") and self.ifcEditor: sel = self.ifcEditor.treeProperties.selectedIndexes() if sel: if self.ifcModel.itemFromIndex(sel[0]).toolTip() == "PropertySet": @@ -2407,6 +2673,7 @@ class ComponentTaskPanel: FreeCADGui.Selection.addSelection(self.obj) FreeCADGui.runCommand("BIM_Classification") + if FreeCAD.GuiUp: class IfcEditorDelegate(QtGui.QStyledItemDelegate): @@ -2424,14 +2691,13 @@ if FreeCAD.GuiUp: A list of the human readable names of IFC property types. """ - def __init__(self, parent=None, dialog=None, ptypes=[], plabels=[], *args): self.dialog = dialog QtGui.QStyledItemDelegate.__init__(self, parent, *args) self.ptypes = ptypes self.plabels = plabels - def createEditor(self,parent,option,index): + def createEditor(self, parent, option, index): """Return the widget used to change data. Return a text line editor if editing the property name. Return a @@ -2454,26 +2720,26 @@ if FreeCAD.GuiUp: The editor widget this method has created. """ - if index.column() == 0: # property name + if index.column() == 0: # property name editor = QtGui.QLineEdit(parent) - elif index.column() == 1: # property type + elif index.column() == 1: # property type editor = QtGui.QComboBox(parent) - else: # property value - ptype = index.sibling(index.row(),1).data() + else: # property value + ptype = index.sibling(index.row(), 1).data() if "Integer" in ptype: editor = QtGui.QSpinBox(parent) elif "Real" in ptype: editor = QtGui.QDoubleSpinBox(parent) - editor.setDecimals(params.get_param("Decimals",path="Units")) + editor.setDecimals(params.get_param("Decimals", path="Units")) elif ("Boolean" in ptype) or ("Logical" in ptype): editor = QtGui.QComboBox(parent) - editor.addItems(["True","False"]) + editor.addItems(["True", "False"]) elif "Measure" in ptype: editor = FreeCADGui.UiLoader().createWidget("Gui::InputField") editor.setParent(parent) else: editor = QtGui.QLineEdit(parent) - editor.setObjectName("editor_"+ptype) + editor.setObjectName("editor_" + ptype) return editor def setEditorData(self, editor, index): @@ -2511,7 +2777,7 @@ if FreeCAD.GuiUp: editor.setValue(0) elif ("Boolean" in editor.objectName()) or ("Logical" in editor.objectName()): try: - editor.setCurrentIndex(["true","false"].index(index.data().lower())) + editor.setCurrentIndex(["true", "false"].index(index.data().lower())) except Exception: editor.setCurrentIndex(1) elif "Measure" in editor.objectName(): @@ -2536,19 +2802,19 @@ if FreeCAD.GuiUp: """ if index.column() == 0: - model.setData(index,editor.text()) + model.setData(index, editor.text()) elif index.column() == 1: if editor.currentIndex() > -1: idx = editor.currentIndex() data = self.plabels[idx] - model.setData(index,data) + model.setData(index, data) else: if ("Integer" in editor.objectName()) or ("Real" in editor.objectName()): - model.setData(index,str(editor.value())) + model.setData(index, str(editor.value())) elif ("Boolean" in editor.objectName()) or ("Logical" in editor.objectName()): - model.setData(index,editor.currentText()) + model.setData(index, editor.currentText()) elif "Measure" in editor.objectName(): - model.setData(index,editor.property("text")) + model.setData(index, editor.property("text")) else: - model.setData(index,editor.text()) + model.setData(index, editor.text()) self.dialog.update() diff --git a/src/Mod/BIM/ArchCurtainWall.py b/src/Mod/BIM/ArchCurtainWall.py index 90ad70fdb8..0cf765b68f 100644 --- a/src/Mod/BIM/ArchCurtainWall.py +++ b/src/Mod/BIM/ArchCurtainWall.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Curtain Wall" +__title__ = "FreeCAD Arch Curtain Wall" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchCurtainWall # \ingroup ARCH @@ -65,140 +65,306 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -ANGLETOLERANCE = 0.67 # vectors with angles below this are considered going in same dir +ANGLETOLERANCE = 0.67 # vectors with angles below this are considered going in same dir class CurtainWall(ArchComponent.Component): - - "The curtain wall object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "CurtainWall" self.setProperties(obj) obj.IfcType = "Curtain Wall" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList vsize = 50 hsize = 50 if not "Host" in pl: - obj.addProperty("App::PropertyLink","Host","CurtainWall",QT_TRANSLATE_NOOP("App::Property","An optional host object for this curtain wall"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Host", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "An optional host object for this curtain wall"), + locked=True, + ) if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The height of the curtain wall, if based on an edge"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", "The height of the curtain wall, if based on an edge" + ), + locked=True, + ) obj.Height = params.get_param_arch("WallHeight") if not "VerticalMullionNumber" in pl: - obj.addProperty("App::PropertyInteger","VerticalMullionNumber","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of vertical mullions"), locked=True) - obj.setEditorMode("VerticalMullionNumber",1) + obj.addProperty( + "App::PropertyInteger", + "VerticalMullionNumber", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "The number of vertical mullions"), + locked=True, + ) + obj.setEditorMode("VerticalMullionNumber", 1) if not "VerticalMullionAlignment" in pl: - obj.addProperty("App::PropertyBool","VerticalMullionAlignment","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","If the profile of the vertical mullions get aligned with the surface or not"), locked=True) + obj.addProperty( + "App::PropertyBool", + "VerticalMullionAlignment", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "If the profile of the vertical mullions get aligned with the surface or not", + ), + locked=True, + ) if not "VerticalSections" in pl: - obj.addProperty("App::PropertyInteger","VerticalSections","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of vertical sections of this curtain wall"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "VerticalSections", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", "The number of vertical sections of this curtain wall" + ), + locked=True, + ) obj.VerticalSections = 1 if "VerticalMullionSize" in pl: # obsolete vsize = obj.VerticalMullionSize.Value obj.removeProperty("VerticalMullionSize") if not "VerticalMullionHeight" in pl: - obj.addProperty("App::PropertyLength","VerticalMullionHeight","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The height of the vertical mullions profile, if no profile is used"), locked=True) + obj.addProperty( + "App::PropertyLength", + "VerticalMullionHeight", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The height of the vertical mullions profile, if no profile is used", + ), + locked=True, + ) obj.VerticalMullionHeight = vsize if not "VerticalMullionWidth" in pl: - obj.addProperty("App::PropertyLength","VerticalMullionWidth","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The width of the vertical mullions profile, if no profile is used"), locked=True) + obj.addProperty( + "App::PropertyLength", + "VerticalMullionWidth", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The width of the vertical mullions profile, if no profile is used", + ), + locked=True, + ) obj.VerticalMullionWidth = vsize if not "VerticalMullionProfile" in pl: - obj.addProperty("App::PropertyLink","VerticalMullionProfile","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","A profile for vertical mullions (disables vertical mullion size)"), locked=True) + obj.addProperty( + "App::PropertyLink", + "VerticalMullionProfile", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "A profile for vertical mullions (disables vertical mullion size)", + ), + locked=True, + ) if not "HorizontalMullionNumber" in pl: - obj.addProperty("App::PropertyInteger","HorizontalMullionNumber","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of horizontal mullions"), locked=True) - obj.setEditorMode("HorizontalMullionNumber",1) + obj.addProperty( + "App::PropertyInteger", + "HorizontalMullionNumber", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "The number of horizontal mullions"), + locked=True, + ) + obj.setEditorMode("HorizontalMullionNumber", 1) if not "HorizontalMullionAlignment" in pl: - obj.addProperty("App::PropertyBool","HorizontalMullionAlignment","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","If the profile of the horizontal mullions gets aligned with the surface or not"), locked=True) + obj.addProperty( + "App::PropertyBool", + "HorizontalMullionAlignment", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "If the profile of the horizontal mullions gets aligned with the surface or not", + ), + locked=True, + ) if not "HorizontalSections" in pl: - obj.addProperty("App::PropertyInteger","HorizontalSections","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of horizontal sections of this curtain wall"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "HorizontalSections", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", "The number of horizontal sections of this curtain wall" + ), + locked=True, + ) obj.HorizontalSections = 1 if "HorizontalMullionSize" in pl: # obsolete hsize = obj.HorizontalMullionSize.Value obj.removeProperty("HorizontalMullionSize") if not "HorizontalMullionHeight" in pl: - obj.addProperty("App::PropertyLength","HorizontalMullionHeight","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The height of the horizontal mullions profile, if no profile is used"), locked=True) + obj.addProperty( + "App::PropertyLength", + "HorizontalMullionHeight", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The height of the horizontal mullions profile, if no profile is used", + ), + locked=True, + ) obj.HorizontalMullionHeight = hsize if not "HorizontalMullionWidth" in pl: - obj.addProperty("App::PropertyLength","HorizontalMullionWidth","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The width of the horizontal mullions profile, if no profile is used"), locked=True) + obj.addProperty( + "App::PropertyLength", + "HorizontalMullionWidth", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The width of the horizontal mullions profile, if no profile is used", + ), + locked=True, + ) obj.HorizontalMullionWidth = hsize if not "HorizontalMullionProfile" in pl: - obj.addProperty("App::PropertyLink","HorizontalMullionProfile","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","A profile for horizontal mullions (disables horizontal mullion size)"), locked=True) + obj.addProperty( + "App::PropertyLink", + "HorizontalMullionProfile", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "A profile for horizontal mullions (disables horizontal mullion size)", + ), + locked=True, + ) if not "DiagonalMullionNumber" in pl: - obj.addProperty("App::PropertyInteger","DiagonalMullionNumber","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of diagonal mullions"), locked=True) - obj.setEditorMode("DiagonalMullionNumber",1) + obj.addProperty( + "App::PropertyInteger", + "DiagonalMullionNumber", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "The number of diagonal mullions"), + locked=True, + ) + obj.setEditorMode("DiagonalMullionNumber", 1) if not "DiagonalMullionSize" in pl: - obj.addProperty("App::PropertyLength","DiagonalMullionSize","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The size of the diagonal mullions, if any, if no profile is used"), locked=True) + obj.addProperty( + "App::PropertyLength", + "DiagonalMullionSize", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The size of the diagonal mullions, if any, if no profile is used", + ), + locked=True, + ) obj.DiagonalMullionSize = 50 if not "DiagonalMullionProfile" in pl: - obj.addProperty("App::PropertyLink","DiagonalMullionProfile","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","A profile for diagonal mullions, if any (disables horizontal mullion size)"), locked=True) + obj.addProperty( + "App::PropertyLink", + "DiagonalMullionProfile", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "A profile for diagonal mullions, if any (disables horizontal mullion size)", + ), + locked=True, + ) if not "PanelNumber" in pl: - obj.addProperty("App::PropertyInteger","PanelNumber","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The number of panels"), locked=True) - obj.setEditorMode("PanelNumber",1) + obj.addProperty( + "App::PropertyInteger", + "PanelNumber", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "The number of panels"), + locked=True, + ) + obj.setEditorMode("PanelNumber", 1) if not "PanelThickness" in pl: - obj.addProperty("App::PropertyLength","PanelThickness","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The thickness of the panels"), locked=True) + obj.addProperty( + "App::PropertyLength", + "PanelThickness", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the panels"), + locked=True, + ) obj.PanelThickness = 20 if not "SwapHorizontalVertical" in pl: - obj.addProperty("App::PropertyBool","SwapHorizontalVertical","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","Swaps horizontal and vertical lines"), locked=True) + obj.addProperty( + "App::PropertyBool", + "SwapHorizontalVertical", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "Swaps horizontal and vertical lines"), + locked=True, + ) if not "Refine" in pl: - obj.addProperty("App::PropertyBool","Refine","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","Perform subtractions between components so none overlap"), locked=True) + obj.addProperty( + "App::PropertyBool", + "Refine", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", "Perform subtractions between components so none overlap" + ), + locked=True, + ) if not "CenterProfiles" in pl: - obj.addProperty("App::PropertyBool","CenterProfiles","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","Centers the profile over the edges or not"), locked=True) + obj.addProperty( + "App::PropertyBool", + "CenterProfiles", + "CurtainWall", + QT_TRANSLATE_NOOP("App::Property", "Centers the profile over the edges or not"), + locked=True, + ) obj.CenterProfiles = True if not "VerticalDirection" in pl: - obj.addProperty("App::PropertyVector","VerticalDirection","CurtainWall", - QT_TRANSLATE_NOOP("App::Property","The vertical direction reference to be used by this object to deduce vertical/horizontal directions. Keep it close to the actual vertical direction of your curtain wall"), locked=True) - obj.VerticalDirection = FreeCAD.Vector(0,0,1) + obj.addProperty( + "App::PropertyVector", + "VerticalDirection", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "The vertical direction reference to be used by this object to deduce vertical/horizontal directions. Keep it close to the actual vertical direction of your curtain wall", + ), + locked=True, + ) + obj.VerticalDirection = FreeCAD.Vector(0, 0, 1) if not "OverrideEdges" in pl: # PropertyStringList - obj.addProperty("App::PropertyStringList","OverrideEdges","CurtainWall",QT_TRANSLATE_NOOP("App::Property","Input are index numbers of edges of Base ArchSketch/Sketch geometries (in Edit mode). Selected edges are used to create the shape of this Arch Curtain Wall (instead of using all edges by default). [ENHANCED by ArchSketch] GUI 'Edit Curtain Wall' Tool is provided in external Add-on ('SketchArch') to let users to select the edges interactively. 'Toponaming-Tolerant' if ArchSketch is used in Base (and SketchArch Add-on is installed). Warning : Not 'Toponaming-Tolerant' if just Sketch is used. Property is ignored if Base ArchSketch provided the selected edges."), locked=True) + obj.addProperty( + "App::PropertyStringList", + "OverrideEdges", + "CurtainWall", + QT_TRANSLATE_NOOP( + "App::Property", + "Input are index numbers of edges of Base ArchSketch/Sketch geometries (in Edit mode). Selected edges are used to create the shape of this Arch Curtain Wall (instead of using all edges by default). [ENHANCED by ArchSketch] GUI 'Edit Curtain Wall' Tool is provided in external Add-on ('SketchArch') to let users to select the edges interactively. 'Toponaming-Tolerant' if ArchSketch is used in Base (and SketchArch Add-on is installed). Warning : Not 'Toponaming-Tolerant' if just Sketch is used. Property is ignored if Base ArchSketch provided the selected edges.", + ), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "CurtainWall" - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): - ArchComponent.Component.onChanged(self,obj,prop) + ArchComponent.Component.onChanged(self, obj, prop) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -212,22 +378,22 @@ class CurtainWall(ArchComponent.Component): # test properties if not obj.Base: - FreeCAD.Console.PrintLog(obj.Label+": no base\n") + FreeCAD.Console.PrintLog(obj.Label + ": no base\n") return - if not hasattr(obj.Base,"Shape"): - FreeCAD.Console.PrintLog(obj.Label+": invalid base\n") + if not hasattr(obj.Base, "Shape"): + FreeCAD.Console.PrintLog(obj.Label + ": invalid base\n") return if obj.VerticalMullionProfile: - if not hasattr(obj.VerticalMullionProfile,"Shape"): - FreeCAD.Console.PrintLog(obj.Label+": invalid vertical mullion profile\n") + if not hasattr(obj.VerticalMullionProfile, "Shape"): + FreeCAD.Console.PrintLog(obj.Label + ": invalid vertical mullion profile\n") return if obj.HorizontalMullionProfile: - if not hasattr(obj.HorizontalMullionProfile,"Shape"): - FreeCAD.Console.PrintLog(obj.Label+": invalid horizontal mullion profile\n") + if not hasattr(obj.HorizontalMullionProfile, "Shape"): + FreeCAD.Console.PrintLog(obj.Label + ": invalid horizontal mullion profile\n") return if obj.DiagonalMullionProfile: - if not hasattr(obj.DiagonalMullionProfile,"Shape"): - FreeCAD.Console.PrintLog(obj.Label+": invalid diagonal mullion profile\n") + if not hasattr(obj.DiagonalMullionProfile, "Shape"): + FreeCAD.Console.PrintLog(obj.Label + ": invalid diagonal mullion profile\n") return facets = [] @@ -241,23 +407,30 @@ class CurtainWall(ArchComponent.Component): ext = FreeCAD.Vector(obj.VerticalDirection) ext.normalize() ext = ext.multiply(obj.Height.Value) - if hasattr(obj.Base, 'Proxy'): - if hasattr(obj.Base.Proxy, 'getCurtainWallBaseShapeEdgesInfo'): - curtainWallBaseShapeEdges = obj.Base.Proxy.getCurtainWallBaseShapeEdgesInfo(obj.Base) + if hasattr(obj.Base, "Proxy"): + if hasattr(obj.Base.Proxy, "getCurtainWallBaseShapeEdgesInfo"): + curtainWallBaseShapeEdges = obj.Base.Proxy.getCurtainWallBaseShapeEdgesInfo( + obj.Base + ) # returned a {dict} # get curtain wall edges (not wires); use original edges if getCurtainWallBaseShapeEdges() provided none - if curtainWallBaseShapeEdges: # would be false (none) if SketchArch Add-on is not installed, or base ArchSketch does not have the edges stored / input by user - curtainWallEdges = curtainWallBaseShapeEdges.get('curtainWallEdges') + if ( + curtainWallBaseShapeEdges + ): # would be false (none) if SketchArch Add-on is not installed, or base ArchSketch does not have the edges stored / input by user + curtainWallEdges = curtainWallBaseShapeEdges.get("curtainWallEdges") elif obj.Base.isDerivedFrom("Sketcher::SketchObject"): skGeom = obj.Base.GeometryFacadeList skGeomEdges = [] skPlacement = obj.Base.Placement # Get Sketch's placement to restore later - for ig, geom in enumerate(skGeom): - if (not obj.OverrideEdges and not geom.Construction) or str(ig) in obj.OverrideEdges: + for ig, geom in enumerate(skGeom): + if (not obj.OverrideEdges and not geom.Construction) or str( + ig + ) in obj.OverrideEdges: # support Line, Arc, Circle, Ellipse for Sketch # as Base at the moment - if isinstance(geom.Geometry, (Part.LineSegment, - Part.Circle, Part.ArcOfCircle)): + if isinstance( + geom.Geometry, (Part.LineSegment, Part.Circle, Part.ArcOfCircle) + ): skGeomEdgesI = geom.Geometry.toShape() skGeomEdges.append(skGeomEdgesI) curtainWallEdges = [] @@ -269,7 +442,7 @@ class CurtainWall(ArchComponent.Component): if curtainWallEdges: faces = [edge.extrude(ext) for edge in curtainWallEdges] if not faces: - FreeCAD.Console.PrintLog(obj.Label+": unable to build base faces\n") + FreeCAD.Console.PrintLog(obj.Label + ": unable to build base faces\n") return # subdivide the faces into quads @@ -280,7 +453,7 @@ class CurtainWall(ArchComponent.Component): # guessing horizontal/vertical directions vdir = obj.VerticalDirection if not vdir.Length: - vdir = FreeCAD.Vector(0,0,1) + vdir = FreeCAD.Vector(0, 0, 1) vdir.normalize() # Check if face if vertical in the first place @@ -290,7 +463,9 @@ class CurtainWall(ArchComponent.Component): face_plane = face.findPlane() if face_plane: - if -0.001 < face_plane.Axis[2] < 0.001: # i.e. face is vertical (normal pointing horizon) + if ( + -0.001 < face_plane.Axis[2] < 0.001 + ): # i.e. face is vertical (normal pointing horizon) faceVert = True # Support 'Swap Horizontal Vertical' # See issue 'Swap Horizontal Vertical does not work' @@ -314,9 +489,11 @@ class CurtainWall(ArchComponent.Component): # https://github.com/FreeCAD/FreeCAD/issues/21845 # Partially improved by checking 'if face is vertical' above # - basevector = face.valueAt(fp[1],fp[3]).sub(face.valueAt(fp[0],fp[2])) + basevector = face.valueAt(fp[1], fp[3]).sub(face.valueAt(fp[0], fp[2])) bv_angle = basevector.getAngle(vdir) - if (bv_angle <= math.pi/2+ANGLETOLERANCE) and (bv_angle >= math.pi/2-ANGLETOLERANCE): + if (bv_angle <= math.pi / 2 + ANGLETOLERANCE) and ( + bv_angle >= math.pi / 2 - ANGLETOLERANCE + ): facedir = True if obj.SwapHorizontalVertical: vertsec = obj.HorizontalSections @@ -333,25 +510,25 @@ class CurtainWall(ArchComponent.Component): vertsec = obj.HorizontalSections horizsec = obj.VerticalSections - hstep = (fp[1]-fp[0]) + hstep = fp[1] - fp[0] if vertsec: - hstep = hstep/vertsec - vstep = (fp[3]-fp[2]) + hstep = hstep / vertsec + vstep = fp[3] - fp[2] if horizsec: - vstep = vstep/horizsec + vstep = vstep / horizsec # construct facets for i in range(vertsec or 1): for j in range(horizsec or 1): - p0 = face.valueAt(fp[0]+i*hstep,fp[2]+j*vstep) - p1 = face.valueAt(fp[0]+(i+1)*hstep,fp[2]+j*vstep) - p2 = face.valueAt(fp[0]+(i+1)*hstep,fp[2]+(j+1)*vstep) - p3 = face.valueAt(fp[0]+i*hstep,fp[2]+(j+1)*vstep) - facet = Part.Face(Part.makePolygon([p0,p1,p2,p3,p0])) + p0 = face.valueAt(fp[0] + i * hstep, fp[2] + j * vstep) + p1 = face.valueAt(fp[0] + (i + 1) * hstep, fp[2] + j * vstep) + p2 = face.valueAt(fp[0] + (i + 1) * hstep, fp[2] + (j + 1) * vstep) + p3 = face.valueAt(fp[0] + i * hstep, fp[2] + (j + 1) * vstep) + facet = Part.Face(Part.makePolygon([p0, p1, p2, p3, p0])) facets.append(facet) if not facets: - FreeCAD.Console.PrintLog(obj.Label+": failed to subdivide shape\n") + FreeCAD.Console.PrintLog(obj.Label + ": failed to subdivide shape\n") return baseshape = Part.makeShell(facets) @@ -366,16 +543,16 @@ class CurtainWall(ArchComponent.Component): else: edgetable[ec] = [face] self.edgenormals = {} - for ec,faces in edgetable.items(): + for ec, faces in edgetable.items(): if len(faces) == 1: - self.edgenormals[ec] = faces[0].normalAt(0,0) + self.edgenormals[ec] = faces[0].normalAt(0, 0) else: - n = faces[0].normalAt(0,0).add(faces[1].normalAt(0,0)) + n = faces[0].normalAt(0, 0).add(faces[1].normalAt(0, 0)) if n.Length > 0.001: n.normalize() else: # adjacent faces have same normals - n = faces[0].normalAt(0,0) + n = faces[0].normalAt(0, 0) self.edgenormals[ec] = n # sort edges between vertical/horizontal @@ -384,40 +561,40 @@ class CurtainWall(ArchComponent.Component): for edge in baseshape.Edges: v = edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point) a = v.getAngle(vdir) - if (a <= math.pi/2+ANGLETOLERANCE) and (a >= math.pi/2-ANGLETOLERANCE): + if (a <= math.pi / 2 + ANGLETOLERANCE) and (a >= math.pi / 2 - ANGLETOLERANCE): hedges.append(edge) else: vedges.append(edge) # construct vertical mullions vmullions = [] - vprofile = self.getMullionProfile(obj,"Vertical") + vprofile = self.getMullionProfile(obj, "Vertical") if vprofile and vertsec: for vedge in vedges: vn = self.edgenormals[vedge.hashCode()] if (vn.x != 0) or (vn.y != 0): - avn = FreeCAD.Vector(vn.x,vn.y,0) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,-1,0),avn) + avn = FreeCAD.Vector(vn.x, vn.y, 0) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), avn) else: rot = FreeCAD.Rotation() if obj.VerticalMullionAlignment: ev = vedge.Vertexes[-1].Point.sub(vedge.Vertexes[0].Point) - rot = FreeCAD.Rotation(FreeCAD.Vector(1,0,0),ev).multiply(rot) - vmullions.append(self.makeMullion(vedge,vprofile,rot,obj.CenterProfiles)) + rot = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), ev).multiply(rot) + vmullions.append(self.makeMullion(vedge, vprofile, rot, obj.CenterProfiles)) # construct horizontal mullions hmullions = [] - hprofile = self.getMullionProfile(obj,"Horizontal") + hprofile = self.getMullionProfile(obj, "Horizontal") if hprofile and horizsec: for hedge in hedges: - rot = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),-90) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), -90) vn = self.edgenormals[hedge.hashCode()] if (vn.x != 0) or (vn.y != 0): - avn = FreeCAD.Vector(vn.x,vn.y,0) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,-1,0),avn).multiply(rot) + avn = FreeCAD.Vector(vn.x, vn.y, 0) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), avn).multiply(rot) if obj.HorizontalMullionAlignment: - rot = FreeCAD.Rotation(avn,vn).multiply(rot) - hmullions.append(self.makeMullion(hedge,hprofile,rot,obj.CenterProfiles)) + rot = FreeCAD.Rotation(avn, vn).multiply(rot) + hmullions.append(self.makeMullion(hedge, hprofile, rot, obj.CenterProfiles)) # construct panels panels = [] @@ -426,26 +603,29 @@ class CurtainWall(ArchComponent.Component): for face in baseshape.Faces: verts = [v.Point for v in face.OuterWire.OrderedVertexes] if DraftGeomUtils.isPlanar(verts): - panel = self.makePanel(verts,obj.PanelThickness.Value) + panel = self.makePanel(verts, obj.PanelThickness.Value) panels.append(panel) elif len(verts) == 4: - verts1 = [verts[0],verts[1],verts[2]] - panel = self.makePanel(verts1,obj.PanelThickness.Value) + verts1 = [verts[0], verts[1], verts[2]] + panel = self.makePanel(verts1, obj.PanelThickness.Value) panels.append(panel) - verts2 = [verts[0],verts[2],verts[3]] - panel = self.makePanel(verts2,obj.PanelThickness.Value) + verts2 = [verts[0], verts[2], verts[3]] + panel = self.makePanel(verts2, obj.PanelThickness.Value) panels.append(panel) - dedges.append(Part.makeLine(verts[0],verts[2])) + dedges.append(Part.makeLine(verts[0], verts[2])) # construct diagonal mullions dmullions = [] if dedges: - n = (dedges[0].Vertexes[-1].Point.sub(dedges[0].Point)) - dprofile = self.getMullionProfile(obj,"Diagonal") + n = dedges[0].Vertexes[-1].Point.sub(dedges[0].Point) + dprofile = self.getMullionProfile(obj, "Diagonal") if dprofile: for dedge in dedges: - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),dedge.Vertexes[-1].Point.sub(dedge.Vertexes[0].Point)) - dmullions.append(self.makeMullion(dedge,dprofile,rot,obj.CenterProfiles)) + rot = FreeCAD.Rotation( + FreeCAD.Vector(0, 0, 1), + dedge.Vertexes[-1].Point.sub(dedge.Vertexes[0].Point), + ) + dmullions.append(self.makeMullion(dedge, dprofile, rot, obj.CenterProfiles)) # perform subtractions if obj.Refine: @@ -479,64 +659,60 @@ class CurtainWall(ArchComponent.Component): obj.HorizontalMullionNumber = len(hmullions) obj.DiagonalMullionNumber = len(dmullions) obj.PanelNumber = len(panels) - shape = Part.makeCompound(vmullions+hmullions+dmullions+panels) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) - - def makePanel(self,verts,thickness): + shape = Part.makeCompound(vmullions + hmullions + dmullions + panels) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) + def makePanel(self, verts, thickness): """creates a panel from face points and thickness""" import Part - panel = Part.Face(Part.makePolygon(verts+[verts[0]])) - n = panel.normalAt(0,0) + panel = Part.Face(Part.makePolygon(verts + [verts[0]])) + n = panel.normalAt(0, 0) n.multiply(thickness) panel = panel.extrude(n) return panel - def makeMullion(self,edge,profile,rotation,recenter=False): - + def makeMullion(self, edge, profile, rotation, recenter=False): """creates a mullions from an edge and a profile""" - center = FreeCAD.Vector(0,0,0) + center = FreeCAD.Vector(0, 0, 0) if recenter: - if hasattr(profile,"CenterOfMass"): + if hasattr(profile, "CenterOfMass"): center = profile.CenterOfMass - elif hasattr(profile,"BoundBox"): + elif hasattr(profile, "BoundBox"): center = profile.BoundBox.Center p0 = edge.Vertexes[0].Point p1 = edge.Vertexes[-1].Point mullion = profile.copy() if rotation: - mullion = mullion.rotate(center,rotation.Axis,math.degrees(rotation.Angle)) + mullion = mullion.rotate(center, rotation.Axis, math.degrees(rotation.Angle)) mullion = mullion.translate(p0.sub(center)) mullion = mullion.extrude(p1.sub(p0)) return mullion - def getMullionProfile(self,obj,direction): - + def getMullionProfile(self, obj, direction): """returns a profile shape already properly oriented, ready for extrude""" import Part import DraftGeomUtils - prof = getattr(obj,direction+"MullionProfile") - proh = getattr(obj,direction+"MullionHeight").Value - prow = getattr(obj,direction+"MullionWidth").Value + prof = getattr(obj, direction + "MullionProfile") + proh = getattr(obj, direction + "MullionHeight").Value + prow = getattr(obj, direction + "MullionWidth").Value if prof: profile = prof.Shape.copy() else: if (not proh) or (not prow): return None - profile = Part.Face(Part.makePlane(prow,proh,FreeCAD.Vector(-prow/2,-proh/2,0))) + profile = Part.Face(Part.makePlane(prow, proh, FreeCAD.Vector(-prow / 2, -proh / 2, 0))) return profile - def getProjectedLength(self,v,ref): - + def getProjectedLength(self, v, ref): """gets a signed length from projecting a vector on another""" - proj = DraftVecUtils.project(v,ref) + proj = DraftVecUtils.project(v, ref) if proj.getAngle(ref) < 1: return proj.Length else: @@ -544,34 +720,32 @@ class CurtainWall(ArchComponent.Component): class ViewProviderCurtainWall(ArchComponent.ViewProviderComponent): - - "A View Provider for the CurtainWall object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_CurtainWall_Tree.svg" - def updateData(self,obj,prop): + def updateData(self, obj, prop): if prop == "Shape": - self.colorize(obj,force=True) + self.colorize(obj, force=True) - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - if (prop in ["DiffuseColor","Transparency"]) and vobj.Object: + if (prop in ["DiffuseColor", "Transparency"]) and vobj.Object: self.colorize(vobj.Object) elif prop == "ShapeColor": - self.colorize(vobj.Object,force=True) - ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) - - def colorize(self,obj,force=False): + self.colorize(vobj.Object, force=True) + ArchComponent.ViewProviderComponent.onChanged(self, vobj, prop) + def colorize(self, obj, force=False): "setting different part colors" if not obj.Shape or not obj.Shape.Solids: @@ -579,35 +753,43 @@ class ViewProviderCurtainWall(ArchComponent.ViewProviderComponent): if not obj.ViewObject: return basecolor = obj.ViewObject.ShapeColor - basetransparency = obj.ViewObject.Transparency/100.0 + basetransparency = obj.ViewObject.Transparency / 100.0 panelcolor = ArchCommands.getDefaultColor("WindowGlass") paneltransparency = 0.7 - if hasattr(obj,"Material") and obj.Material and hasattr(obj.Material,"Materials"): + if hasattr(obj, "Material") and obj.Material and hasattr(obj.Material, "Materials"): if obj.Material.Names: if "Frame" in obj.Material.Names: mat = obj.Material.Materials[obj.Material.Names.index("Frame")] - if ('DiffuseColor' in mat.Material) and ("(" in mat.Material['DiffuseColor']): - basecolor = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) + if ("DiffuseColor" in mat.Material) and ("(" in mat.Material["DiffuseColor"]): + basecolor = tuple( + [float(f) for f in mat.Material["DiffuseColor"].strip("()").split(",")] + ) if "Glass panel" in obj.Material.Names: mat = obj.Material.Materials[obj.Material.Names.index("Glass panel")] - if ('DiffuseColor' in mat.Material) and ("(" in mat.Material['DiffuseColor']): - panelcolor = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) - if ('Transparency' in mat.Material): - paneltransparency = float(mat.Material['Transparency'])/100.0 + if ("DiffuseColor" in mat.Material) and ("(" in mat.Material["DiffuseColor"]): + panelcolor = tuple( + [float(f) for f in mat.Material["DiffuseColor"].strip("()").split(",")] + ) + if "Transparency" in mat.Material: + paneltransparency = float(mat.Material["Transparency"]) / 100.0 elif "Solid panel" in obj.Material.Names: mat = obj.Material.Materials[obj.Material.Names.index("Solid panel")] - if ('DiffuseColor' in mat.Material) and ("(" in mat.Material['DiffuseColor']): - panelcolor = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) + if ("DiffuseColor" in mat.Material) and ("(" in mat.Material["DiffuseColor"]): + panelcolor = tuple( + [float(f) for f in mat.Material["DiffuseColor"].strip("()").split(",")] + ) paneltransparency = 0 - basecolor = basecolor[:3]+(1.0 - basetransparency,) - panelcolor = panelcolor[:3]+(1.0 - paneltransparency,) + basecolor = basecolor[:3] + (1.0 - basetransparency,) + panelcolor = panelcolor[:3] + (1.0 - paneltransparency,) colors = [] - nmullions = obj.VerticalMullionNumber + obj.HorizontalMullionNumber + obj.DiagonalMullionNumber - for i,solid in enumerate(obj.Shape.Solids): + nmullions = ( + obj.VerticalMullionNumber + obj.HorizontalMullionNumber + obj.DiagonalMullionNumber + ) + for i, solid in enumerate(obj.Shape.Solids): for _ in solid.Faces: if i < nmullions: colors.append(basecolor) else: colors.append(panelcolor) - if self.areDifferentColors(colors,obj.ViewObject.DiffuseColor) or force: + if self.areDifferentColors(colors, obj.ViewObject.DiffuseColor) or force: obj.ViewObject.DiffuseColor = colors diff --git a/src/Mod/BIM/ArchCutPlane.py b/src/Mod/BIM/ArchCutPlane.py index e569046280..809e2e6800 100644 --- a/src/Mod/BIM/ArchCutPlane.py +++ b/src/Mod/BIM/ArchCutPlane.py @@ -24,7 +24,7 @@ # * * # ***************************************************************************** -__title__="FreeCAD CutPlane" +__title__ = "FreeCAD CutPlane" __author__ = "Jonathan Wiedemann" __url__ = "https://www.freecad.org" @@ -47,6 +47,7 @@ else: # \cond def translate(ctxt, txt): return txt + # \endcond @@ -101,17 +102,18 @@ def _getShapes(sels): pt_v = mtx.col(1) + pt_main cutterShp = Part.Face(Part.makePolygon([pt_main, pt_u, pt_v, pt_main])) # _extrudeEdge can create a face with a zero area (if the edge is parallel to the WP normal): - if not cutterShp.Faces \ - or cutterShp.Faces[0].Area < 1e-6 \ - or cutterShp.findPlane() is None: + if not cutterShp.Faces or cutterShp.Faces[0].Area < 1e-6 or cutterShp.findPlane() is None: return baseObj, baseShp, None return baseObj, baseShp, cutterShp.Faces[0] + def _extrudeEdge(edge): """Exrude an edge along the WP normal""" import WorkingPlane + return edge.extrude(WorkingPlane.get_working_plane().axis) + def cutComponentwithPlane(baseObj, cutterShp=None, side=0): """cut an object with a plane defined by a face. @@ -130,9 +132,11 @@ def cutComponentwithPlane(baseObj, cutterShp=None, side=0): Defaults to 0. Behind = 0, front = 1. """ - if isinstance(baseObj, list) \ - and len(baseObj) >= 1 \ - and baseObj[0].isDerivedFrom("Gui::SelectionObject"): + if ( + isinstance(baseObj, list) + and len(baseObj) >= 1 + and baseObj[0].isDerivedFrom("Gui::SelectionObject") + ): baseObj, baseShp, cutterShp = _getShapes(baseObj) baseParent = baseObj.getParentGeoFeatureGroup() else: @@ -154,7 +158,7 @@ def cutComponentwithPlane(baseObj, cutterShp=None, side=0): if baseParent is not None: baseParent.addObject(obj) if "Additions" in baseObj.PropertiesList: - ArchCommands.removeComponents(obj, baseObj) # Also changes the obj colors. + ArchCommands.removeComponents(obj, baseObj) # Also changes the obj colors. else: Draft.format_object(obj, baseObj) cutObj = FreeCAD.ActiveDocument.addObject("Part::Cut", "CutPlane") diff --git a/src/Mod/BIM/ArchEquipment.py b/src/Mod/BIM/ArchEquipment.py index 030b83d582..5157cf15cf 100644 --- a/src/Mod/BIM/ArchEquipment.py +++ b/src/Mod/BIM/ArchEquipment.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Equipment" +__title__ = "FreeCAD Equipment" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchEquipment # \ingroup ARCH @@ -45,15 +45,16 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largestonly=False): - +def createMeshView(obj, direction=FreeCAD.Vector(0, 0, -1), outeronly=False, largestonly=False): """createMeshView(obj,[direction,outeronly,largestonly]): creates a flat shape that is the projection of the given mesh object in the given direction (default = on the XY plane). If outeronly is True, only the outer contour is taken into consideration, discarding the inner @@ -63,6 +64,7 @@ def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largesto import DraftGeomUtils import Mesh import Part + if not obj.isDerivedFrom("Mesh::Feature"): return mesh = obj.Mesh @@ -74,7 +76,7 @@ def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largesto for v in f.Points: v = FreeCAD.Vector(v) a = v.negative().getAngle(direction) - l = math.cos(a)*v.Length + l = math.cos(a) * v.Length p = v.add(FreeCAD.Vector(direction).multiply(l)) p = DraftVecUtils.rounded(p) nf.append(p) @@ -88,12 +90,12 @@ def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largesto facets.append(f) cleanmesh = Mesh.Mesh(facets) - #Mesh.show(cleanmesh) + # Mesh.show(cleanmesh) # 3. Getting the bigger mesh from the planar segments if largestonly: c = cleanmesh.getSeparateComponents() - #print(c) + # print(c) cleanmesh = c[0] segs = cleanmesh.getPlanarSegments(1) meshes = [] @@ -105,15 +107,15 @@ def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largesto if m.Area > a: boundarymesh = m a = m.Area - #Mesh.show(boundarymesh) + # Mesh.show(boundarymesh) cleanmesh = boundarymesh # 4. Creating a Part and getting the contour shape = None for f in cleanmesh.Facets: - p = Part.makePolygon(f.Points+[f.Points[0]]) - #print(p,len(p.Vertexes),p.isClosed()) + p = Part.makePolygon(f.Points + [f.Points[0]]) + # print(p,len(p.Vertexes),p.isClosed()) try: p = Part.Face(p) if shape: @@ -144,17 +146,16 @@ def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largesto return shape - class _Equipment(ArchComponent.Component): - "The Equipment object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Equipment" self.setProperties(obj) from ArchIFC import IfcTypes + if "Furniture" in IfcTypes: # IfcFurniture is new in IFC4 obj.IfcType = "Furniture" @@ -166,57 +167,90 @@ class _Equipment(ArchComponent.Component): # Add features in the SketchArch External Add-on, if present self.addSketchArchFeatures(obj) - def addSketchArchFeatures(self,obj,linkObj=None,mode=None): - ''' - To add features in the SketchArch External Add-on, if present (https://github.com/paullee0/FreeCAD_SketchArch) - - import ArchSketchObject module, and - - set properties that are common to ArchObjects (including Links) and ArchSketch - to support the additional features + def addSketchArchFeatures(self, obj, linkObj=None, mode=None): + """ + To add features in the SketchArch External Add-on, if present (https://github.com/paullee0/FreeCAD_SketchArch) + - import ArchSketchObject module, and + - set properties that are common to ArchObjects (including Links) and ArchSketch + to support the additional features - To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install - ''' + To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install + """ try: import ArchSketchObject + ArchSketchObject.ArchSketch.setPropertiesLinkCommon(self, obj, linkObj, mode) except: pass - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Model" in pl: - obj.addProperty("App::PropertyString","Model","Equipment",QT_TRANSLATE_NOOP("App::Property","The model description of this equipment"), locked=True) + obj.addProperty( + "App::PropertyString", + "Model", + "Equipment", + QT_TRANSLATE_NOOP("App::Property", "The model description of this equipment"), + locked=True, + ) if not "ProductURL" in pl: - obj.addProperty("App::PropertyString","ProductURL","Equipment",QT_TRANSLATE_NOOP("App::Property","The URL of the product page of this equipment"), locked=True) + obj.addProperty( + "App::PropertyString", + "ProductURL", + "Equipment", + QT_TRANSLATE_NOOP("App::Property", "The URL of the product page of this equipment"), + locked=True, + ) if not "StandardCode" in pl: - obj.addProperty("App::PropertyString","StandardCode","Equipment",QT_TRANSLATE_NOOP("App::Property","A standard code (MasterFormat, OmniClass,…)"), locked=True) + obj.addProperty( + "App::PropertyString", + "StandardCode", + "Equipment", + QT_TRANSLATE_NOOP("App::Property", "A standard code (MasterFormat, OmniClass,…)"), + locked=True, + ) if not "SnapPoints" in pl: - obj.addProperty("App::PropertyVectorList","SnapPoints","Equipment",QT_TRANSLATE_NOOP("App::Property","Additional snap points for this equipment"), locked=True) + obj.addProperty( + "App::PropertyVectorList", + "SnapPoints", + "Equipment", + QT_TRANSLATE_NOOP("App::Property", "Additional snap points for this equipment"), + locked=True, + ) if not "EquipmentPower" in pl: - obj.addProperty("App::PropertyFloat","EquipmentPower","Equipment",QT_TRANSLATE_NOOP("App::Property","The electric power needed by this equipment in Watts"), locked=True) - obj.setEditorMode("VerticalArea",2) - obj.setEditorMode("HorizontalArea",2) - obj.setEditorMode("PerimeterLength",2) + obj.addProperty( + "App::PropertyFloat", + "EquipmentPower", + "Equipment", + QT_TRANSLATE_NOOP( + "App::Property", "The electric power needed by this equipment in Watts" + ), + locked=True, + ) + obj.setEditorMode("VerticalArea", 2) + obj.setEditorMode("HorizontalArea", 2) + obj.setEditorMode("PerimeterLength", 2) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) # Add features in the SketchArch External Add-on, if present self.addSketchArchFeatures(obj) - def loads(self,state): + def loads(self, state): self.Type = "Equipment" - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): - self.hideSubobjects(obj,prop) - ArchComponent.Component.onChanged(self,obj,prop) + self.hideSubobjects(obj, prop) + ArchComponent.Component.onChanged(self, obj, prop) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -226,26 +260,27 @@ class _Equipment(ArchComponent.Component): pl = obj.Placement if obj.Base: base = None - if hasattr(obj.Base,'Shape'): + if hasattr(obj.Base, "Shape"): base = obj.Base.Shape.copy() - base = self.processSubShapes(obj,base,pl) - self.applyShape(obj,base,pl,allowinvalid=False,allownosolid=True) + base = self.processSubShapes(obj, base, pl) + self.applyShape(obj, base, pl, allowinvalid=False, allownosolid=True) # Execute features in the SketchArch External Add-on, if present self.executeSketchArchFeatures(obj) def executeSketchArchFeatures(self, obj, linkObj=None, index=None, linkElement=None): - ''' - To execute features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) - - import ArchSketchObject module, and - - execute features that are common to ArchObjects (including Links) and ArchSketch + """ + To execute features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) + - import ArchSketchObject module, and + - execute features that are common to ArchObjects (including Links) and ArchSketch - To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install - ''' + To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install + """ # To execute features in SketchArch External Add-on, if present try: import ArchSketchObject + # Execute SketchArch Feature - Intuitive Automatic Placement for Arch Windows/Doors, Equipment etc. # see https://forum.freecad.org/viewtopic.php?f=23&t=50802 ArchSketchObject.updateAttachmentOffset(obj, linkObj) @@ -253,11 +288,11 @@ class _Equipment(ArchComponent.Component): pass def appLinkExecute(self, obj, linkObj, index, linkElement): - ''' - Default Link Execute method() - - See https://forum.freecad.org/viewtopic.php?f=22&t=42184&start=10#p361124 - @realthunder added support to Links to run Linked Scripted Object's methods() - ''' + """ + Default Link Execute method() - + See https://forum.freecad.org/viewtopic.php?f=22&t=42184&start=10#p361124 + @realthunder added support to Links to run Linked Scripted Object's methods() + """ # Add features in the SketchArch External Add-on, if present self.addSketchArchFeatures(obj, linkObj) @@ -265,23 +300,23 @@ class _Equipment(ArchComponent.Component): # Execute features in the SketchArch External Add-on, if present self.executeSketchArchFeatures(obj, linkObj) - def computeAreas(self,obj): + def computeAreas(self, obj): return class _ViewProviderEquipment(ArchComponent.ViewProviderComponent): - "A View Provider for the Equipment object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + + if hasattr(self, "Object"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Equipment_Clone.svg" return ":/icons/Arch_Equipment_Tree.svg" @@ -290,6 +325,7 @@ class _ViewProviderEquipment(ArchComponent.ViewProviderComponent): self.Object = vobj.Object from pivy import coin + sep = coin.SoSeparator() self.coords = coin.SoCoordinate3() sep.addChild(self.coords) @@ -299,15 +335,15 @@ class _ViewProviderEquipment(ArchComponent.ViewProviderComponent): sep.addChild(symbol) rn = vobj.RootNode rn.addChild(sep) - ArchComponent.ViewProviderComponent.attach(self,vobj) + ArchComponent.ViewProviderComponent.attach(self, vobj) def updateData(self, obj, prop): if prop == "SnapPoints": if obj.SnapPoints: self.coords.point.setNum(len(obj.SnapPoints)) - self.coords.point.setValues([[p.x,p.y,p.z] for p in obj.SnapPoints]) + self.coords.point.setValues([[p.x, p.y, p.z] for p in obj.SnapPoints]) else: self.coords.point.deleteValues(0) else: - ArchComponent.ViewProviderComponent.updateData(self,obj,prop) + ArchComponent.ViewProviderComponent.updateData(self, obj, prop) diff --git a/src/Mod/BIM/ArchFence.py b/src/Mod/BIM/ArchFence.py index a7bbe0a0eb..80a5bb27c6 100644 --- a/src/Mod/BIM/ArchFence.py +++ b/src/Mod/BIM/ArchFence.py @@ -41,6 +41,7 @@ else: def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond EAST = FreeCAD.Vector(1, 0, 0) @@ -62,29 +63,54 @@ class _Fence(ArchComponent.Component): pl = obj.PropertiesList if not "Section" in pl: - obj.addProperty("App::PropertyLink", "Section", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "A single section of the fence"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Section", + "Fence", + QT_TRANSLATE_NOOP("App::Property", "A single section of the fence"), + locked=True, + ) if not "Post" in pl: - obj.addProperty("App::PropertyLink", "Post", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "A single fence post"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Post", + "Fence", + QT_TRANSLATE_NOOP("App::Property", "A single fence post"), + locked=True, + ) if not "Path" in pl: - obj.addProperty("App::PropertyLink", "Path", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "The Path the fence should follow"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Path", + "Fence", + QT_TRANSLATE_NOOP("App::Property", "The Path the fence should follow"), + locked=True, + ) if not "NumberOfSections" in pl: - obj.addProperty("App::PropertyInteger", "NumberOfSections", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "The number of sections the fence is built of"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "NumberOfSections", + "Fence", + QT_TRANSLATE_NOOP("App::Property", "The number of sections the fence is built of"), + locked=True, + ) obj.setEditorMode("NumberOfSections", 1) if not "NumberOfPosts" in pl: - obj.addProperty("App::PropertyInteger", "NumberOfPosts", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "The number of posts used to build the fence"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "NumberOfPosts", + "Fence", + QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"), + locked=True, + ) obj.setEditorMode("NumberOfPosts", 1) def dumps(self): - if hasattr(self, 'sectionFaceNumbers'): + if hasattr(self, "sectionFaceNumbers"): return self.sectionFaceNumbers return None @@ -99,20 +125,17 @@ class _Fence(ArchComponent.Component): pathwire = self.calculatePathWire(obj) if not pathwire: - FreeCAD.Console.PrintLog( - "ArchFence.execute: path " + obj.Path.Name + " has no edges\n") + FreeCAD.Console.PrintLog("ArchFence.execute: path " + obj.Path.Name + " has no edges\n") return if not obj.Section: - FreeCAD.Console.PrintLog( - "ArchFence.execute: Section not set\n") + FreeCAD.Console.PrintLog("ArchFence.execute: Section not set\n") return if not obj.Post: - FreeCAD.Console.PrintLog( - "ArchFence.execute: Post not set\n") + FreeCAD.Console.PrintLog("ArchFence.execute: Post not set\n") return @@ -120,8 +143,7 @@ class _Fence(ArchComponent.Component): sectionLength = obj.Section.Shape.BoundBox.XMax postLength = obj.Post.Shape.BoundBox.XMax - obj.NumberOfSections = self.calculateNumberOfSections( - pathLength, sectionLength, postLength) + obj.NumberOfSections = self.calculateNumberOfSections(pathLength, sectionLength, postLength) obj.NumberOfPosts = obj.NumberOfSections + 1 # We assume that the section was drawn in front view. @@ -129,12 +151,12 @@ class _Fence(ArchComponent.Component): # correctly by the algorithm later on downRotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), -90) - postPlacements = self.calculatePostPlacements( - obj, pathwire, downRotation) + postPlacements = self.calculatePostPlacements(obj, pathwire, downRotation) postShapes = self.calculatePosts(obj, postPlacements) sectionShapes, sectionFaceNumbers = self.calculateSections( - obj, postPlacements, postLength, sectionLength) + obj, postPlacements, postLength, sectionLength + ) allShapes = [] allShapes.extend(postShapes) @@ -155,11 +177,11 @@ class _Fence(ArchComponent.Component): postWidth = obj.Post.Shape.BoundBox.YMax # We want to center the posts on the path. So move them the half width in - transformationVector = FreeCAD.Vector(0, - postWidth / 2, 0) + transformationVector = FreeCAD.Vector(0, -postWidth / 2, 0) - return patharray.placements_on_path(rotation, pathwire, - obj.NumberOfPosts, - transformationVector, True) + return patharray.placements_on_path( + rotation, pathwire, obj.NumberOfPosts, transformationVector, True + ) def calculatePosts(self, obj, postPlacements): posts = [] @@ -186,8 +208,7 @@ class _Fence(ArchComponent.Component): startPlacement = postPlacements[i] endPlacement = postPlacements[i + 1] - sectionLine = Part.LineSegment( - startPlacement.Base, endPlacement.Base) + sectionLine = Part.LineSegment(startPlacement.Base, endPlacement.Base) sectionBase = sectionLine.value(postLength) if startPlacement.Rotation.isSame(endPlacement.Rotation): @@ -206,9 +227,12 @@ class _Fence(ArchComponent.Component): if sectionLength > sectionLine.length() - postLength: # Part.show(Part.Shape([sectionLine]), 'line') sectionCopy = self.clipSection( - sectionCopy, sectionLength, sectionLine.length() - postLength) + sectionCopy, sectionLength, sectionLine.length() - postLength + ) - sectionCopy = Part.Compound([sectionCopy]) # nest in compound to ensure correct Placement + sectionCopy = Part.Compound( + [sectionCopy] + ) # nest in compound to ensure correct Placement sectionCopy.Placement = placement shapes.append(sectionCopy) @@ -223,10 +247,20 @@ class _Fence(ArchComponent.Component): lengthToCut = length - clipLength halfLengthToCut = lengthToCut / 2 - leftBox = Part.makeBox(halfLengthToCut, boundBox.YMax + 1, boundBox.ZMax + 1, - FreeCAD.Vector(boundBox.XMin, boundBox.YMin, boundBox.ZMin)) - rightBox = Part.makeBox(halfLengthToCut, boundBox.YMax + 1, boundBox.ZMax + 1, - FreeCAD.Vector(boundBox.XMin + halfLengthToCut + clipLength, boundBox.YMin, boundBox.ZMin)) + leftBox = Part.makeBox( + halfLengthToCut, + boundBox.YMax + 1, + boundBox.ZMax + 1, + FreeCAD.Vector(boundBox.XMin, boundBox.YMin, boundBox.ZMin), + ) + rightBox = Part.makeBox( + halfLengthToCut, + boundBox.YMax + 1, + boundBox.ZMax + 1, + FreeCAD.Vector( + boundBox.XMin + halfLengthToCut + clipLength, boundBox.YMin, boundBox.ZMin + ), + ) newShape = shape.cut([leftBox, rightBox]) newBoundBox = newShape.BoundBox @@ -236,7 +270,7 @@ class _Fence(ArchComponent.Component): return newShape.removeSplitter() def calculatePathWire(self, obj): - if (hasattr(obj.Path.Shape, 'Wires') and obj.Path.Shape.Wires): + if hasattr(obj.Path.Shape, "Wires") and obj.Path.Shape.Wires: return obj.Path.Shape.Wires[0] elif obj.Path.Shape.Edges: return Part.Wire(obj.Path.Shape.Edges) @@ -245,7 +279,6 @@ class _Fence(ArchComponent.Component): class _ViewProviderFence(ArchComponent.ViewProviderComponent): - "A View Provider for the Fence object" def __init__(self, vobj): @@ -259,8 +292,16 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): pl = vobj.PropertiesList if not "UseOriginalColors" in pl: - vobj.addProperty("App::PropertyBool", "UseOriginalColors", "Fence", QT_TRANSLATE_NOOP( - "App::Property", "When true, the fence will be colored like the original post and section."), locked=True) + vobj.addProperty( + "App::PropertyBool", + "UseOriginalColors", + "Fence", + QT_TRANSLATE_NOOP( + "App::Property", + "When true, the fence will be colored like the original post and section.", + ), + locked=True, + ) def attach(self, vobj): self.setProperties(vobj) @@ -322,7 +363,7 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): numberOfPostFaces = len(post.Shape.Faces) numberOfSectionFaces = len(section.Shape.Faces) - if hasattr(obj.Proxy, 'sectionFaceNumbers'): + if hasattr(obj.Proxy, "sectionFaceNumbers"): sectionFaceNumbers = obj.Proxy.sectionFaceNumbers else: sectionFaceNumbers = [0] @@ -331,8 +372,7 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): return postColors = self.normalizeColors(post, numberOfPostFaces) - defaultSectionColors = self.normalizeColors( - section, numberOfSectionFaces) + defaultSectionColors = self.normalizeColors(section, numberOfSectionFaces) ownColors = [] @@ -347,8 +387,7 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): if actualSectionFaceCount == numberOfSectionFaces: ownColors.extend(defaultSectionColors) else: - ownColors.extend(self.normalizeColors( - section, actualSectionFaceCount)) + ownColors.extend(self.normalizeColors(section, actualSectionFaceCount)) vobj.DiffuseColor = ownColors @@ -365,6 +404,7 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): colors = obj.ViewObject.DiffuseColor else: import Draft + colors = Draft.get_diffuse_color(obj) # To handle Std_Parts for example. numberOfColors = len(colors) @@ -387,5 +427,5 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent): def hide(obj): - if hasattr(obj, 'ViewObject') and obj.ViewObject: + if hasattr(obj, "ViewObject") and obj.ViewObject: obj.ViewObject.Visibility = False diff --git a/src/Mod/BIM/ArchFloor.py b/src/Mod/BIM/ArchFloor.py index d607432b63..ee86e659b3 100644 --- a/src/Mod/BIM/ArchFloor.py +++ b/src/Mod/BIM/ArchFloor.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Floor" +__title__ = "FreeCAD Arch Floor" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchFloor # \ingroup ARCH @@ -56,14 +56,16 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -def makeFloor(objectslist=None,baseobj=None,name=None): +def makeFloor(objectslist=None, baseobj=None, name=None): """Obsolete, superseded by ArchBuildingPart.makeFloor. Create a floor. @@ -86,12 +88,11 @@ def makeFloor(objectslist=None,baseobj=None,name=None): The created floor. """ - if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return - obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython","Floor") - obj.Label = name if name else translate("Arch","Floor") + obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython", "Floor") + obj.Label = name if name else translate("Arch", "Floor") _Floor(obj) if FreeCAD.GuiUp: _ViewProviderFloor(obj.ViewObject) @@ -114,14 +115,18 @@ class _CommandFloor: https://wiki.freecad.org/Arch_Floor """ - def GetResources(self): """Return a dictionary with the visual aspects of the Arch Floor tool.""" - return {'Pixmap' : 'Arch_Floor', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Floor","Level"), - 'Accel': "L, V", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Floor","Creates a Building Part object that represents a level, including selected objects")} + return { + "Pixmap": "Arch_Floor", + "MenuText": QT_TRANSLATE_NOOP("Arch_Floor", "Level"), + "Accel": "L, V", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Floor", + "Creates a Building Part object that represents a level, including selected objects", + ), + } def IsActive(self): """Determine whether or not the Arch Floor tool is active. @@ -144,32 +149,44 @@ class _CommandFloor: link = params.get_param_arch("FreeLinking") floorobj = [] warning = False - for obj in sel : - if not Draft.getType(obj) in ["Site", "Building"] : + for obj in sel: + if not Draft.getType(obj) in ["Site", "Building"]: floorobj.append(obj) - else : + else: if link: floorobj.append(obj) else: warning = True - if warning : - message = translate( "Arch" , "You can put anything but the following objects: Site, Building, and Floor - in a Floor object.\n\ + if warning: + message = ( + translate( + "Arch", + "You can put anything but the following objects: Site, Building, and Floor - in a Floor object.\n\ Floor object is not allowed to accept Site, Building, or Floor objects.\n\ Site, Building, and Floor objects will be removed from the selection.\n\ -You can change that in the preferences.") + "\n" - ArchCommands.printMessage( message ) +You can change that in the preferences.", + ) + + "\n" + ) + ArchCommands.printMessage(message) if sel and len(floorobj) == 0: - message = translate( "Arch" , "There is no valid object in the selection.\n\ -Floor creation aborted.") + "\n" - ArchCommands.printMessage( message ) - else : + message = ( + translate( + "Arch", + "There is no valid object in the selection.\n\ +Floor creation aborted.", + ) + + "\n" + ) + ArchCommands.printMessage(message) + else: ss = "[ " for o in floorobj: ss += "FreeCAD.ActiveDocument." + o.Name + ", " ss += "]" - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Floor")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Floor")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("obj = Arch.makeFloor("+ss+")") + FreeCADGui.doCommand("obj = Arch.makeFloor(" + ss + ")") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() @@ -195,15 +212,15 @@ class _Floor(ArchIFC.IfcProduct): The object to turn into a Floor. """ - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "Floor" self.Object = obj - _Floor.setProperties(self,obj) + _Floor.setProperties(self, obj) self.IfcType = "Building Storey" - def setProperties(self,obj): + def setProperties(self, obj): """Give the object properties unique to floors. Add the IFC product properties, and the floor's height and area. @@ -212,27 +229,45 @@ class _Floor(ArchIFC.IfcProduct): ArchIFC.IfcProduct.setProperties(self, obj) pl = obj.PropertiesList if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","Floor",QT_TRANSLATE_NOOP("App::Property","The height of this object"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "Floor", + QT_TRANSLATE_NOOP("App::Property", "The height of this object"), + locked=True, + ) if not "Area" in pl: - obj.addProperty("App::PropertyArea","Area", "Floor",QT_TRANSLATE_NOOP("App::Property","The computed floor area of this floor"), locked=True) - if not hasattr(obj,"Placement"): + obj.addProperty( + "App::PropertyArea", + "Area", + "Floor", + QT_TRANSLATE_NOOP("App::Property", "The computed floor area of this floor"), + locked=True, + ) + if not hasattr(obj, "Placement"): # obj can be a Part Feature and already has a placement - obj.addProperty("App::PropertyPlacement","Placement","Base",QT_TRANSLATE_NOOP("App::Property","The placement of this object"), locked=True) + obj.addProperty( + "App::PropertyPlacement", + "Placement", + "Base", + QT_TRANSLATE_NOOP("App::Property", "The placement of this object"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): """Method run when the document is restored. Re-adds the properties.""" - _Floor.setProperties(self,obj) + _Floor.setProperties(self, obj) def dumps(self): return None - def loads(self,state): + def loads(self, state): self.Type = "Floor" - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): """Method called when the object has a property changed. If the objects grouped under the floor object changes, recompute @@ -247,20 +282,21 @@ class _Floor(ArchIFC.IfcProduct): """ ArchIFC.IfcProduct.onChanged(self, obj, prop) - if not hasattr(self,"Object"): + if not hasattr(self, "Object"): # on restore, self.Object is not there anymore self.Object = obj - if (prop == "Group") and hasattr(obj,"Area"): + if (prop == "Group") and hasattr(obj, "Area"): a = 0 - for o in Draft.getObjectsOfType(Draft.get_group_contents(obj.Group, addgroups=True), - "Space"): - if hasattr(o,"Area"): - if hasattr(o.Area,"Value"): + for o in Draft.getObjectsOfType( + Draft.get_group_contents(obj.Group, addgroups=True), "Space" + ): + if hasattr(o, "Area"): + if hasattr(o.Area, "Value"): a += o.Area.Value if obj.Area.Value != a: obj.Area = a - def execute(self,obj): + def execute(self, obj): """Method run when the object is recomputed. Move its children if its placement has changed since the previous @@ -269,26 +305,26 @@ class _Floor(ArchIFC.IfcProduct): """ # move children with this floor - if hasattr(obj,"Placement"): - if not hasattr(self,"OldPlacement"): + if hasattr(obj, "Placement"): + if not hasattr(self, "OldPlacement"): self.OldPlacement = obj.Placement.copy() else: pl = obj.Placement.copy() - if not DraftVecUtils.equals(pl.Base,self.OldPlacement.Base): + if not DraftVecUtils.equals(pl.Base, self.OldPlacement.Base): print("placement moved") delta = pl.Base.sub(self.OldPlacement.Base) for o in obj.Group: - if hasattr(o,"Placement"): + if hasattr(o, "Placement"): o.Placement.move(delta) self.OldPlacement = pl # adjust childrens heights if obj.Height.Value: for o in obj.Group: - if Draft.getType(o) in ["Wall","Structure"]: + if Draft.getType(o) in ["Wall", "Structure"]: if not o.Height.Value: o.Proxy.execute(o) - def addObject(self,child): + def addObject(self, child): """Add the object to the floor's group. Parameters @@ -297,13 +333,13 @@ class _Floor(ArchIFC.IfcProduct): The object to add to the floor's group. """ - if hasattr(self,"Object"): + if hasattr(self, "Object"): g = self.Object.Group if not child in g: g.append(child) self.Object.Group = g - def removeObject(self,child): + def removeObject(self, child): """Remove the object from the floor's group, if it's present. Parameters @@ -312,7 +348,7 @@ class _Floor(ArchIFC.IfcProduct): The object to remove from the floor's group. """ - if hasattr(self,"Object"): + if hasattr(self, "Object"): g = self.Object.Group if child in g: g.remove(child) @@ -330,7 +366,7 @@ class _ViewProviderFloor: The view provider to turn into a floor view provider. """ - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): @@ -343,9 +379,10 @@ class _ViewProviderFloor: """ import Arch_rc + return ":/icons/Arch_Floor_Tree.svg" - def attach(self,vobj): + def attach(self, vobj): """Add display modes' data to the coin scenegraph. Add each display mode as a coin node, whose parent is this view @@ -373,7 +410,7 @@ class _ViewProviderFloor: The objects claimed as children. """ - if hasattr(self,"Object"): + if hasattr(self, "Object"): if self.Object: return self.Object.Group return [] @@ -382,11 +419,11 @@ class _ViewProviderFloor: return None - def loads(self,state): + def loads(self, state): return None - def setupContextMenu(self,vobj,menu): + def setupContextMenu(self, vobj, menu): """Add the floor specific options to the context menu. The context menu is the drop down menu that opens when the user right @@ -402,12 +439,15 @@ class _ViewProviderFloor: called. """ - from PySide import QtCore,QtGui + from PySide import QtCore, QtGui import Arch_rc - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - action1 = QtGui.QAction(QtGui.QIcon(":/icons/Arch_BuildingPart.svg"),"Convert to BuildingPart",menu) - QtCore.QObject.connect(action1,QtCore.SIGNAL("triggered()"),self.convertToBuildingPart) + action1 = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_BuildingPart.svg"), "Convert to BuildingPart", menu + ) + QtCore.QObject.connect(action1, QtCore.SIGNAL("triggered()"), self.convertToBuildingPart) menu.addAction(action1) def convertToBuildingPart(self): @@ -416,11 +456,12 @@ class _ViewProviderFloor: TODO: May be depreciated? """ - if hasattr(self,"Object"): + if hasattr(self, "Object"): import ArchBuildingPart from draftutils import todo - todo.ToDo.delay(ArchBuildingPart.convertFloors,self.Object) + + todo.ToDo.delay(ArchBuildingPart.convertFloors, self.Object) if FreeCAD.GuiUp: - FreeCADGui.addCommand('Arch_Floor',_CommandFloor()) + FreeCADGui.addCommand("Arch_Floor", _CommandFloor()) diff --git a/src/Mod/BIM/ArchFrame.py b/src/Mod/BIM/ArchFrame.py index 1dd982546c..de0d545be1 100644 --- a/src/Mod/BIM/ArchFrame.py +++ b/src/Mod/BIM/ArchFrame.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Frame" +__title__ = "FreeCAD Arch Frame" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchFrame # \ingroup ARCH @@ -45,55 +45,122 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _Frame(ArchComponent.Component): - "A parametric frame object" - def __init__(self,obj): - ArchComponent.Component.__init__(self,obj) + def __init__(self, obj): + ArchComponent.Component.__init__(self, obj) self.Type = "Frame" self.setProperties(obj) obj.IfcType = "Railing" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Profile" in pl: - obj.addProperty("App::PropertyLink","Profile","Frame",QT_TRANSLATE_NOOP("App::Property","The profile used to build this frame"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Profile", + "Frame", + QT_TRANSLATE_NOOP("App::Property", "The profile used to build this frame"), + locked=True, + ) if not "Align" in pl: - obj.addProperty("App::PropertyBool","Align","Frame",QT_TRANSLATE_NOOP("App::Property","Specifies if the profile must be aligned with the extrusion wires"), locked=True) + obj.addProperty( + "App::PropertyBool", + "Align", + "Frame", + QT_TRANSLATE_NOOP( + "App::Property", + "Specifies if the profile must be aligned with the extrusion wires", + ), + locked=True, + ) obj.Align = True if not "Offset" in pl: - obj.addProperty("App::PropertyVectorDistance","Offset","Frame",QT_TRANSLATE_NOOP("App::Property","An offset vector between the base sketch and the frame"), locked=True) + obj.addProperty( + "App::PropertyVectorDistance", + "Offset", + "Frame", + QT_TRANSLATE_NOOP( + "App::Property", "An offset vector between the base sketch and the frame" + ), + locked=True, + ) if not "BasePoint" in pl: - obj.addProperty("App::PropertyInteger","BasePoint","Frame",QT_TRANSLATE_NOOP("App::Property","Crossing point of the path on the profile."), locked=True) + obj.addProperty( + "App::PropertyInteger", + "BasePoint", + "Frame", + QT_TRANSLATE_NOOP("App::Property", "Crossing point of the path on the profile."), + locked=True, + ) if not "ProfilePlacement" in pl: - obj.addProperty("App::PropertyPlacement","ProfilePlacement","Frame",QT_TRANSLATE_NOOP("App::Property","An optional additional placement to add to the profile before extruding it"), locked=True) + obj.addProperty( + "App::PropertyPlacement", + "ProfilePlacement", + "Frame", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional additional placement to add to the profile before extruding it", + ), + locked=True, + ) if not "Rotation" in pl: - obj.addProperty("App::PropertyAngle","Rotation","Frame",QT_TRANSLATE_NOOP("App::Property","The rotation of the profile around its extrusion axis"), locked=True) + obj.addProperty( + "App::PropertyAngle", + "Rotation", + "Frame", + QT_TRANSLATE_NOOP( + "App::Property", "The rotation of the profile around its extrusion axis" + ), + locked=True, + ) if not "Edges" in pl: - obj.addProperty("App::PropertyEnumeration","Edges","Frame",QT_TRANSLATE_NOOP("App::Property","The type of edges to consider"), locked=True) - obj.Edges = ["All edges","Vertical edges","Horizontal edges","Bottom horizontal edges","Top horizontal edges"] + obj.addProperty( + "App::PropertyEnumeration", + "Edges", + "Frame", + QT_TRANSLATE_NOOP("App::Property", "The type of edges to consider"), + locked=True, + ) + obj.Edges = [ + "All edges", + "Vertical edges", + "Horizontal edges", + "Bottom horizontal edges", + "Top horizontal edges", + ] if not "Fuse" in pl: - obj.addProperty("App::PropertyBool","Fuse","Frame",QT_TRANSLATE_NOOP("App::Property","If true, geometry is fused, otherwise a compound"), locked=True) + obj.addProperty( + "App::PropertyBool", + "Fuse", + "Frame", + QT_TRANSLATE_NOOP( + "App::Property", "If true, geometry is fused, otherwise a compound" + ), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Frame" - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -128,8 +195,9 @@ class _Frame(ArchComponent.Component): import math import DraftGeomUtils import Part + baseprofile = obj.Profile.Shape.copy() - if hasattr(obj,"ProfilePlacement"): + if hasattr(obj, "ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: @@ -143,30 +211,46 @@ class _Frame(ArchComponent.Component): shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) edges = obj.Base.Shape.Edges - if hasattr(obj,"Edges"): + if hasattr(obj, "Edges"): if obj.Edges == "Vertical edges": - rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) - edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] + rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) + edges = [ + e + for e in edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] elif obj.Edges == "Horizontal edges": - rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] + rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) + edges = [ + e + for e in edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] elif obj.Edges == "Top horizontal edges": - rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] - edges = sorted(edges,key=lambda x: x.CenterOfMass.z,reverse=True) + rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) + edges = [ + e + for e in edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] + edges = sorted(edges, key=lambda x: x.CenterOfMass.z, reverse=True) z = edges[0].CenterOfMass.z - edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] + edges = [e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001] elif obj.Edges == "Bottom horizontal edges": - rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] - edges = sorted(edges,key=lambda x: x.CenterOfMass.z) + rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) + edges = [ + e + for e in edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] + edges = sorted(edges, key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z - edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] + edges = [e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001] for e in edges: bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() - rot = None # New rotation. + rot = None # New rotation. # Supplying FreeCAD.Rotation() with two parallel vectors and # a null vector may seem strange, but the function is perfectly # able to handle this. Its algorithm will use default axes in @@ -186,11 +270,13 @@ class _Frame(ArchComponent.Component): try: basepoint = basepointliste[obj.BasePoint] except IndexError: - FreeCAD.Console.PrintMessage(translate("Arch", "Crossing point not found in profile.")+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Crossing point not found in profile.") + "\n" + ) basepoint = basepointliste[0] else: basepoint = profile.Placement.Base - delta = bpoint.sub(basepoint) # Translation vector. + delta = bpoint.sub(basepoint) # Translation vector. if obj.Offset and (not DraftVecUtils.isNull(obj.Offset)): if rot is None: delta = delta + obj.Offset @@ -203,7 +289,7 @@ class _Frame(ArchComponent.Component): profile = profile.extrude(bvec) shapes.append(profile) if shapes: - if hasattr(obj,"Fuse"): + if hasattr(obj, "Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) @@ -216,22 +302,22 @@ class _Frame(ArchComponent.Component): class _ViewProviderFrame(ArchComponent.ViewProviderComponent): - "A View Provider for the Frame object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Frame_Tree.svg" def claimChildren(self): p = [] - if hasattr(self,"Object"): + if hasattr(self, "Object"): if self.Object.Profile: p = [self.Object.Profile] - return ArchComponent.ViewProviderComponent.claimChildren(self)+p + return ArchComponent.ViewProviderComponent.claimChildren(self) + p diff --git a/src/Mod/BIM/ArchGrid.py b/src/Mod/BIM/ArchGrid.py index de39ac6ee0..ca350fe85e 100644 --- a/src/Mod/BIM/ArchGrid.py +++ b/src/Mod/BIM/ArchGrid.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Axis System" +__title__ = "FreeCAD Axis System" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchGrid # \ingroup ARCH @@ -44,83 +44,166 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class ArchGrid: - "The Grid object" - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "Grid" self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Rows" in pl: - obj.addProperty("App::PropertyInteger","Rows","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The number of rows'), locked=True) + obj.addProperty( + "App::PropertyInteger", + "Rows", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The number of rows"), + locked=True, + ) if not "Columns" in pl: - obj.addProperty("App::PropertyInteger","Columns","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The number of columns'), locked=True) + obj.addProperty( + "App::PropertyInteger", + "Columns", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The number of columns"), + locked=True, + ) if not "RowSize" in pl: - obj.addProperty("App::PropertyFloatList","RowSize","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The sizes of rows'), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "RowSize", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The sizes of rows"), + locked=True, + ) if not "ColumnSize" in pl: - obj.addProperty("App::PropertyFloatList","ColumnSize","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The sizes of columns'), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "ColumnSize", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The sizes of columns"), + locked=True, + ) if not "Spans" in pl: - obj.addProperty("App::PropertyStringList","Spans","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The span ranges of cells that are merged together'), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Spans", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The span ranges of cells that are merged together"), + locked=True, + ) if not "PointsOutput" in pl: - obj.addProperty("App::PropertyEnumeration","PointsOutput","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The type of 3D points produced by this grid object'), locked=True) - obj.PointsOutput = ["Vertices","Edges","Vertical Edges","Horizontal Edges","Faces"] + obj.addProperty( + "App::PropertyEnumeration", + "PointsOutput", + "Grid", + QT_TRANSLATE_NOOP( + "Arch_Grid", "The type of 3D points produced by this grid object" + ), + locked=True, + ) + obj.PointsOutput = ["Vertices", "Edges", "Vertical Edges", "Horizontal Edges", "Faces"] if not "Width" in pl: - obj.addProperty("App::PropertyLength","Width","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The total width of this grid'), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The total width of this grid"), + locked=True, + ) if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The total height of this grid'), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The total height of this grid"), + locked=True, + ) if not "AutoWidth" in pl: - obj.addProperty("App::PropertyLength","AutoWidth","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'Creates automatic column divisions (set to 0 to disable)'), locked=True) + obj.addProperty( + "App::PropertyLength", + "AutoWidth", + "Grid", + QT_TRANSLATE_NOOP( + "Arch_Grid", "Creates automatic column divisions (set to 0 to disable)" + ), + locked=True, + ) if not "AutoHeight" in pl: - obj.addProperty("App::PropertyLength","AutoHeight","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'Creates automatic row divisions (set to 0 to disable)'), locked=True) + obj.addProperty( + "App::PropertyLength", + "AutoHeight", + "Grid", + QT_TRANSLATE_NOOP( + "Arch_Grid", "Creates automatic row divisions (set to 0 to disable)" + ), + locked=True, + ) if not "Reorient" in pl: - obj.addProperty("App::PropertyBool","Reorient","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'When in edge midpoint mode, if this grid must reorient its children along edge normals or not'), locked=True) + obj.addProperty( + "App::PropertyBool", + "Reorient", + "Grid", + QT_TRANSLATE_NOOP( + "Arch_Grid", + "When in edge midpoint mode, if this grid must reorient its children along edge normals or not", + ), + locked=True, + ) if not "HiddenFaces" in pl: - obj.addProperty("App::PropertyIntegerList","HiddenFaces","Grid",QT_TRANSLATE_NOOP("Arch_Grid",'The indices of faces to hide'), locked=True) + obj.addProperty( + "App::PropertyIntegerList", + "HiddenFaces", + "Grid", + QT_TRANSLATE_NOOP("Arch_Grid", "The indices of faces to hide"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) - def getSizes(self,obj): - + def getSizes(self, obj): "returns rowsizes,columnsizes,spangroups" if not obj.Height.Value: - return [],[],[] + return [], [], [] if not obj.Width.Value: - return [],[],[] + return [], [], [] if (not obj.Rows) and (not obj.AutoHeight.Value): - return [],[],[] + return [], [], [] if (not obj.Columns) and (not obj.AutoWidth.Value): - return [],[],[] + return [], [], [] # rescale rows rowsizes = [] if obj.AutoHeight.Value: if obj.AutoHeight.Value > obj.Height.Value: - FreeCAD.Console.PrintError(translate("Arch","Auto height is larger than height")) - return [],[],[] - rows = int(math.floor(obj.Height.Value/obj.AutoHeight.Value)) + FreeCAD.Console.PrintError(translate("Arch", "Auto height is larger than height")) + return [], [], [] + rows = int(math.floor(obj.Height.Value / obj.AutoHeight.Value)) for i in range(rows): rowsizes.append(obj.AutoHeight.Value) - rowsizes.append(obj.Height.Value-rows*obj.AutoHeight.Value) + rowsizes.append(obj.Height.Value - rows * obj.AutoHeight.Value) else: reserved_rowsize = sum(v for v in obj.RowSize) if reserved_rowsize > obj.Height.Value: - FreeCAD.Console.PrintError(translate("Arch","Total row size is larger than height")) - return [],[],[] + FreeCAD.Console.PrintError( + translate("Arch", "Total row size is larger than height") + ) + return [], [], [] for i in range(obj.Rows): v = 0 if i < len(obj.RowSize): @@ -141,17 +224,19 @@ class ArchGrid: columnsizes = [] if obj.AutoWidth.Value: if obj.AutoWidth.Value > obj.Width.Value: - FreeCAD.Console.PrintError(translate("Arch","Auto width is larger than width")) - return [],[],[] - cols = int(math.floor(obj.Width.Value/obj.AutoWidth.Value)) + FreeCAD.Console.PrintError(translate("Arch", "Auto width is larger than width")) + return [], [], [] + cols = int(math.floor(obj.Width.Value / obj.AutoWidth.Value)) for i in range(cols): columnsizes.append(obj.AutoWidth.Value) - columnsizes.append(obj.Width.Value-cols*obj.AutoWidth.Value) + columnsizes.append(obj.Width.Value - cols * obj.AutoWidth.Value) else: reserved_columnsize = sum(v for v in obj.ColumnSize) if reserved_columnsize > obj.Width.Value: - FreeCAD.Console.PrintError(translate("Arch","Total column size is larger than width")) - return [],[],[] + FreeCAD.Console.PrintError( + translate("Arch", "Total column size is larger than width") + ) + return [], [], [] for i in range(obj.Columns): v = 0 if i < len(obj.ColumnSize): @@ -175,15 +260,15 @@ class ArchGrid: span = [int(i.strip()) for i in s.split(",")] for row in range(span[2]): for column in range(span[3]): - nspan.append((span[0]+row)*obj.Columns + (span[1]+column)) + nspan.append((span[0] + row) * obj.Columns + (span[1] + column)) spangroups.append(nspan) - return rowsizes,columnsizes,spangroups + return rowsizes, columnsizes, spangroups - def execute(self,obj): + def execute(self, obj): pl = obj.Placement - rowsizes,columnsizes,spangroups = self.getSizes(obj) - #print rowsizes,columnsizes,spangroups + rowsizes, columnsizes, spangroups = self.getSizes(obj) + # print rowsizes,columnsizes,spangroups # create one face for each cell faces = [] facenumber = 0 @@ -191,11 +276,11 @@ class ArchGrid: for row in rowsizes: columnoffset = 0 for column in columnsizes: - v1 = FreeCAD.Vector(columnoffset,rowoffset,0) - v2 = v1.add(FreeCAD.Vector(column,0,0)) - v3 = v2.add(FreeCAD.Vector(0,-row,0)) - v4 = v3.add(FreeCAD.Vector(-column,0,0)) - f = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) + v1 = FreeCAD.Vector(columnoffset, rowoffset, 0) + v2 = v1.add(FreeCAD.Vector(column, 0, 0)) + v3 = v2.add(FreeCAD.Vector(0, -row, 0)) + v4 = v3.add(FreeCAD.Vector(-column, 0, 0)) + f = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) if not facenumber in obj.HiddenFaces: spanning = False for i in range(len(spangroups)): @@ -219,8 +304,7 @@ class ArchGrid: obj.Shape = Part.makeCompound(faces) obj.Placement = pl - def getPoints(self,obj): - + def getPoints(self, obj): "returns the gridpoints" def remdupes(pts): @@ -230,17 +314,26 @@ class ArchGrid: if not p in ret: ret.append(p) return ret + if obj.PointsOutput == "Vertices": return [v.Point for v in obj.Shape.Vertexes] elif obj.PointsOutput == "Edges": return remdupes([e.CenterOfMass for e in obj.Shape.Edges]) elif obj.PointsOutput == "Vertical Edges": - rv = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) - edges = [e for e in obj.Shape.Edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] + rv = obj.Placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) + edges = [ + e + for e in obj.Shape.Edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] return remdupes([e.CenterOfMass for e in edges]) elif obj.PointsOutput == "Horizontal Edges": - rv = obj.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - edges = [e for e in obj.Shape.Edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] + rv = obj.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) + edges = [ + e + for e in obj.Shape.Edges + if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] + ] return remdupes([e.CenterOfMass for e in edges]) else: return [f.CenterOfMass for f in obj.Shape.Faces] @@ -249,22 +342,22 @@ class ArchGrid: return None - def loads(self,state): + def loads(self, state): self.Type = "Grid" class ViewProviderArchGrid: - "A View Provider for the Arch Grid" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): import Arch_rc + return ":/icons/Arch_Grid.svg" def attach(self, vobj): @@ -286,13 +379,10 @@ class ViewProviderArchGrid: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) def edit(self): @@ -302,16 +392,15 @@ class ViewProviderArchGrid: return None - def loads(self,state): + def loads(self, state): return None class ArchGridTaskPanel: + """A TaskPanel for the Arch Grid""" - '''A TaskPanel for the Arch Grid''' - - def __init__(self,obj): + def __init__(self, obj): # length, width, label self.width = 0 @@ -371,9 +460,9 @@ class ArchGridTaskPanel: hbox2.addWidget(self.delSpanButton) self.delSpanButton.setEnabled(False) - #signals - QtCore.QObject.connect(self.widthUi,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) - QtCore.QObject.connect(self.heightUi,QtCore.SIGNAL("valueChanged(double)"),self.setHeight) + # signals + QtCore.QObject.connect(self.widthUi, QtCore.SIGNAL("valueChanged(double)"), self.setWidth) + QtCore.QObject.connect(self.heightUi, QtCore.SIGNAL("valueChanged(double)"), self.setHeight) QtCore.QObject.connect(self.table, QtCore.SIGNAL("itemSelectionChanged()"), self.checkSpan) QtCore.QObject.connect(self.addRowButton, QtCore.SIGNAL("clicked()"), self.addRow) QtCore.QObject.connect(self.delRowButton, QtCore.SIGNAL("clicked()"), self.delRow) @@ -381,12 +470,20 @@ class ArchGridTaskPanel: QtCore.QObject.connect(self.delColumnButton, QtCore.SIGNAL("clicked()"), self.delColumn) QtCore.QObject.connect(self.spanButton, QtCore.SIGNAL("clicked()"), self.addSpan) QtCore.QObject.connect(self.delSpanButton, QtCore.SIGNAL("clicked()"), self.removeSpan) - QtCore.QObject.connect(self.table.horizontalHeader(),QtCore.SIGNAL("sectionDoubleClicked(int)"), self.editHorizontalHeader) - QtCore.QObject.connect(self.table.verticalHeader(),QtCore.SIGNAL("sectionDoubleClicked(int)"), self.editVerticalHeader) + QtCore.QObject.connect( + self.table.horizontalHeader(), + QtCore.SIGNAL("sectionDoubleClicked(int)"), + self.editHorizontalHeader, + ) + QtCore.QObject.connect( + self.table.verticalHeader(), + QtCore.SIGNAL("sectionDoubleClicked(int)"), + self.editVerticalHeader, + ) self.update() self.retranslateUi() - def retranslateUi(self,widget=None): + def retranslateUi(self, widget=None): self.form.setWindowTitle(QtGui.QApplication.translate("Arch", "Grid", None)) self.wLabel.setText(QtGui.QApplication.translate("Arch", "Total width", None)) @@ -397,7 +494,15 @@ class ArchGridTaskPanel: self.delColumnButton.setText(QtGui.QApplication.translate("Arch", "Delete Column", None)) self.spanButton.setText(QtGui.QApplication.translate("Arch", "Create Span", None)) self.delSpanButton.setText(QtGui.QApplication.translate("Arch", "Remove Span", None)) - self.title.setText(QtGui.QApplication.translate("Arch", "Rows", None)+": "+str(self.table.rowCount())+" / "+QtGui.QApplication.translate("Arch", "Columns", None)+": "+str(self.table.columnCount())) + self.title.setText( + QtGui.QApplication.translate("Arch", "Rows", None) + + ": " + + str(self.table.rowCount()) + + " / " + + QtGui.QApplication.translate("Arch", "Columns", None) + + ": " + + str(self.table.columnCount()) + ) def update(self): @@ -405,16 +510,20 @@ class ArchGridTaskPanel: if self.obj.Rows: self.table.setRowCount(self.obj.Rows) vlabels = ["0" for i in range(self.obj.Rows)] - for i,v in enumerate(self.obj.RowSize): + for i, v in enumerate(self.obj.RowSize): if i < len(vlabels): - vlabels[i] = FreeCAD.Units.Quantity(v,FreeCAD.Units.Length).getUserPreferred()[0] + vlabels[i] = FreeCAD.Units.Quantity(v, FreeCAD.Units.Length).getUserPreferred()[ + 0 + ] self.table.setVerticalHeaderLabels(vlabels) if self.obj.Columns: self.table.setColumnCount(self.obj.Columns) hlabels = ["0" for i in range(self.obj.Columns)] - for i,v in enumerate(self.obj.ColumnSize): + for i, v in enumerate(self.obj.ColumnSize): if i < len(hlabels): - hlabels[i] = FreeCAD.Units.Quantity(v,FreeCAD.Units.Length).getUserPreferred()[0] + hlabels[i] = FreeCAD.Units.Quantity(v, FreeCAD.Units.Length).getUserPreferred()[ + 0 + ] self.table.setHorizontalHeaderLabels(hlabels) self.widthUi.setText(self.obj.Width.getUserPreferred()[0]) self.heightUi.setText(self.obj.Height.getUserPreferred()[0]) @@ -422,7 +531,7 @@ class ArchGridTaskPanel: for s in self.obj.Spans: span = [int(i.strip()) for i in s.split(",")] if len(span) == 4: - self.table.setSpan(span[0],span[1],span[2],span[3]) + self.table.setSpan(span[0], span[1], span[2], span[3]) self.spans.append(span) def checkSpan(self): @@ -436,8 +545,10 @@ class ArchGridTaskPanel: if (r.rowCount() * r.columnCount()) > 1: self.spanButton.setEnabled(True) elif (r.rowCount() * r.columnCount()) == 1: - if self.table.rowSpan(r.topRow(),r.leftColumn()) > 1 \ - or self.table.columnSpan(r.topRow(),r.leftColumn()) > 1: + if ( + self.table.rowSpan(r.topRow(), r.leftColumn()) > 1 + or self.table.columnSpan(r.topRow(), r.leftColumn()) > 1 + ): self.delSpanButton.setEnabled(True) else: self.spanButton.setEnabled(False) @@ -446,8 +557,8 @@ class ArchGridTaskPanel: def addRow(self): c = self.table.currentRow() - self.table.insertRow(c+1) - self.table.setVerticalHeaderItem(c+1,QtGui.QTableWidgetItem("0")) + self.table.insertRow(c + 1) + self.table.setVerticalHeaderItem(c + 1, QtGui.QTableWidgetItem("0")) self.retranslateUi() def delRow(self): @@ -459,8 +570,8 @@ class ArchGridTaskPanel: def addColumn(self): c = self.table.currentColumn() - self.table.insertColumn(c+1) - self.table.setHorizontalHeaderItem(c+1,QtGui.QTableWidgetItem("0")) + self.table.insertColumn(c + 1) + self.table.setHorizontalHeaderItem(c + 1, QtGui.QTableWidgetItem("0")) self.retranslateUi() def delColumn(self): @@ -473,8 +584,8 @@ class ArchGridTaskPanel: for r in self.table.selectedRanges(): if r.rowCount() * r.columnCount() > 1: - self.table.setSpan(r.topRow(),r.leftColumn(),r.rowCount(),r.columnCount()) - self.spans.append([r.topRow(),r.leftColumn(),r.rowCount(),r.columnCount()]) + self.table.setSpan(r.topRow(), r.leftColumn(), r.rowCount(), r.columnCount()) + self.spans.append([r.topRow(), r.leftColumn(), r.rowCount(), r.columnCount()]) return if len(self.table.selectedRanges()) > 1: tr = 99999 @@ -491,18 +602,20 @@ class ArchGridTaskPanel: if r.rightColumn() > rc: rc = r.rightColumn() if (rc >= lc) and (br >= tr): - self.table.setSpan(tr,lc,(br-tr)+1,(rc-lc)+1) - self.spans.append([tr,lc,(br-tr)+1,(rc-lc)+1]) + self.table.setSpan(tr, lc, (br - tr) + 1, (rc - lc) + 1) + self.spans.append([tr, lc, (br - tr) + 1, (rc - lc) + 1]) def removeSpan(self): for r in self.table.selectedRanges(): if r.rowCount() * r.columnCount() == 1: - if self.table.rowSpan(r.topRow(),r.leftColumn()) > 1 \ - or self.table.columnSpan(r.topRow(),r.leftColumn()) > 1: - self.table.setSpan(r.topRow(),r.leftColumn(),1,1) + if ( + self.table.rowSpan(r.topRow(), r.leftColumn()) > 1 + or self.table.columnSpan(r.topRow(), r.leftColumn()) > 1 + ): + self.table.setSpan(r.topRow(), r.leftColumn(), 1, 1) f = None - for i,s in enumerate(self.spans): + for i, s in enumerate(self.spans): if (s[0] == r.topRow()) and (s[1] == r.leftColumn()): f = i break @@ -511,21 +624,21 @@ class ArchGridTaskPanel: def editHorizontalHeader(self, index): - val,ok = QtGui.QInputDialog.getText(None,'Edit size','New size') + val, ok = QtGui.QInputDialog.getText(None, "Edit size", "New size") if ok: - self.table.setHorizontalHeaderItem(index,QtGui.QTableWidgetItem(val)) + self.table.setHorizontalHeaderItem(index, QtGui.QTableWidgetItem(val)) def editVerticalHeader(self, index): - val,ok = QtGui.QInputDialog.getText(None,'Edit size','New size') + val, ok = QtGui.QInputDialog.getText(None, "Edit size", "New size") if ok: - self.table.setVerticalHeaderItem(index,QtGui.QTableWidgetItem(val)) + self.table.setVerticalHeaderItem(index, QtGui.QTableWidgetItem(val)) - def setWidth(self,d): + def setWidth(self, d): self.width = d - def setHeight(self,d): + def setHeight(self, d): self.height = d @@ -535,8 +648,14 @@ class ArchGridTaskPanel: self.obj.Height = self.height self.obj.Rows = self.table.rowCount() self.obj.Columns = self.table.columnCount() - self.obj.RowSize = [FreeCAD.Units.Quantity(self.table.verticalHeaderItem(i).text()).Value for i in range(self.table.rowCount())] - self.obj.ColumnSize = [FreeCAD.Units.Quantity(self.table.horizontalHeaderItem(i).text()).Value for i in range(self.table.columnCount())] + self.obj.RowSize = [ + FreeCAD.Units.Quantity(self.table.verticalHeaderItem(i).text()).Value + for i in range(self.table.rowCount()) + ] + self.obj.ColumnSize = [ + FreeCAD.Units.Quantity(self.table.horizontalHeaderItem(i).text()).Value + for i in range(self.table.columnCount()) + ] self.obj.Spans = [str(s)[1:-1] for s in self.spans] FreeCAD.ActiveDocument.recompute() FreeCADGui.ActiveDocument.resetEdit() diff --git a/src/Mod/BIM/ArchIFC.py b/src/Mod/BIM/ArchIFC.py index 2d2846ca10..7443c66614 100644 --- a/src/Mod/BIM/ArchIFC.py +++ b/src/Mod/BIM/ArchIFC.py @@ -36,15 +36,18 @@ import ArchIFCSchema if FreeCAD.GuiUp: from PySide.QtCore import QT_TRANSLATE_NOOP else: - def QT_TRANSLATE_NOOP(ctx,txt): + + def QT_TRANSLATE_NOOP(ctx, txt): return txt def uncamel(t): - return ''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] + return "".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:] + IfcTypes = [uncamel(t) for t in ArchIFCSchema.IfcProducts.keys()] + class IfcRoot: """This class defines the common methods and properties for managing IFC data. @@ -68,14 +71,32 @@ class IfcRoot: """ if not "IfcData" in obj.PropertiesList: - obj.addProperty("App::PropertyMap","IfcData","IFC",QT_TRANSLATE_NOOP("App::Property","IFC data"), locked=True) + obj.addProperty( + "App::PropertyMap", + "IfcData", + "IFC", + QT_TRANSLATE_NOOP("App::Property", "IFC data"), + locked=True, + ) if not "IfcType" in obj.PropertiesList: - obj.addProperty("App::PropertyEnumeration","IfcType","IFC",QT_TRANSLATE_NOOP("App::Property","The type of this object"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "IfcType", + "IFC", + QT_TRANSLATE_NOOP("App::Property", "The type of this object"), + locked=True, + ) obj.IfcType = self.getCanonicalisedIfcTypes() if not "IfcProperties" in obj.PropertiesList: - obj.addProperty("App::PropertyMap","IfcProperties","IFC",QT_TRANSLATE_NOOP("App::Property","IFC properties of this object"), locked=True) + obj.addProperty( + "App::PropertyMap", + "IfcProperties", + "IFC", + QT_TRANSLATE_NOOP("App::Property", "IFC properties of this object"), + locked=True, + ) self.migrateDeprecatedAttributes(obj) @@ -198,7 +219,9 @@ class IfcRoot: """ schema = self.getIfcSchema() - return [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in schema.keys()] + return [ + "".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:] for t in schema.keys() + ] def getIfcAttributeSchema(self, ifcTypeSchema, name): """Get the schema of an IFC attribute with the given name. @@ -224,7 +247,7 @@ class IfcRoot: """ for attribute in ifcTypeSchema["attributes"]: - if attribute["name"].replace(' ', '') == name: + if attribute["name"].replace(" ", "") == name: return attribute return None @@ -254,10 +277,12 @@ class IfcRoot: """ for attribute in ifcTypeSchema["attributes"]: - if attribute["name"] in obj.PropertiesList \ - or attribute["name"] == "RefLatitude" \ - or attribute["name"] == "RefLongitude" \ - or attribute["name"] == "Name": + if ( + attribute["name"] in obj.PropertiesList + or attribute["name"] == "RefLatitude" + or attribute["name"] == "RefLongitude" + or attribute["name"] == "Name" + ): continue self.addIfcAttribute(obj, attribute) self.addIfcAttributeValueExpressions(obj, attribute) @@ -288,17 +313,25 @@ class IfcRoot: obj.IfcData = IfcData if attribute["is_enum"]: - obj.addProperty("App::PropertyEnumeration", - attribute["name"], - "IFC Attributes", - QT_TRANSLATE_NOOP("App::Property", "Description of IFC attributes are not yet implemented")) + obj.addProperty( + "App::PropertyEnumeration", + attribute["name"], + "IFC Attributes", + QT_TRANSLATE_NOOP( + "App::Property", "Description of IFC attributes are not yet implemented" + ), + ) setattr(obj, attribute["name"], attribute["enum_values"]) else: propertyType = "App::" + ArchIFCSchema.IfcTypes[attribute["type"]]["property"] - obj.addProperty(propertyType, - attribute["name"], - "IFC Attributes", - QT_TRANSLATE_NOOP("App::Property", "Description of IFC attributes are not yet implemented")) + obj.addProperty( + propertyType, + attribute["name"], + "IFC Attributes", + QT_TRANSLATE_NOOP( + "App::Property", "Description of IFC attributes are not yet implemented" + ), + ) def addIfcAttributeValueExpressions(self, obj, attribute): """Add expressions for IFC attributes, so they stay accurate with the object. @@ -327,8 +360,10 @@ class IfcRoot: The schema of the attribute to add the expression for. """ - if obj.getGroupOfProperty(attribute["name"]) != "IFC Attributes" \ - or attribute["name"] not in obj.PropertiesList: + if ( + obj.getGroupOfProperty(attribute["name"]) != "IFC Attributes" + or attribute["name"] not in obj.PropertiesList + ): return if attribute["name"] == "OverallWidth": if "Length" in obj.PropertiesList: @@ -431,27 +466,31 @@ class IfcRoot: obj.removeProperty(property) def migrateDeprecatedAttributes(self, obj): - """Update the object to use the newer property names for IFC related properties. - """ + """Update the object to use the newer property names for IFC related properties.""" if "Role" in obj.PropertiesList: r = obj.Role obj.removeProperty("Role") if r in IfcTypes: obj.IfcType = r - FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" Role property to IfcType\n") + FreeCAD.Console.PrintMessage( + "Upgrading " + obj.Label + " Role property to IfcType\n" + ) if "IfcRole" in obj.PropertiesList: r = obj.IfcRole obj.removeProperty("IfcRole") if r in IfcTypes: obj.IfcType = r - FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" IfcRole property to IfcType\n") + FreeCAD.Console.PrintMessage( + "Upgrading " + obj.Label + " IfcRole property to IfcType\n" + ) - if "IfcAttributes"in obj.PropertiesList: + if "IfcAttributes" in obj.PropertiesList: obj.IfcData = obj.IfcAttributes obj.removeProperty("IfcAttributes") + class IfcProduct(IfcRoot): """This class is subclassed by classes that have a specific location in space. @@ -473,6 +512,7 @@ class IfcProduct(IfcRoot): """ return ArchIFCSchema.IfcProducts + class IfcContext(IfcRoot): """This class is subclassed by classes that define a particular context. diff --git a/src/Mod/BIM/ArchIFCSchema.py b/src/Mod/BIM/ArchIFCSchema.py index de455db3c4..50bb44d35d 100644 --- a/src/Mod/BIM/ArchIFCSchema.py +++ b/src/Mod/BIM/ArchIFCSchema.py @@ -39,14 +39,23 @@ from draftutils import params ifcVersions = ["IFC4", "IFC2X3"] IfcVersion = ifcVersions[params.get_param_arch("IfcVersion")] -with open(os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", -"ifc_contexts_" + IfcVersion + ".json")) as f: +with open( + os.path.join( + FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "ifc_contexts_" + IfcVersion + ".json" + ) +) as f: IfcContexts = json.load(f) -with open(os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", -"ifc_products_" + IfcVersion + ".json")) as f: +with open( + os.path.join( + FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "ifc_products_" + IfcVersion + ".json" + ) +) as f: IfcProducts = json.load(f) -with open(os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", -"ifc_types_" + IfcVersion + ".json")) as f: +with open( + os.path.join( + FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "ifc_types_" + IfcVersion + ".json" + ) +) as f: IfcTypes = json.load(f) diff --git a/src/Mod/BIM/ArchIFCView.py b/src/Mod/BIM/ArchIFCView.py index 23a645badc..737145f305 100644 --- a/src/Mod/BIM/ArchIFCView.py +++ b/src/Mod/BIM/ArchIFCView.py @@ -32,9 +32,11 @@ if FreeCAD.GuiUp: import FreeCADGui from draftutils.translate import translate else: - def translate(ctxt,txt): + + def translate(ctxt, txt): return txt + class IfcContextView: """A default view provider for IfcContext objects.""" @@ -56,25 +58,22 @@ class IfcContextView: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) # The default Part::FeaturePython context menu contains a `Set colors` # option. This option does not makes sense here. We therefore # override this menu and have to add our own `Transform` item. # To override the default menu this function must return `True`. - actionTransform = QtGui.QAction(FreeCADGui.getIcon("Std_TransformManip.svg"), - translate("Command", "Transform"), # Context `Command` instead of `Arch`. - menu) - QtCore.QObject.connect(actionTransform, - QtCore.SIGNAL("triggered()"), - self.transform) + actionTransform = QtGui.QAction( + FreeCADGui.getIcon("Std_TransformManip.svg"), + translate("Command", "Transform"), # Context `Command` instead of `Arch`. + menu, + ) + QtCore.QObject.connect(actionTransform, QtCore.SIGNAL("triggered()"), self.transform) menu.addAction(actionTransform) return True @@ -88,7 +87,7 @@ class IfcContextView: def dumps(self): return None - def loads(self,state): + def loads(self, state): return None @@ -112,7 +111,9 @@ class IfcContextUI: data = {} for lineEdit in self.lineEditObjects: data[lineEdit.objectName()] = lineEdit.text() - ArchIFC.IfcRoot.setObjIfcComplexAttributeValue(self, self.object, "RepresentationContexts", data) + ArchIFC.IfcRoot.setObjIfcComplexAttributeValue( + self, self.object, "RepresentationContexts", data + ) FreeCADGui.ActiveDocument.resetEdit() return True @@ -146,7 +147,9 @@ class IfcContextUI: self.baseLayout.addLayout(self.createFormEntry("eastings", "Eastings")) self.baseLayout.addLayout(self.createFormEntry("northings", "Northings")) self.baseLayout.addLayout(self.createFormEntry("orthogonal_height", "Orthogonal height")) - self.baseLayout.addLayout(self.createFormEntry("true_north", "True north (anti-clockwise from +Y)")) + self.baseLayout.addLayout( + self.createFormEntry("true_north", "True north (anti-clockwise from +Y)") + ) self.baseLayout.addLayout(self.createFormEntry("scale", "Scale")) def prefillMapConversionForm(self): @@ -155,7 +158,9 @@ class IfcContextUI: Gets the existing value from the object's IfcData, specifically the complex attribute, RepresentationContexts. """ - data = ArchIFC.IfcRoot.getObjIfcComplexAttribute(self, self.object, "RepresentationContexts") + data = ArchIFC.IfcRoot.getObjIfcComplexAttribute( + self, self.object, "RepresentationContexts" + ) for lineEdit in self.lineEditObjects: if lineEdit.objectName() in data: lineEdit.setText(data[lineEdit.objectName()]) diff --git a/src/Mod/BIM/ArchMaterial.py b/src/Mod/BIM/ArchMaterial.py index ea2347c946..50ecdf39e1 100644 --- a/src/Mod/BIM/ArchMaterial.py +++ b/src/Mod/BIM/ArchMaterial.py @@ -42,68 +42,67 @@ if FreeCAD.GuiUp: from PySide import QtCore, QtGui from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCADGui - import Arch_rc # Needed for access to icons # lgtm [py/unused_import] + import Arch_rc # Needed for access to icons # lgtm [py/unused_import] from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _ArchMaterialContainer: - - "The Material Container" - def __init__(self,obj): + def __init__(self, obj): self.Type = "MaterialContainer" obj.Proxy = self - def execute(self,obj): + def execute(self, obj): return def dumps(self): - if hasattr(self,"Type"): + if hasattr(self, "Type"): return self.Type - def loads(self,state): + def loads(self, state): if state: self.Type = state class _ViewProviderArchMaterialContainer: - - "A View Provider for the Material Container" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): return ":/icons/Arch_Material_Group.svg" - def attach(self,vobj): + def attach(self, vobj): self.Object = vobj.Object def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionMergeByName = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Material_Group.svg"), - translate("Arch", "Merge Duplicates"), - menu) + actionMergeByName = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_Material_Group.svg"), + translate("Arch", "Merge Duplicates"), + menu, + ) actionMergeByName.triggered.connect(self.mergeByName) menu.addAction(actionMergeByName) - actionReorder = QtGui.QAction(translate("Arch", "Reorder Children Alphabetically"), - menu) + actionReorder = QtGui.QAction(translate("Arch", "Reorder Children Alphabetically"), menu) actionReorder.triggered.connect(self.reorder) menu.addAction(actionReorder) def mergeByName(self): - if hasattr(self,"Object"): + if hasattr(self, "Object"): mats = [o for o in self.Object.Group if o.isDerivedFrom("App::MaterialObject")] todelete = [] for mat in mats: @@ -113,7 +112,11 @@ class _ViewProviderArchMaterialContainer: orig = om break else: - if mat.Label[-1].isdigit() and mat.Label[-2].isdigit() and mat.Label[-3].isdigit(): + if ( + mat.Label[-1].isdigit() + and mat.Label[-2].isdigit() + and mat.Label[-3].isdigit() + ): for om in mats: if om.Label == mat.Label[:-3].strip(): orig = om @@ -121,23 +124,37 @@ class _ViewProviderArchMaterialContainer: if orig: for par in mat.InList: for prop in par.PropertiesList: - if getattr(par,prop) == mat: - FreeCAD.Console.PrintMessage("Changed property '"+prop+"' of object "+par.Label+" from "+mat.Label+" to "+orig.Label+"\n") - setattr(par,prop,orig) + if getattr(par, prop) == mat: + FreeCAD.Console.PrintMessage( + "Changed property '" + + prop + + "' of object " + + par.Label + + " from " + + mat.Label + + " to " + + orig.Label + + "\n" + ) + setattr(par, prop, orig) todelete.append(mat) for tod in todelete: if not tod.InList: - FreeCAD.Console.PrintMessage("Merging duplicate material "+tod.Label+"\n") + FreeCAD.Console.PrintMessage("Merging duplicate material " + tod.Label + "\n") FreeCAD.ActiveDocument.removeObject(tod.Name) - elif (len(tod.InList) == 1) and (tod.InList[0].isDerivedFrom("App::DocumentObjectGroup")): - FreeCAD.Console.PrintMessage("Merging duplicate material "+tod.Label+"\n") + elif (len(tod.InList) == 1) and ( + tod.InList[0].isDerivedFrom("App::DocumentObjectGroup") + ): + FreeCAD.Console.PrintMessage("Merging duplicate material " + tod.Label + "\n") FreeCAD.ActiveDocument.removeObject(tod.Name) else: - FreeCAD.Console.PrintMessage("Unable to delete material "+tod.Label+": InList not empty\n") + FreeCAD.Console.PrintMessage( + "Unable to delete material " + tod.Label + ": InList not empty\n" + ) def reorder(self): - if hasattr(self,"Object"): - if hasattr(self.Object,"Group") and self.Object.Group: + if hasattr(self, "Object"): + if hasattr(self.Object, "Group") and self.Object.Group: g = self.Object.Group g.sort(key=lambda obj: obj.Label) self.Object.Group = g @@ -146,82 +163,128 @@ class _ViewProviderArchMaterialContainer: def dumps(self): return None - def loads(self,state): + def loads(self, state): return None class _ArchMaterial: - - "The Material object" - def __init__(self,obj): + def __init__(self, obj): self.Type = "Material" obj.Proxy = self self.setProperties(obj) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): if not "Description" in obj.PropertiesList: - obj.addProperty("App::PropertyString","Description","Material",QT_TRANSLATE_NOOP("App::Property","A description for this material"), locked=True) + obj.addProperty( + "App::PropertyString", + "Description", + "Material", + QT_TRANSLATE_NOOP("App::Property", "A description for this material"), + locked=True, + ) if not "StandardCode" in obj.PropertiesList: - obj.addProperty("App::PropertyString","StandardCode","Material",QT_TRANSLATE_NOOP("App::Property","A standard code (MasterFormat, OmniClass,…)"), locked=True) + obj.addProperty( + "App::PropertyString", + "StandardCode", + "Material", + QT_TRANSLATE_NOOP("App::Property", "A standard code (MasterFormat, OmniClass,…)"), + locked=True, + ) if not "ProductURL" in obj.PropertiesList: - obj.addProperty("App::PropertyString","ProductURL","Material",QT_TRANSLATE_NOOP("App::Property","A URL where to find information about this material"), locked=True) + obj.addProperty( + "App::PropertyString", + "ProductURL", + "Material", + QT_TRANSLATE_NOOP( + "App::Property", "A URL where to find information about this material" + ), + locked=True, + ) if not "Transparency" in obj.PropertiesList: - obj.addProperty("App::PropertyPercent","Transparency","Material",QT_TRANSLATE_NOOP("App::Property","The transparency value of this material"), locked=True) + obj.addProperty( + "App::PropertyPercent", + "Transparency", + "Material", + QT_TRANSLATE_NOOP("App::Property", "The transparency value of this material"), + locked=True, + ) if not "Color" in obj.PropertiesList: - obj.addProperty("App::PropertyColor","Color","Material",QT_TRANSLATE_NOOP("App::Property","The color of this material"), locked=True) + obj.addProperty( + "App::PropertyColor", + "Color", + "Material", + QT_TRANSLATE_NOOP("App::Property", "The color of this material"), + locked=True, + ) if not "SectionColor" in obj.PropertiesList: - obj.addProperty("App::PropertyColor","SectionColor","Material",QT_TRANSLATE_NOOP("App::Property","The color of this material when cut"), locked=True) + obj.addProperty( + "App::PropertyColor", + "SectionColor", + "Material", + QT_TRANSLATE_NOOP("App::Property", "The color of this material when cut"), + locked=True, + ) - def isSameColor(self,c1,c2): + def isSameColor(self, c1, c2): r = 4 - if round(c1[0],r) == round(c2[0],r): - if round(c1[1],r) == round(c2[1],r): - if round(c1[2],r) == round(c2[2],r): + if round(c1[0], r) == round(c2[0], r): + if round(c1[1], r) == round(c2[1], r): + if round(c1[2], r) == round(c2[2], r): return True return False - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): d = obj.Material if prop == "Material": if "SectionColor" in obj.Material: - c = tuple([float(f) for f in obj.Material['SectionColor'].strip("()").strip("[]").split(",")]) - if hasattr(obj,"SectionColor"): - if not self.isSameColor(obj.SectionColor,c): + c = tuple( + [ + float(f) + for f in obj.Material["SectionColor"].strip("()").strip("[]").split(",") + ] + ) + if hasattr(obj, "SectionColor"): + if not self.isSameColor(obj.SectionColor, c): obj.SectionColor = c if "DiffuseColor" in obj.Material: - c = tuple([float(f) for f in obj.Material['DiffuseColor'].strip("()").strip("[]").split(",")]) - if hasattr(obj,"Color"): - if not self.isSameColor(obj.Color,c): + c = tuple( + [ + float(f) + for f in obj.Material["DiffuseColor"].strip("()").strip("[]").split(",") + ] + ) + if hasattr(obj, "Color"): + if not self.isSameColor(obj.Color, c): obj.Color = c if "Transparency" in obj.Material: - t = int(obj.Material['Transparency']) - if hasattr(obj,"Transparency"): + t = int(obj.Material["Transparency"]) + if hasattr(obj, "Transparency"): if obj.Transparency != t: obj.Transparency = t if "ProductURL" in obj.Material: - if hasattr(obj,"ProductURL"): + if hasattr(obj, "ProductURL"): if obj.ProductURL != obj.Material["ProductURL"]: obj.ProductURL = obj.Material["ProductURL"] if "StandardCode" in obj.Material: - if hasattr(obj,"StandardCode"): + if hasattr(obj, "StandardCode"): if obj.StandardCode != obj.Material["StandardCode"]: obj.StandardCode = obj.Material["StandardCode"] if "Description" in obj.Material: - if hasattr(obj,"Description"): + if hasattr(obj, "Description"): if obj.Description != obj.Material["Description"]: obj.Description = obj.Material["Description"] if "Name" in obj.Material: - if hasattr(obj,"Label"): + if hasattr(obj, "Label"): if obj.Label != obj.Material["Name"]: obj.Label = obj.Material["Name"] elif prop == "Label": @@ -230,40 +293,50 @@ class _ArchMaterial: return d["Name"] = obj.Label elif prop == "SectionColor": - if hasattr(obj,"SectionColor"): + if hasattr(obj, "SectionColor"): if "SectionColor" in d: - if self.isSameColor(tuple([float(f) for f in d['SectionColor'].strip("()").strip("[]").split(",")]),obj.SectionColor[:3]): + if self.isSameColor( + tuple( + [float(f) for f in d["SectionColor"].strip("()").strip("[]").split(",")] + ), + obj.SectionColor[:3], + ): return d["SectionColor"] = str(obj.SectionColor[:3]) elif prop == "Color": - if hasattr(obj,"Color"): + if hasattr(obj, "Color"): if "DiffuseColor" in d: - if self.isSameColor(tuple([float(f) for f in d['DiffuseColor'].strip("()").strip("[]").split(",")]),obj.Color[:3]): + if self.isSameColor( + tuple( + [float(f) for f in d["DiffuseColor"].strip("()").strip("[]").split(",")] + ), + obj.Color[:3], + ): return d["DiffuseColor"] = str(obj.Color[:3]) elif prop == "Transparency": - if hasattr(obj,"Transparency"): + if hasattr(obj, "Transparency"): val = str(obj.Transparency) if "Transparency" in d: if d["Transparency"] == val: return d["Transparency"] = val elif prop == "ProductURL": - if hasattr(obj,"ProductURL"): + if hasattr(obj, "ProductURL"): val = obj.ProductURL if "ProductURL" in d: if d["ProductURL"] == val: return obj.Material["ProductURL"] = val elif prop == "StandardCode": - if hasattr(obj,"StandardCode"): + if hasattr(obj, "StandardCode"): val = obj.StandardCode if "StandardCode" in d: if d["StandardCode"] == val: return d["StandardCode"] = val elif prop == "Description": - if hasattr(obj,"Description"): + if hasattr(obj, "Description"): val = obj.Description if "Description" in d: if d["Description"] == val: @@ -271,46 +344,54 @@ class _ArchMaterial: d["Description"] = val if d and (d != obj.Material): obj.Material = d - #if FreeCAD.GuiUp: - #import FreeCADGui - # not sure why this is needed, but it is... - #FreeCADGui.ActiveDocument.resetEdit() + # if FreeCAD.GuiUp: + # import FreeCADGui + # not sure why this is needed, but it is... + # FreeCADGui.ActiveDocument.resetEdit() - def execute(self,obj): + def execute(self, obj): if obj.Material: if FreeCAD.GuiUp: c = None t = None if "DiffuseColor" in obj.Material: - c = tuple([float(f) for f in obj.Material["DiffuseColor"].strip("()").strip("[]").split(",")]) + c = tuple( + [ + float(f) + for f in obj.Material["DiffuseColor"].strip("()").strip("[]").split(",") + ] + ) if "Transparency" in obj.Material: t = int(obj.Material["Transparency"]) for p in obj.InList: - if hasattr(p,"Material") \ - and p.Material.Name == obj.Name \ - and getattr(obj.ViewObject,"UseMaterialColor",True): - if c: p.ViewObject.ShapeColor = c - if t: p.ViewObject.Transparency = t + if ( + hasattr(p, "Material") + and p.Material.Name == obj.Name + and getattr(obj.ViewObject, "UseMaterialColor", True) + ): + if c: + p.ViewObject.ShapeColor = c + if t: + p.ViewObject.Transparency = t return def dumps(self): - if hasattr(self,"Type"): + if hasattr(self, "Type"): return self.Type - def loads(self,state): + def loads(self, state): if state: self.Type = state class _ViewProviderArchMaterial: - "A View Provider for the Material object" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): - if hasattr(self,"icondata"): + if hasattr(self, "icondata"): return self.icondata return ":/icons/Arch_Material.svg" @@ -319,32 +400,32 @@ class _ViewProviderArchMaterial: def updateData(self, obj, prop): if prop == "Color": - from PySide import QtCore,QtGui + from PySide import QtCore, QtGui # custom icon - if hasattr(obj,"Color"): + if hasattr(obj, "Color"): c = obj.Color - matcolor = QtGui.QColor(int(c[0]*255),int(c[1]*255),int(c[2]*255)) - darkcolor = QtGui.QColor(int(c[0]*125),int(c[1]*125),int(c[2]*125)) - im = QtGui.QImage(48,48,QtGui.QImage.Format_ARGB32) + matcolor = QtGui.QColor(int(c[0] * 255), int(c[1] * 255), int(c[2] * 255)) + darkcolor = QtGui.QColor(int(c[0] * 125), int(c[1] * 125), int(c[2] * 125)) + im = QtGui.QImage(48, 48, QtGui.QImage.Format_ARGB32) im.fill(QtCore.Qt.transparent) pt = QtGui.QPainter(im) pt.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) - #pt.setBrush(QtGui.QBrush(matcolor, QtCore.Qt.SolidPattern)) - gradient = QtGui.QLinearGradient(0,0,48,48) - gradient.setColorAt(0,matcolor) - gradient.setColorAt(1,darkcolor) + # pt.setBrush(QtGui.QBrush(matcolor, QtCore.Qt.SolidPattern)) + gradient = QtGui.QLinearGradient(0, 0, 48, 48) + gradient.setColorAt(0, matcolor) + gradient.setColorAt(1, darkcolor) pt.setBrush(QtGui.QBrush(gradient)) - pt.drawEllipse(6,6,36,36) + pt.drawEllipse(6, 6, 36, 36) pt.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) pt.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern)) - pt.drawEllipse(12,12,12,12) + pt.drawEllipse(12, 12, 12, 12) pt.end() ba = QtCore.QByteArray() b = QtCore.QBuffer(ba) b.open(QtCore.QIODevice.WriteOnly) - im.save(b,"XPM") + im.save(b, "XPM") self.icondata = ba.data().decode("latin1") def onChanged(self, vobj, prop): @@ -373,35 +454,34 @@ class _ViewProviderArchMaterial: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) actionEdit.triggered.connect(self.edit) menu.addAction(actionEdit) def edit(self): FreeCADGui.ActiveDocument.setEdit(self.Object, 0) - def setTaskValue(self,widgetname,value): - if hasattr(self,"taskd"): - if hasattr(self.taskd,"form"): - if hasattr(self.taskd.form,widgetname): - widget = getattr(self.taskd.form,widgetname) - if hasattr(widget,"setText"): + def setTaskValue(self, widgetname, value): + if hasattr(self, "taskd"): + if hasattr(self.taskd, "form"): + if hasattr(self.taskd.form, widgetname): + widget = getattr(self.taskd.form, widgetname) + if hasattr(widget, "setText"): widget.setText(value) - elif hasattr(widget,"setValue"): + elif hasattr(widget, "setValue"): widget.setText(value) def dumps(self): return None - def loads(self,state): + def loads(self, state): return None def claimChildren(self): ch = [] - if hasattr(self,"Object"): + if hasattr(self, "Object"): for o in self.Object.Document.Objects: if o.isDerivedFrom("App::MaterialObject"): if o.Material: @@ -412,16 +492,15 @@ class _ViewProviderArchMaterial: class _ArchMaterialTaskPanel: + """The editmode TaskPanel for Arch Material objects""" - '''The editmode TaskPanel for Arch Material objects''' - - def __init__(self,obj=None): + def __init__(self, obj=None): self.cards = None self.existingmaterials = [] self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchMaterial.ui") - colorPix = QtGui.QPixmap(16,16) - colorPix.fill(QtGui.QColor(204,204,204)) + colorPix = QtGui.QPixmap(16, 16) + colorPix.fill(QtGui.QColor(204, 204, 204)) self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix)) self.form.ButtonSectionColor.setIcon(QtGui.QIcon(colorPix)) self.form.ButtonUrl.setIcon(QtGui.QIcon(":/icons/internet-web-browser.svg")) @@ -442,31 +521,31 @@ class _ArchMaterialTaskPanel: else: self.form.ButtonCode.setIcon(QtGui.QIcon(":/icons/BIM_Classification.svg")) if self.obj: - if hasattr(self.obj,"Material"): + if hasattr(self.obj, "Material"): self.material = self.obj.Material self.setFields() def setFields(self): "sets the task box contents from self.material" - if 'Name' in self.material: - self.form.FieldName.setText(self.material['Name']) + if "Name" in self.material: + self.form.FieldName.setText(self.material["Name"]) elif self.obj: self.form.FieldName.setText(self.obj.Label) - if 'Description' in self.material: - self.form.FieldDescription.setText(self.material['Description']) - if 'DiffuseColor' in self.material: + if "Description" in self.material: + self.form.FieldDescription.setText(self.material["Description"]) + if "DiffuseColor" in self.material: self.form.ButtonColor.setIcon(self.getColorIcon(self.material["DiffuseColor"])) - elif 'ViewColor' in self.material: + elif "ViewColor" in self.material: self.form.ButtonColor.setIcon(self.getColorIcon(self.material["ViewColor"])) - elif 'Color' in self.material: + elif "Color" in self.material: self.form.ButtonColor.setIcon(self.getColorIcon(self.material["Color"])) - if 'SectionColor' in self.material: + if "SectionColor" in self.material: self.form.ButtonSectionColor.setIcon(self.getColorIcon(self.material["SectionColor"])) - if 'StandardCode' in self.material: - self.form.FieldCode.setText(self.material['StandardCode']) - if 'ProductURL' in self.material: - self.form.FieldUrl.setText(self.material['ProductURL']) - if 'Transparency' in self.material: + if "StandardCode" in self.material: + self.form.FieldCode.setText(self.material["StandardCode"]) + if "ProductURL" in self.material: + self.form.FieldUrl.setText(self.material["ProductURL"]) + if "Transparency" in self.material: self.form.SpinBox_Transparency.setValue(int(self.material["Transparency"])) if "Father" in self.material: father = self.material["Father"] @@ -479,19 +558,19 @@ class _ArchMaterialTaskPanel: if o != self.obj: self.form.comboFather.addItem(o.Label) if o.Label == father: - self.form.comboFather.setCurrentIndex(self.form.comboFather.count()-1) + self.form.comboFather.setCurrentIndex(self.form.comboFather.count() - 1) found = True if father and not found: self.form.comboFather.addItem(father) - self.form.comboFather.setCurrentIndex(self.form.comboFather.count()-1) + self.form.comboFather.setCurrentIndex(self.form.comboFather.count() - 1) - def getColorIcon(self,color): + def getColorIcon(self, color): if color: if "(" in color: c = tuple([float(f) for f in color.strip("()").split(",")]) qcolor = QtGui.QColor() - qcolor.setRgbF(c[0],c[1],c[2]) - colorPix = QtGui.QPixmap(16,16) + qcolor.setRgbF(c[0], c[1], c[2]) + colorPix = QtGui.QPixmap(16, 16) colorPix.fill(qcolor) icon = QtGui.QIcon(colorPix) return icon @@ -499,27 +578,27 @@ class _ArchMaterialTaskPanel: def getFields(self): "sets self.material from the contents of the task box" - self.material['Name'] = self.form.FieldName.text() - self.material['Description'] = self.form.FieldDescription.text() - self.material['DiffuseColor'] = self.getColorFromIcon(self.form.ButtonColor.icon()) - self.material['ViewColor'] = self.material['DiffuseColor'] - self.material['Color'] = self.material['DiffuseColor'] - self.material['SectionColor'] = self.getColorFromIcon(self.form.ButtonSectionColor.icon()) - self.material['StandardCode'] = self.form.FieldCode.text() - self.material['ProductURL'] = self.form.FieldUrl.text() - self.material['Transparency'] = str(self.form.SpinBox_Transparency.value()) + self.material["Name"] = self.form.FieldName.text() + self.material["Description"] = self.form.FieldDescription.text() + self.material["DiffuseColor"] = self.getColorFromIcon(self.form.ButtonColor.icon()) + self.material["ViewColor"] = self.material["DiffuseColor"] + self.material["Color"] = self.material["DiffuseColor"] + self.material["SectionColor"] = self.getColorFromIcon(self.form.ButtonSectionColor.icon()) + self.material["StandardCode"] = self.form.FieldCode.text() + self.material["ProductURL"] = self.form.FieldUrl.text() + self.material["Transparency"] = str(self.form.SpinBox_Transparency.value()) - def getColorFromIcon(self,icon): + def getColorFromIcon(self, icon): "gets pixel color from the given icon" - pixel = icon.pixmap(16,16).toImage().pixel(0,0) + pixel = icon.pixmap(16, 16).toImage().pixel(0, 0) return str(QtGui.QColor(pixel).getRgbF()) def accept(self): self.getFields() if self.obj: - if hasattr(self.obj,"Material"): + if hasattr(self.obj, "Material"): self.obj.Material = self.material - self.obj.Label = self.material['Name'] + self.obj.Label = self.material["Name"] FreeCAD.ActiveDocument.recompute() FreeCADGui.ActiveDocument.resetEdit() return True @@ -533,14 +612,15 @@ class _ArchMaterialTaskPanel: card = self.form.comboBox_MaterialsInDir.currentText() if card in self.cards: import importFCMat + self.material = importFCMat.read(self.cards[card]) self.setFields() - def fromExisting(self,index): + def fromExisting(self, index): "sets the contents from an existing material" if index > 0: if index <= len(self.existingmaterials): - m = self.existingmaterials[index-1] + m = self.existingmaterials[index - 1] if m.Material: self.material = m.Material self.setFields() @@ -561,13 +641,13 @@ class _ArchMaterialTaskPanel: def getSectionColor(self): self.getColorForButton(self.form.ButtonSectionColor) - def getColorForButton(self,button): + def getColorForButton(self, button): "opens a color picker dialog" icon = button.icon() - pixel = icon.pixmap(16,16).toImage().pixel(0,0) + pixel = icon.pixmap(16, 16).toImage().pixel(0, 0) color = QtGui.QColorDialog.getColor(QtGui.QColor(pixel)) if color.isValid(): - colorPix = QtGui.QPixmap(16,16) + colorPix = QtGui.QPixmap(16, 16) colorPix.fill(color) button.setIcon(QtGui.QIcon(colorPix)) @@ -575,7 +655,9 @@ class _ArchMaterialTaskPanel: "fills the combo with the existing FCMat cards" # look for cards in both resources dir and a Materials sub-folder in the user folder. # User cards with same name will override system cards - resources_mat_path = os.path.join(FreeCAD.getResourceDir(), "Mod", "Material", "Resources", "Materials") + resources_mat_path = os.path.join( + FreeCAD.getResourceDir(), "Mod", "Material", "Resources", "Materials" + ) resources_mat_path_std = os.path.join(resources_mat_path, "Standard") user_mat_path = os.path.join(FreeCAD.ConfigGet("UserAppData"), "Material") @@ -586,7 +668,7 @@ class _ArchMaterialTaskPanel: for p in paths: for root, _, f_names in os.walk(p): for f in f_names: - b,e = os.path.splitext(f) + b, e = os.path.splitext(f) if e.upper() == ".FCMAT": self.cards[b] = os.path.join(root, f) if self.cards: @@ -603,20 +685,20 @@ class _ArchMaterialTaskPanel: for m in self.existingmaterials: self.form.comboBox_FromExisting.addItem(m.Label) - def openEditor(self): "opens the full material editor from the material module" self.getFields() if self.material: import MaterialEditor + self.material = MaterialEditor.editMaterial(self.material) self.setFields() def openUrl(self): self.getFields() if self.material: - if 'ProductURL' in self.material: - QtGui.QDesktopServices.openUrl(self.material['ProductURL']) + if "ProductURL" in self.material: + QtGui.QDesktopServices.openUrl(self.material["ProductURL"]) def getCode(self): FreeCADGui.Selection.addSelection(self.obj) @@ -624,30 +706,53 @@ class _ArchMaterialTaskPanel: class _ArchMultiMaterial: - "The MultiMaterial object" - def __init__(self,obj): + def __init__(self, obj): self.Type = "MultiMaterial" obj.Proxy = self - obj.addProperty("App::PropertyString","Description","Arch",QT_TRANSLATE_NOOP("App::Property","A description for this material"), locked=True) - obj.addProperty("App::PropertyStringList","Names","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer names"), locked=True) - obj.addProperty("App::PropertyLinkList","Materials","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer materials"), locked=True) - obj.addProperty("App::PropertyFloatList","Thicknesses","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer thicknesses"), locked=True) + obj.addProperty( + "App::PropertyString", + "Description", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "A description for this material"), + locked=True, + ) + obj.addProperty( + "App::PropertyStringList", + "Names", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "The list of layer names"), + locked=True, + ) + obj.addProperty( + "App::PropertyLinkList", + "Materials", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "The list of layer materials"), + locked=True, + ) + obj.addProperty( + "App::PropertyFloatList", + "Thicknesses", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "The list of layer thicknesses"), + locked=True, + ) def dumps(self): - if hasattr(self,"Type"): + if hasattr(self, "Type"): return self.Type - def loads(self,state): + def loads(self, state): if state: self.Type = state -class _ViewProviderArchMultiMaterial: +class _ViewProviderArchMultiMaterial: "A View Provider for the MultiMaterial object" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): @@ -671,14 +776,13 @@ class _ViewProviderArchMultiMaterial: FreeCADGui.Control.closeDialog() return True - def doubleClicked(self,vobj): + def doubleClicked(self, vobj): self.edit() def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) actionEdit.triggered.connect(self.edit) menu.addAction(actionEdit) @@ -688,12 +792,13 @@ class _ViewProviderArchMultiMaterial: def dumps(self): return None - def loads(self,state): + def loads(self, state): return None def isShow(self): return True + if FreeCAD.GuiUp: class MultiMaterialDelegate(QtGui.QStyledItemDelegate): @@ -705,7 +810,7 @@ if FreeCAD.GuiUp: self.mats.append(obj) QtGui.QStyledItemDelegate.__init__(self, parent, *args) - def createEditor(self,parent,option,index): + def createEditor(self, parent, option, index): if index.column() == 0: editor = QtGui.QComboBox(parent) editor.setEditable(True) @@ -714,7 +819,7 @@ if FreeCAD.GuiUp: elif index.column() == 2: ui = FreeCADGui.UiLoader() editor = ui.createWidget("Gui::InputField") - editor.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Minimum) + editor.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum) editor.setParent(parent) else: editor = QtGui.QLineEdit(parent) @@ -723,10 +828,11 @@ if FreeCAD.GuiUp: def setEditorData(self, editor, index): if index.column() == 0: import ArchWindow - editor.addItems([index.data()]+ArchWindow.WindowPartTypes) + + editor.addItems([index.data()] + ArchWindow.WindowPartTypes) elif index.column() == 1: idx = -1 - for i,m in enumerate(self.mats): + for i, m in enumerate(self.mats): editor.addItem(m.Label) if m.Label == index.data(): idx = i @@ -750,14 +856,19 @@ if FreeCAD.GuiUp: class _ArchMultiMaterialTaskPanel: + """The editmode TaskPanel for MultiMaterial objects""" - '''The editmode TaskPanel for MultiMaterial objects''' - - def __init__(self,obj=None): + def __init__(self, obj=None): self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchMultiMaterial.ui") self.model = QtGui.QStandardItemModel() - self.model.setHorizontalHeaderLabels([translate("Arch","Name"),translate("Arch","Material"),translate("Arch","Thickness")]) + self.model.setHorizontalHeaderLabels( + [ + translate("Arch", "Name"), + translate("Arch", "Material"), + translate("Arch", "Thickness"), + ] + ) self.form.tree.setModel(self.model) self.form.tree.setUniformRowHeights(True) self.form.tree.setItemDelegate(MultiMaterialDelegate()) @@ -771,25 +882,36 @@ class _ArchMultiMaterialTaskPanel: self.fillExistingCombo() self.fillData() - def fillData(self,obj=None): + def fillData(self, obj=None): if not obj: obj = self.obj if obj: self.model.clear() - self.model.setHorizontalHeaderLabels([translate("Arch","Name"),translate("Arch","Material"),translate("Arch","Thickness")]) + self.model.setHorizontalHeaderLabels( + [ + translate("Arch", "Name"), + translate("Arch", "Material"), + translate("Arch", "Thickness"), + ] + ) # restore widths - self.form.tree.setColumnWidth(0,params.get_param_arch("MultiMaterialColumnWidth0")) - self.form.tree.setColumnWidth(1,params.get_param_arch("MultiMaterialColumnWidth1")) + self.form.tree.setColumnWidth(0, params.get_param_arch("MultiMaterialColumnWidth0")) + self.form.tree.setColumnWidth(1, params.get_param_arch("MultiMaterialColumnWidth1")) for i in range(len(obj.Names)): item1 = QtGui.QStandardItem(obj.Names[i]) item2 = QtGui.QStandardItem(obj.Materials[i].Label) - item3 = QtGui.QStandardItem(FreeCAD.Units.Quantity(obj.Thicknesses[i],FreeCAD.Units.Length).getUserPreferred()[0]) - self.model.appendRow([item1,item2,item3]) + item3 = QtGui.QStandardItem( + FreeCAD.Units.Quantity( + obj.Thicknesses[i], FreeCAD.Units.Length + ).getUserPreferred()[0] + ) + self.model.appendRow([item1, item2, item3]) self.form.nameField.setText(obj.Label) def fillExistingCombo(self): "fills the existing multimaterials combo" import Draft + self.existingmaterials = [] for obj in FreeCAD.ActiveDocument.Objects: if Draft.getType(obj) == "MultiMaterial": @@ -798,19 +920,19 @@ class _ArchMultiMaterialTaskPanel: for m in self.existingmaterials: self.form.chooseCombo.addItem(m.Label) - def fromExisting(self,index): + def fromExisting(self, index): "sets the contents from an existing material" if index > 0: if index <= len(self.existingmaterials): - m = self.existingmaterials[index-1] + m = self.existingmaterials[index - 1] if m: self.fillData(m) def addLayer(self): - item1 = QtGui.QStandardItem(translate("Arch","New layer")) + item1 = QtGui.QStandardItem(translate("Arch", "New layer")) item2 = QtGui.QStandardItem() item3 = QtGui.QStandardItem() - self.model.appendRow([item1,item2,item3]) + self.model.appendRow([item1, item2, item3]) def delLayer(self): sel = self.form.tree.selectedIndexes() @@ -820,15 +942,15 @@ class _ArchMultiMaterialTaskPanel: self.model.takeRow(row) self.recalcThickness() - def moveLayer(self,mvt=0): + def moveLayer(self, mvt=0): sel = self.form.tree.selectedIndexes() if sel and mvt: row = sel[0].row() if row >= 0: - if row+mvt >= 0: + if row + mvt >= 0: data = self.model.takeRow(row) - self.model.insertRow(row+mvt,data) - ind = self.model.index(row+mvt,0) + self.model.insertRow(row + mvt, data) + ind = self.model.index(row + mvt, 0) self.form.tree.setCurrentIndex(ind) def upLayer(self): @@ -838,34 +960,34 @@ class _ArchMultiMaterialTaskPanel: self.moveLayer(mvt=1) def invertLayer(self): - items = [self.model.takeRow(row) for row in range(self.model.rowCount()-1,-1,-1)] + items = [self.model.takeRow(row) for row in range(self.model.rowCount() - 1, -1, -1)] items.reverse() for item in items: - self.model.insertRow(0,item) + self.model.insertRow(0, item) - def recalcThickness(self,item=None): - prefix = translate("Arch","Total thickness")+": " + def recalcThickness(self, item=None): + prefix = translate("Arch", "Total thickness") + ": " th = 0 suffix = "" for row in range(self.model.rowCount()): thick = 0 - d = self.model.item(row,2).text() + d = self.model.item(row, 2).text() try: d = float(d) except Exception: thick = FreeCAD.Units.Quantity(d).Value else: - thick = FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).Value + thick = FreeCAD.Units.Quantity(d, FreeCAD.Units.Length).Value th += abs(thick) if not thick: - suffix = " ("+translate("Arch","depends on the object")+")" - val = FreeCAD.Units.Quantity(th,FreeCAD.Units.Length).UserString + suffix = " (" + translate("Arch", "depends on the object") + ")" + val = FreeCAD.Units.Quantity(th, FreeCAD.Units.Length).UserString self.form.labelTotalThickness.setText(prefix + val + suffix) def accept(self): # store widths - params.set_param_arch("MultiMaterialColumnWidth0",self.form.tree.columnWidth(0)) - params.set_param_arch("MultiMaterialColumnWidth1",self.form.tree.columnWidth(1)) + params.set_param_arch("MultiMaterialColumnWidth0", self.form.tree.columnWidth(0)) + params.set_param_arch("MultiMaterialColumnWidth1", self.form.tree.columnWidth(1)) if self.obj: mats = [] for m in FreeCAD.ActiveDocument.Objects: @@ -875,20 +997,20 @@ class _ArchMultiMaterialTaskPanel: materials = [] thicknesses = [] for row in range(self.model.rowCount()): - name = self.model.item(row,0).text() + name = self.model.item(row, 0).text() mat = None - ml = self.model.item(row,1).text() + ml = self.model.item(row, 1).text() for m in mats: if m.Label == ml: mat = m - d = self.model.item(row,2).text() + d = self.model.item(row, 2).text() try: d = float(d) except Exception: thick = FreeCAD.Units.Quantity(d).Value else: - thick = FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).Value - if round(thick,32) == 0: + thick = FreeCAD.Units.Quantity(d, FreeCAD.Units.Length).Value + if round(thick, 32) == 0: thick = 0.0 if name and mat: names.append(name) diff --git a/src/Mod/BIM/ArchNesting.py b/src/Mod/BIM/ArchNesting.py index 9ffa8acc98..45549a2c16 100644 --- a/src/Mod/BIM/ArchNesting.py +++ b/src/Mod/BIM/ArchNesting.py @@ -35,83 +35,77 @@ import WorkingPlane # SvgNest: https://github.com/Jack000/SVGnest # Wikihouse plugin: https://github.com/tav/wikihouse-plugin/blob/master/wikihouse.rb -TOLERANCE = 0.0001 # smaller than this, two points are considered equal -DISCRETIZE = 4 # the number of segments in which arcs must be subdivided -ROTATIONS = [0,90,180,270] # the possible rotations to try +TOLERANCE = 0.0001 # smaller than this, two points are considered equal +DISCRETIZE = 4 # the number of segments in which arcs must be subdivided +ROTATIONS = [0, 90, 180, 270] # the possible rotations to try + class Nester: - - def __init__(self,container=None,shapes=None): - + def __init__(self, container=None, shapes=None): """Nester([container,shapes]): Creates a nester object with a container - shape and a list of other shapes to nest into it. Container and - shapes must be Part.Faces. + shape and a list of other shapes to nest into it. Container and + shapes must be Part.Faces. - Typical workflow: + Typical workflow: - n = Nester() # creates the nester - n.addContainer(object) # adds a doc object as the container - n.addObjects(objects) # adds a list of doc objects as shapes - n.run() # runs the nesting - n.show() # creates a preview (compound) of the results - n.apply() # applies transformations to the original objects + n = Nester() # creates the nester + n.addContainer(object) # adds a doc object as the container + n.addObjects(objects) # adds a list of doc objects as shapes + n.run() # runs the nesting + n.show() # creates a preview (compound) of the results + n.apply() # applies transformations to the original objects - Defaults (can be changed): + Defaults (can be changed): - Nester.TOLERANCE = 0.0001 - Nester.DISCRETIZE = 4 - Nester.ROTATIONS = [0,90,180,270] - """ + Nester.TOLERANCE = 0.0001 + Nester.DISCRETIZE = 4 + Nester.ROTATIONS = [0,90,180,270] + """ self.objects = None self.container = container self.shapes = shapes - self.results = [] # storage for the different results + self.results = [] # storage for the different results self.indexedFaces = None self.running = True self.progress = 0 - self.setCounter = None # optionally define a setCounter(value) function where value is a % - - def addObjects(self,objects): + self.setCounter = None # optionally define a setCounter(value) function where value is a % + def addObjects(self, objects): """addObjects(objects): adds FreeCAD DocumentObjects to the nester""" - if not isinstance(objects,list): + if not isinstance(objects, list): objects = [objects] if not self.objects: self.objects = {} if not self.shapes: self.shapes = [] for obj in objects: - if hasattr(obj,'Shape'): + if hasattr(obj, "Shape"): h = obj.Shape.hashCode() if not h in self.objects: self.objects[h] = obj self.shapes.append(obj.Shape) - def addContainer(self,container): - + def addContainer(self, container): """addContainer(object): adds a FreeCAD DocumentObject as the container""" - if hasattr(container,'Shape'): + if hasattr(container, "Shape"): self.container = container.Shape def clear(self): - """clear(): Removes all objects and shape from the nester""" self.objects = None self.shapes = None def stop(self): - """stop((): stops the computation""" self.running = False def update(self): - """update(): internal function to verify if computation can go on""" @@ -119,16 +113,16 @@ class Nester: self.setCounter(self.progress) if FreeCAD.GuiUp: from PySide import QtGui + QtGui.QApplication.processEvents() if not self.running: return False return True def run(self): - """run(): Runs a nesting operation. Returns a list of lists of - shapes, each primary list being one filled container, or None - if the operation failed.""" + shapes, each primary list being one filled container, or None + if the operation failed.""" # reset abort mechanism and variables @@ -138,17 +132,17 @@ class Nester: # general conformity tests - print("Executing conformity tests… ",end="") + print("Executing conformity tests… ", end="") if not self.container: print("Empty container. Aborting.") return if not self.shapes: print("Empty shapes. Aborting.") return - if not isinstance(self.container,Part.Face): + if not isinstance(self.container, Part.Face): print("Container is not a face. Aborting.") return - normal = self.container.normalAt(0,0) + normal = self.container.normalAt(0, 0) for s in self.shapes: if not self.update(): return @@ -156,10 +150,12 @@ class Nester: print("One of the shapes does not contain exactly one face. Aborting.") return # check if all faces correctly oriented (same normal) - if s.Faces[0].normalAt(0,0).getAngle(normal) > TOLERANCE: + if s.Faces[0].normalAt(0, 0).getAngle(normal) > TOLERANCE: # let pass faces with inverted normal - if s.Faces[0].normalAt(0,0).getAngle(normal)-math.pi > TOLERANCE: - print("One of the face does not have the same orientation as the container. Aborting.") + if s.Faces[0].normalAt(0, 0).getAngle(normal) - math.pi > TOLERANCE: + print( + "One of the face does not have the same orientation as the container. Aborting." + ) return # TODO @@ -171,20 +167,20 @@ class Nester: # add genetic algo to swap pieces, and check if the result is better # track progresses - step = 100.0/(len(self.shapes)*len(ROTATIONS)) + step = 100.0 / (len(self.shapes) * len(ROTATIONS)) # store hashCode together with the face so we can change the order # and still identify the original face, so we can calculate a transform afterwards - self.indexedfaces = [[shape.hashCode(),shape] for shape in self.shapes] + self.indexedfaces = [[shape.hashCode(), shape] for shape in self.shapes] # build a clean copy so we don't touch the original faces = list(self.indexedfaces) # replace shapes by their face - faces = [[f[0],f[1].Faces[0]] for f in faces] + faces = [[f[0], f[1].Faces[0]] for f in faces] # order by area - faces = sorted(faces,key=lambda face: face[1].Area) + faces = sorted(faces, key=lambda face: face[1].Area) # discretize non-linear edges and remove holes nfaces = [] @@ -194,16 +190,16 @@ class Nester: nedges = [] allLines = True for edge in face[1].OuterWire.OrderedEdges: - if isinstance(edge.Curve,(Part.LineSegment,Part.Line)): + if isinstance(edge.Curve, (Part.LineSegment, Part.Line)): nedges.append(edge) else: allLines = False last = edge.Vertexes[0].Point for i in range(DISCRETIZE): - s = float(i+1)/DISCRETIZE - par = edge.FirstParameter + (edge.LastParameter-edge.FirstParameter)*s + s = float(i + 1) / DISCRETIZE + par = edge.FirstParameter + (edge.LastParameter - edge.FirstParameter) * s new = edge.valueAt(par) - nedges.append(Part.LineSegment(last,new).toShape()) + nedges.append(Part.LineSegment(last, new).toShape()) last = new f = Part.Face(Part.Wire(nedges)) if not f.isValid(): @@ -212,24 +208,33 @@ class Nester: else: print("Face distretizing failed. Aborting.") return - nfaces.append([face[0],f]) + nfaces.append([face[0], f]) faces = nfaces # container for sheets with a first, empty sheet sheets = [[]] - print("Everything OK (",datetime.now()-starttime,")") + print("Everything OK (", datetime.now() - starttime, ")") # main loop facenumber = 1 facesnumber = len(faces) - #print("Vertices per face:",[len(face[1].Vertexes) for face in faces]) + # print("Vertices per face:",[len(face[1].Vertexes) for face in faces]) while faces: - print("Placing piece",facenumber,"/",facesnumber,"Area:",FreeCAD.Units.Quantity(faces[-1][1].Area,FreeCAD.Units.Area).getUserPreferred()[0],": ",end="") + print( + "Placing piece", + facenumber, + "/", + facesnumber, + "Area:", + FreeCAD.Units.Quantity(faces[-1][1].Area, FreeCAD.Units.Area).getUserPreferred()[0], + ": ", + end="", + ) face = faces.pop() boc = self.container.BoundBox @@ -254,17 +259,17 @@ class Nester: self.progress += step - print(rotation,", ",end="") + print(rotation, ", ", end="") hashcode = face[0] rotface = face[1].copy() if rotation: - rotface.rotate(rotface.CenterOfMass,normal,rotation) + rotface.rotate(rotface.CenterOfMass, normal, rotation) bof = rotface.BoundBox rotverts = self.order(rotface) - #for i,v in enumerate(rotverts): + # for i,v in enumerate(rotverts): # Draft.makeText([str(i)],point=v) - basepoint = rotverts[0] # leftmost point of the rotated face - basecorner = boc.getPoint(0) # lower left corner of the container + basepoint = rotverts[0] # leftmost point of the rotated face + basecorner = boc.getPoint(0) # lower left corner of the container # See if the piece fits in the container dimensions if (bof.XLength < boc.XLength) and (bof.YLength < boc.YLength): @@ -275,15 +280,15 @@ class Nester: # circulate, and the face still be fully inside the container v1 = basecorner.add(basepoint.sub(bof.getPoint(0))) - v2 = v1.add(FreeCAD.Vector(0,boc.YLength-bof.YLength,0)) - v3 = v2.add(FreeCAD.Vector(boc.XLength-bof.XLength,0,0)) - v4 = v3.add(FreeCAD.Vector(0,-(boc.YLength-bof.YLength),0)) - binpol = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) - initials.append([binpol,[hashcode,rotface],basepoint]) + v2 = v1.add(FreeCAD.Vector(0, boc.YLength - bof.YLength, 0)) + v3 = v2.add(FreeCAD.Vector(boc.XLength - bof.XLength, 0, 0)) + v4 = v3.add(FreeCAD.Vector(0, -(boc.YLength - bof.YLength), 0)) + binpol = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) + initials.append([binpol, [hashcode, rotface], basepoint]) # check for available space on each existing sheet - for sheetnumber,sheet in enumerate(sheets): + for sheetnumber, sheet in enumerate(sheets): # Get the no-fit polygon for each already placed face in # current sheet. That is, a polygon in which basepoint # cannot be, if we want our face to not overlap with the @@ -296,9 +301,9 @@ class Nester: nofitpol = [] for placed in sheet: pts = [] - for placedvert in self.order(placed[1],right=True): + for placedvert in self.order(placed[1], right=True): fpts = [] - for i,rotvert in enumerate(rotverts): + for i, rotvert in enumerate(rotverts): if not self.update(): return @@ -312,7 +317,7 @@ class Nester: faceverts = self.order(facecopy) for vert in faceverts: if (vert.sub(placedvert)).Length > TOLERANCE: - if placed[1].isInside(vert,TOLERANCE,True): + if placed[1].isInside(vert, TOLERANCE, True): outside = False break @@ -328,15 +333,19 @@ class Nester: if True: # Draft code (SLOW) - p = DraftGeomUtils.findIntersection(e1,e2) + p = DraftGeomUtils.findIntersection(e1, e2) if p: p = p[0] p1 = e1.Vertexes[0].Point p2 = e1.Vertexes[1].Point p3 = e2.Vertexes[0].Point p4 = e2.Vertexes[1].Point - if (p.sub(p1).Length > TOLERANCE) and (p.sub(p2).Length > TOLERANCE) \ - and (p.sub(p3).Length > TOLERANCE) and (p.sub(p4).Length > TOLERANCE): + if ( + (p.sub(p1).Length > TOLERANCE) + and (p.sub(p2).Length > TOLERANCE) + and (p.sub(p3).Length > TOLERANCE) + and (p.sub(p4).Length > TOLERANCE) + ): outside = False break else: @@ -345,30 +354,31 @@ class Nester: if p: if p[0] < TOLERANCE: # allow vertex-to-vertex intersection - if (p[2][0][0] != "Vertex") or (p[2][0][3] != "Vertex"): + if (p[2][0][0] != "Vertex") or ( + p[2][0][3] != "Vertex" + ): outside = False break - if outside: - fpts.append([faceverts[0],i]) - #Draft.makeText([str(i)],point=faceverts[0]) + fpts.append([faceverts[0], i]) + # Draft.makeText([str(i)],point=faceverts[0]) # reorder available solutions around a same point if needed # ensure they are in the correct order idxs = [p[1] for p in fpts] - if (0 in idxs) and (len(faceverts)-1 in idxs): + if (0 in idxs) and (len(faceverts) - 1 in idxs): slicepoint = len(fpts) last = len(faceverts) for p in reversed(fpts): - if p[1] == last-1: + if p[1] == last - 1: slicepoint -= 1 last -= 1 else: break - fpts = fpts[slicepoint:]+fpts[:slicepoint] - #print(fpts) + fpts = fpts[slicepoint:] + fpts[:slicepoint] + # print(fpts) pts.extend(fpts) # create the polygon @@ -377,7 +387,7 @@ class Nester: print("Error calculating a no-fit polygon. Aborting.") return pts = [p[0] for p in pts] - pol = Part.Face(Part.makePolygon(pts+[pts[0]])) + pol = Part.Face(Part.makePolygon(pts + [pts[0]])) if not pol.isValid(): @@ -386,27 +396,35 @@ class Nester: overlap = True while overlap: overlap = False - for i in range(len(pol.OuterWire.Edges)-1): + for i in range(len(pol.OuterWire.Edges) - 1): if not self.update(): return v1 = DraftGeomUtils.vec(pol.OuterWire.OrderedEdges[i]) - v2 = DraftGeomUtils.vec(pol.OuterWire.OrderedEdges[i+1]) - if abs(v1.getAngle(v2)-math.pi) <= TOLERANCE: + v2 = DraftGeomUtils.vec(pol.OuterWire.OrderedEdges[i + 1]) + if abs(v1.getAngle(v2) - math.pi) <= TOLERANCE: overlap = True - ne = Part.LineSegment(pol.OuterWire.OrderedEdges[i].Vertexes[0].Point, - pol.OuterWire.OrderedEdges[i+1].Vertexes[-1].Point).toShape() - pol = Part.Face(Part.Wire(pol.OuterWire.OrderedEdges[:i]+[ne]+pol.OuterWire.OrderedEdges[i+2:])) + ne = Part.LineSegment( + pol.OuterWire.OrderedEdges[i].Vertexes[0].Point, + pol.OuterWire.OrderedEdges[i + 1].Vertexes[-1].Point, + ).toShape() + pol = Part.Face( + Part.Wire( + pol.OuterWire.OrderedEdges[:i] + + [ne] + + pol.OuterWire.OrderedEdges[i + 2 :] + ) + ) break if not pol.isValid(): # trying basic OCC fix - pol.fix(0,0,0) + pol.fix(0, 0, 0) if pol.isValid(): if pol.ShapeType == "Face": - pol = Part.Face(pol.OuterWire) # discard possible inner holes + pol = Part.Face(pol.OuterWire) # discard possible inner holes elif pol.Faces: # several faces after the fix, keep the biggest one a = 0 @@ -431,13 +449,13 @@ class Nester: for p in sheet: Part.show(p[1]) Part.show(facecopy) - #for i,p in enumerate(faceverts): + # for i,p in enumerate(faceverts): # Draft.makeText([str(i)],point=p) return if pol.isValid(): nofitpol.append(pol) - #Part.show(pol) + # Part.show(pol) # Union all the no-fit pols into one @@ -495,7 +513,7 @@ class Nester: # order the fitpol vertexes by smallest X # and try to place the piece, making sure it doesn't # intersect with already placed pieces - fitverts = sorted([v.Point for v in fitpol.Vertexes],key=lambda v: v.x) + fitverts = sorted([v.Point for v in fitpol.Vertexes], key=lambda v: v.x) for p in fitverts: if not self.update(): return @@ -506,21 +524,25 @@ class Nester: for placed in sheet: if ok: for vert in trface.Vertexes: - if placed[1].isInside(vert.Point,TOLERANCE,False): + if placed[1].isInside(vert.Point, TOLERANCE, False): ok = False break if ok: for e1 in trface.OuterWire.Edges: for e2 in placed[1].OuterWire.Edges: - p = DraftGeomUtils.findIntersection(e1,e2) + p = DraftGeomUtils.findIntersection(e1, e2) if p: p = p[0] p1 = e1.Vertexes[0].Point p2 = e1.Vertexes[1].Point p3 = e2.Vertexes[0].Point p4 = e2.Vertexes[1].Point - if (p.sub(p1).Length > TOLERANCE) and (p.sub(p2).Length > TOLERANCE) \ - and (p.sub(p3).Length > TOLERANCE) and (p.sub(p4).Length > TOLERANCE): + if ( + (p.sub(p1).Length > TOLERANCE) + and (p.sub(p2).Length > TOLERANCE) + and (p.sub(p3).Length > TOLERANCE) + and (p.sub(p4).Length > TOLERANCE) + ): ok = False break if not ok: @@ -537,7 +559,7 @@ class Nester: bb = rotface.BoundBox for placed in sheet: bb.add(placed[1].BoundBox) - available.append([sheetnumber,[hashcode,rotface],bb.XMax,fitpol]) + available.append([sheetnumber, [hashcode, rotface], bb.XMax, fitpol]) if unfit: print("One face does not fit in the container. Aborting.") @@ -546,36 +568,34 @@ class Nester: if available: # order by smallest X size and take the first one - available = sorted(available,key=lambda sol: sol[2]) - print("Adding piece to sheet",available[0][0]+1) + available = sorted(available, key=lambda sol: sol[2]) + print("Adding piece to sheet", available[0][0] + 1) sheets[available[0][0]].append(available[0][1]) - #Part.show(available[0][3]) + # Part.show(available[0][3]) else: # adding to the leftmost vertex of the binpol sheet = [] - print("Creating new sheet, adding piece to sheet",len(sheets)) + print("Creating new sheet, adding piece to sheet", len(sheets)) # order initial positions by smallest X size - initials = sorted(initials,key=lambda sol: sol[1][1].BoundBox.XLength) + initials = sorted(initials, key=lambda sol: sol[1][1].BoundBox.XLength) hashcode = initials[0][1][0] face = initials[0][1][1] # order binpol vertexes by X coord - verts = sorted([v.Point for v in initials[0][0].Vertexes],key=lambda v: v.x) + verts = sorted([v.Point for v in initials[0][0].Vertexes], key=lambda v: v.x) face.translate(verts[0].sub(initials[0][2])) - sheet.append([hashcode,face]) + sheet.append([hashcode, face]) sheets.append(sheet) facenumber += 1 - print("Run time:",datetime.now()-starttime) + print("Run time:", datetime.now() - starttime) self.results.append(sheets) return sheets - - def order(self,face,right=False): - + def order(self, face, right=False): """order(face,[right]): returns a list of vertices ordered clockwise. The first vertex will be the lefmost one, unless right is True, in which case the @@ -586,25 +606,25 @@ class Nester: # flatten the polygon on the XY plane wp = WorkingPlane.PlaneBase() - wp.align_to_point_and_axis(face.CenterOfMass,face.normalAt(0,0)) + wp.align_to_point_and_axis(face.CenterOfMass, face.normalAt(0, 0)) pverts = [] for v in verts: - vx = DraftVecUtils.project(v,wp.u) + vx = DraftVecUtils.project(v, wp.u) lx = vx.Length if vx.getAngle(wp.u) > 1: lx = -lx - vy = DraftVecUtils.project(v,wp.v) + vy = DraftVecUtils.project(v, wp.v) ly = vy.Length if vy.getAngle(wp.v) > 1: ly = -ly - pverts.append(FreeCAD.Vector(lx,ly,0)) + pverts.append(FreeCAD.Vector(lx, ly, 0)) pverts.append(pverts[0]) # https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order s = 0 - for i in range(len(pverts)-1): - s += (pverts[i+1].x-pverts[i].x)*(pverts[i+1].y+pverts[i].y) + for i in range(len(pverts) - 1): + s += (pverts[i + 1].x - pverts[i].x) * (pverts[i + 1].y + pverts[i].y) if s < 0: verts.reverse() elif s == 0: @@ -613,33 +633,30 @@ class Nester: return verts - - def show(self,result=None): - + def show(self, result=None): """show([result]): creates shapes in the document, showing - the given result (list of sheets) or the last result if - none is provided""" + the given result (list of sheets) or the last result if + none is provided""" if not result: result = [] if self.results: result = self.results[-1] - offset = FreeCAD.Vector(0,0,0) + offset = FreeCAD.Vector(0, 0, 0) feats = [] for sheet in result: shapes = [self.container.OuterWire] shapes.extend([face[1] for face in sheet]) comp = Part.makeCompound(shapes) comp.translate(offset) - o = FreeCAD.ActiveDocument.addObject("Part::Feature","Nest") + o = FreeCAD.ActiveDocument.addObject("Part::Feature", "Nest") o.Shape = comp feats.append(o) - offset = offset.add(FreeCAD.Vector(1.1*self.container.BoundBox.XLength,0,0)) + offset = offset.add(FreeCAD.Vector(1.1 * self.container.BoundBox.XLength, 0, 0)) FreeCAD.ActiveDocument.recompute() return feats - def getPlacements(self,result=None): - + def getPlacements(self, result=None): """getPlacements([result]): returns a dictionary of hashCode:Placement pairs from the given result or the last computed result if none is given. The Placement contains a translation vector and a rotation @@ -653,7 +670,7 @@ class Nester: if self.results: result = self.results[-1] d = {} - offset = FreeCAD.Vector(0,0,0) + offset = FreeCAD.Vector(0, 0, 0) for sheet in result: for face in sheet: orig = None @@ -667,14 +684,16 @@ class Nester: if offset.Length: shape.translate(offset) deltav = shape.Faces[0].CenterOfMass.sub(orig.Faces[0].CenterOfMass) - rot = FreeCAD.Rotation(orig.Vertexes[0].Point.sub(orig.Faces[0].CenterOfMass),shape.Vertexes[0].Point.sub(shape.Faces[0].CenterOfMass)) - pla = FreeCAD.Placement(deltav,rot) + rot = FreeCAD.Rotation( + orig.Vertexes[0].Point.sub(orig.Faces[0].CenterOfMass), + shape.Vertexes[0].Point.sub(shape.Faces[0].CenterOfMass), + ) + pla = FreeCAD.Placement(deltav, rot) d[face[0]] = pla - offset = offset.add(FreeCAD.Vector(1.1*self.container.BoundBox.XLength,0,0)) + offset = offset.add(FreeCAD.Vector(1.1 * self.container.BoundBox.XLength, 0, 0)) return d - def apply(self,result=None): - + def apply(self, result=None): """apply([result]): Applies the computed placements of the given result, or the last computed result if none is given, to the document objects given to the nester via addObjects() before @@ -685,26 +704,30 @@ class Nester: return p = self.getPlacements(result) if p: - for key,pla in p.items(): + for key, pla in p.items(): if key in self.objects: sh = self.objects[key].Shape.copy() sh.translate(pla.Base) - sh.rotate(sh.Faces[0].CenterOfMass,pla.Rotation.Axis,math.degrees(pla.Rotation.Angle)) + sh.rotate( + sh.Faces[0].CenterOfMass, + pla.Rotation.Axis, + math.degrees(pla.Rotation.Angle), + ) self.objects[key].Placement = sh.Placement else: print("error: hashCode mismatch with original object") def test(): - "runs a test with selected shapes, container selected last" import FreeCADGui + sel = FreeCADGui.Selection.getSelection() if sel: container = sel.pop().Shape shapes = [o.Shape for o in sel] - n = Nester(container,shapes) + n = Nester(container, shapes) result = n.run() if result: n.show() diff --git a/src/Mod/BIM/ArchPanel.py b/src/Mod/BIM/ArchPanel.py index ac673e83ff..665c79da8e 100644 --- a/src/Mod/BIM/ArchPanel.py +++ b/src/Mod/BIM/ArchPanel.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Panel" +__title__ = "FreeCAD Panel" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchPanel # \ingroup ARCH @@ -53,72 +53,175 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _Panel(ArchComponent.Component): - "The Panel object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Panel" self.setProperties(obj) obj.IfcType = "Plate" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Length" in pl: - obj.addProperty("App::PropertyLength","Length","Panel", QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Length", + "Panel", + 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","Panel", QT_TRANSLATE_NOOP("App::Property","The width of this element, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "The width of this element, if not based on a profile" + ), + locked=True, + ) if not "Thickness" in pl: - obj.addProperty("App::PropertyLength","Thickness","Panel",QT_TRANSLATE_NOOP("App::Property","The thickness or extrusion depth of this element"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Thickness", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "The thickness or extrusion depth of this element" + ), + locked=True, + ) if not "Sheets" in pl: - obj.addProperty("App::PropertyInteger","Sheets","Panel", QT_TRANSLATE_NOOP("App::Property","The number of sheets to use"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "Sheets", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The number of sheets to use"), + locked=True, + ) obj.Sheets = 1 if not "Offset" in pl: - obj.addProperty("App::PropertyDistance","Offset","Panel", QT_TRANSLATE_NOOP("App::Property","The offset between this panel and its baseline"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Offset", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "The offset between this panel and its baseline" + ), + locked=True, + ) if not "WaveLength" in pl: - obj.addProperty("App::PropertyLength","WaveLength","Panel", QT_TRANSLATE_NOOP("App::Property","The length of waves for corrugated elements"), locked=True) + obj.addProperty( + "App::PropertyLength", + "WaveLength", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The length of waves for corrugated elements"), + locked=True, + ) if not "WaveHeight" in pl: - obj.addProperty("App::PropertyLength","WaveHeight","Panel", QT_TRANSLATE_NOOP("App::Property","The height of waves for corrugated elements"), locked=True) + obj.addProperty( + "App::PropertyLength", + "WaveHeight", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The height of waves for corrugated elements"), + locked=True, + ) if not "WaveOffset" in pl: - obj.addProperty("App::PropertyDistance","WaveOffset","Panel", QT_TRANSLATE_NOOP("App::Property","The horizontal offset of waves for corrugated elements"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "WaveOffset", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "The horizontal offset of waves for corrugated elements" + ), + locked=True, + ) if not "WaveDirection" in pl: - obj.addProperty("App::PropertyAngle","WaveDirection","Panel", QT_TRANSLATE_NOOP("App::Property","The direction of waves for corrugated elements"), locked=True) + obj.addProperty( + "App::PropertyAngle", + "WaveDirection", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "The direction of waves for corrugated elements" + ), + locked=True, + ) if not "WaveType" in pl: - obj.addProperty("App::PropertyEnumeration","WaveType","Panel", QT_TRANSLATE_NOOP("App::Property","The type of waves for corrugated elements"), locked=True) - obj.WaveType = ["Curved","Trapezoidal","Spikes"] + obj.addProperty( + "App::PropertyEnumeration", + "WaveType", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The type of waves for corrugated elements"), + locked=True, + ) + obj.WaveType = ["Curved", "Trapezoidal", "Spikes"] if not "WaveBottom" in pl: - obj.addProperty("App::PropertyBool","WaveBottom","Panel", QT_TRANSLATE_NOOP("App::Property","If the wave also affects the bottom side or not"), locked=True) + obj.addProperty( + "App::PropertyBool", + "WaveBottom", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", "If the wave also affects the bottom side or not" + ), + locked=True, + ) if not "Area" in pl: - obj.addProperty("App::PropertyArea","Area","Panel", QT_TRANSLATE_NOOP("App::Property","The area of this panel"), locked=True) + obj.addProperty( + "App::PropertyArea", + "Area", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The area of this panel"), + locked=True, + ) if not "FaceMaker" in pl: - obj.addProperty("App::PropertyEnumeration","FaceMaker","Panel",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"] + obj.addProperty( + "App::PropertyEnumeration", + "FaceMaker", + "Panel", + 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 "Normal" in pl: - obj.addProperty("App::PropertyVector","Normal","Panel",QT_TRANSLATE_NOOP("App::Property","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"), locked=True) - obj.setEditorMode("VerticalArea",2) - obj.setEditorMode("HorizontalArea",2) + obj.addProperty( + "App::PropertyVector", + "Normal", + "Panel", + QT_TRANSLATE_NOOP( + "App::Property", + "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)", + ), + locked=True, + ) + obj.setEditorMode("VerticalArea", 2) + obj.setEditorMode("HorizontalArea", 2) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Panel" - def execute(self,obj): - + def execute(self, obj): "creates the panel shape" if self.clone(obj): @@ -131,7 +234,7 @@ class _Panel(ArchComponent.Component): # base tests if obj.Base: - if hasattr(obj.Base,'Shape'): + if hasattr(obj.Base, "Shape"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): @@ -151,19 +254,19 @@ class _Panel(ArchComponent.Component): else: if not obj.Base: return - elif hasattr(obj.Base,'Shape'): + elif hasattr(obj.Base, "Shape"): if not obj.Base.Shape.Solids: return - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: - if hasattr(obj.Material,"Materials"): + if hasattr(obj.Material, "Materials"): varwidth = 0 thicknesses = [t for t in obj.Material.Thicknesses if t >= 0] restwidth = thickness - sum(thicknesses) if restwidth > 0: varwidth = [t for t in thicknesses if t == 0] if varwidth: - varwidth = restwidth/len(varwidth) + varwidth = restwidth / len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) @@ -173,7 +276,7 @@ class _Panel(ArchComponent.Component): pl = obj.Placement base = None normal = None - if hasattr(obj,"Normal"): + if hasattr(obj, "Normal"): if obj.Normal.Length > 0: normal = Vector(obj.Normal) normal.normalize() @@ -182,11 +285,11 @@ class _Panel(ArchComponent.Component): if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: - # p = FreeCAD.Placement(obj.Base.Placement) + # p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base if not normal: - normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness) + normal = baseprofile.Faces[0].normalAt(0, 0).multiply(thickness) if layers: layeroffset = 0 shps = [] @@ -204,13 +307,17 @@ class _Panel(ArchComponent.Component): base = base.extrude(normal) elif base.Wires: fm = False - if hasattr(obj,"FaceMaker"): + if hasattr(obj, "FaceMaker"): if obj.FaceMaker != "None": try: - baseprofile = Part.makeFace(base.Wires,"Part::FaceMaker"+str(obj.FaceMaker)) + baseprofile = Part.makeFace( + base.Wires, "Part::FaceMaker" + str(obj.FaceMaker) + ) fm = True except Exception: - FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Facemaker returned an error") + "\n" + ) return if not fm: closed = True @@ -220,7 +327,7 @@ class _Panel(ArchComponent.Component): if closed: baseprofile = ArchCommands.makeFace(base.Wires) if not normal: - normal = baseprofile.normalAt(0,0).multiply(thickness) + normal = baseprofile.normalAt(0, 0).multiply(thickness) if layers: layeroffset = 0 shps = [] @@ -251,14 +358,14 @@ class _Panel(ArchComponent.Component): if normal: n = Vector(normal).normalize().multiply(l) else: - n = Vector(0,0,1).multiply(abs(l)) - l2 = length/2 or 0.5 - w2 = width/2 or 0.5 - v1 = Vector(-l2,-w2,layeroffset) - v2 = Vector(l2,-w2,layeroffset) - v3 = Vector(l2,w2,layeroffset) - v4 = Vector(-l2,w2,layeroffset) - base = Part.makePolygon([v1,v2,v3,v4,v1]) + n = Vector(0, 0, 1).multiply(abs(l)) + l2 = length / 2 or 0.5 + w2 = width / 2 or 0.5 + v1 = Vector(-l2, -w2, layeroffset) + v2 = Vector(l2, -w2, layeroffset) + v3 = Vector(l2, w2, layeroffset) + v4 = Vector(-l2, w2, layeroffset) + base = Part.makePolygon([v1, v2, v3, v4, v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(n) shps.append(base) @@ -266,28 +373,28 @@ class _Panel(ArchComponent.Component): base = Part.makeCompound(shps) else: if not normal: - normal = Vector(0,0,1).multiply(thickness) - 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) - base = Part.makePolygon([v1,v2,v3,v4,v1]) + normal = Vector(0, 0, 1).multiply(thickness) + 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) + base = Part.makePolygon([v1, v2, v3, v4, v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) - if hasattr(obj,"Area"): + if hasattr(obj, "Area"): if baseprofile: obj.Area = baseprofile.Area - if hasattr(obj,"WaveLength"): + if hasattr(obj, "WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) downsegment = None - if hasattr(obj,"WaveBottom"): + if hasattr(obj, "WaveBottom"): if not obj.WaveBottom: if obj.WaveType == "Curved": if obj.Thickness.Value > obj.WaveHeight.Value: @@ -296,105 +403,109 @@ class _Panel(ArchComponent.Component): downsegment = obj.WaveHeight.Value + obj.Thickness.Value else: downsegment = obj.Thickness.Value - p1 = Vector(0,0,0) - p5 = Vector(obj.WaveLength.Value*2,0,0) + p1 = Vector(0, 0, 0) + p5 = Vector(obj.WaveLength.Value * 2, 0, 0) if obj.WaveType == "Curved": - p2 = Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value) - p3 = Vector(obj.WaveLength.Value,0,0) - e1 = Part.Arc(p1,p2,p3).toShape() - p4 = Vector(obj.WaveLength.Value*1.5,0,-obj.WaveHeight.Value) - e2 = Part.Arc(p3,p4,p5).toShape() - upsegment = Part.Wire([e1,e2]) + p2 = Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value) + p3 = Vector(obj.WaveLength.Value, 0, 0) + e1 = Part.Arc(p1, p2, p3).toShape() + p4 = Vector(obj.WaveLength.Value * 1.5, 0, -obj.WaveHeight.Value) + e2 = Part.Arc(p3, p4, p5).toShape() + upsegment = Part.Wire([e1, e2]) if not downsegment: if obj.Thickness.Value < e1.Curve.Radius: c3 = e1.Curve.copy() - c3.Radius = e1.Curve.Radius-obj.Thickness.Value - e3 = Part.Arc(c3,e1.FirstParameter,e1.LastParameter).toShape() + c3.Radius = e1.Curve.Radius - obj.Thickness.Value + e3 = Part.Arc(c3, e1.FirstParameter, e1.LastParameter).toShape() c4 = e2.Curve.copy() - c4.Radius = e2.Curve.Radius+obj.Thickness.Value - e4 = Part.Arc(c4,e2.FirstParameter,e2.LastParameter).toShape() - downsegment = Part.Wire([e3,e4]) + c4.Radius = e2.Curve.Radius + obj.Thickness.Value + e4 = Part.Arc(c4, e2.FirstParameter, e2.LastParameter).toShape() + downsegment = Part.Wire([e3, e4]) else: - r = e2.Curve.Radius+obj.Thickness.Value + r = e2.Curve.Radius + obj.Thickness.Value z = math.sqrt(r**2 - obj.WaveLength.Value**2) - p6 = e2.Curve.Center.add(Vector(-obj.WaveLength,0,-z)) - p7 = e2.Curve.Center.add(Vector(0,0,-r)) - p8 = e2.Curve.Center.add(Vector(obj.WaveLength,0,-z)) - downsegment = Part.Arc(p6,p7,p8).toShape() + p6 = e2.Curve.Center.add(Vector(-obj.WaveLength, 0, -z)) + p7 = e2.Curve.Center.add(Vector(0, 0, -r)) + p8 = e2.Curve.Center.add(Vector(obj.WaveLength, 0, -z)) + downsegment = Part.Arc(p6, p7, p8).toShape() elif obj.WaveType == "Trapezoidal": - p2 = Vector(obj.WaveLength.Value/4,0,obj.WaveHeight.Value) - p3 = Vector(obj.WaveLength.Value,0,obj.WaveHeight.Value) - p4 = Vector(obj.WaveLength.Value*1.25,0,0) - upsegment = Part.makePolygon([p1,p2,p3,p4,p5]) + p2 = Vector(obj.WaveLength.Value / 4, 0, obj.WaveHeight.Value) + p3 = Vector(obj.WaveLength.Value, 0, obj.WaveHeight.Value) + p4 = Vector(obj.WaveLength.Value * 1.25, 0, 0) + upsegment = Part.makePolygon([p1, p2, p3, p4, p5]) if not downsegment: - a = ((p1.sub(p2)).getAngle(p3.sub(p2)))/2 - tx = obj.Thickness.Value/math.tan(a) - d1 = Vector(tx,0,-obj.Thickness.Value) - d2 = Vector(-tx,0,-obj.Thickness.Value) + a = ((p1.sub(p2)).getAngle(p3.sub(p2))) / 2 + tx = obj.Thickness.Value / math.tan(a) + d1 = Vector(tx, 0, -obj.Thickness.Value) + d2 = Vector(-tx, 0, -obj.Thickness.Value) p6 = p1.add(d1) - if tx >= p3.sub(p2).Length/2: + if tx >= p3.sub(p2).Length / 2: d3 = p2.sub(p1) d3.normalize() - d3.multiply((0.625*obj.WaveLength.Value)/d3.x) - d4 = Vector(d3.x,0,-d3.z) + d3.multiply((0.625 * obj.WaveLength.Value) / d3.x) + d4 = Vector(d3.x, 0, -d3.z) p7 = p6.add(d3) p8 = p7.add(d4) p9 = p5.add(d1) - downsegment = Part.makePolygon([p6,p7,p8,p9]) - elif tx <= 0.625*obj.WaveLength.Value: + downsegment = Part.makePolygon([p6, p7, p8, p9]) + elif tx <= 0.625 * obj.WaveLength.Value: p7 = p2.add(d1) p8 = p3.add(d2) p9 = p4.add(d2) p10 = p5.add(d1) - downsegment = Part.makePolygon([p6,p7,p8,p9,p10]) + downsegment = Part.makePolygon([p6, p7, p8, p9, p10]) else: downsegment = obj.Thickness.Value - else: # spike - p2 = Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value) - p3 = Vector(obj.WaveHeight.Value*2,0,0) - upsegment = Part.makePolygon([p1,p2,p3,p5]) + else: # spike + p2 = Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value) + p3 = Vector(obj.WaveHeight.Value * 2, 0, 0) + upsegment = Part.makePolygon([p1, p2, p3, p5]) if not downsegment: downsegment = obj.Thickness.Value - upsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z)) - if isinstance(downsegment,Part.Shape): - downsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z)) - if hasattr(obj,"WaveOffset"): + upsegment.translate(Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z)) + if isinstance(downsegment, Part.Shape): + downsegment.translate(Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z)) + if hasattr(obj, "WaveOffset"): if obj.WaveOffset.Value: - upsegment.translate(Vector(obj.WaveOffset.Value,0,0)) - if isinstance(downsegment,Part.Shape): - downsegment.translate(Vector(obj.WaveOffset.Value,0,0)) + upsegment.translate(Vector(obj.WaveOffset.Value, 0, 0)) + if isinstance(downsegment, Part.Shape): + downsegment.translate(Vector(obj.WaveOffset.Value, 0, 0)) upedges = [] downedges = [] - for i in range(int(bb.XLength/(obj.WaveLength.Value*2))): + for i in range(int(bb.XLength / (obj.WaveLength.Value * 2))): w1 = upsegment.copy() - w1.translate(Vector(obj.WaveLength.Value*2*i,0,0)) + w1.translate(Vector(obj.WaveLength.Value * 2 * i, 0, 0)) upedges.extend(w1.Edges) - if isinstance(downsegment,Part.Shape): + if isinstance(downsegment, Part.Shape): w2 = downsegment.copy() - w2.translate(Vector(obj.WaveLength.Value*2*i,0,0)) + w2.translate(Vector(obj.WaveLength.Value * 2 * i, 0, 0)) downedges.extend(w2.Edges) upwire = Part.Wire(upedges) - FreeCAD.upwire = upwire # REMOVE - if isinstance(downsegment,Part.Shape): + FreeCAD.upwire = upwire # REMOVE + if isinstance(downsegment, Part.Shape): downwire = Part.Wire(downedges) - FreeCAD.downwire = downwire # REMOVE - e1 = Part.LineSegment(upwire.Vertexes[0].Point,downwire.Vertexes[0].Point).toShape() - e2 = Part.LineSegment(upwire.Vertexes[-1].Point,downwire.Vertexes[-1].Point).toShape() - basewire = Part.Wire(upwire.Edges+[e1,e2]+downwire.Edges) + FreeCAD.downwire = downwire # REMOVE + e1 = Part.LineSegment( + upwire.Vertexes[0].Point, downwire.Vertexes[0].Point + ).toShape() + e2 = Part.LineSegment( + upwire.Vertexes[-1].Point, downwire.Vertexes[-1].Point + ).toShape() + basewire = Part.Wire(upwire.Edges + [e1, e2] + downwire.Edges) else: z = obj.Thickness.Value if obj.WaveType == "Curved": z += obj.WaveHeight.Value p1 = upwire.Vertexes[0].Point - p2 = p1.add(Vector(0,0,-z)) - p3 = Vector(upwire.Vertexes[-1].Point.x,upwire.Vertexes[-1].Point.y,p2.z) + p2 = p1.add(Vector(0, 0, -z)) + p3 = Vector(upwire.Vertexes[-1].Point.x, upwire.Vertexes[-1].Point.y, p2.z) p4 = upwire.Vertexes[-1].Point - w = Part.makePolygon([p1,p2,p3,p4]) - basewire = Part.Wire(upwire.Edges+w.Edges) + w = Part.makePolygon([p1, p2, p3, p4]) + basewire = Part.Wire(upwire.Edges + w.Edges) FreeCAD.basewire = basewire if not basewire.isClosed(): @@ -402,40 +513,42 @@ class _Panel(ArchComponent.Component): return baseface = Part.Face(basewire) - base = baseface.extrude(Vector(0,bb.YLength,0)) - if normal.cross(FreeCAD.Vector(0,0,1)).Length > 1e-6: - rot = FreeCAD.Rotation(FreeCAD.Vector(0,-1,0),FreeCAD.Vector(*normal[:2],0)) - base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle)) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal) - base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle)) + base = baseface.extrude(Vector(0, bb.YLength, 0)) + if normal.cross(FreeCAD.Vector(0, 0, 1)).Length > 1e-6: + rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), FreeCAD.Vector(*normal[:2], 0)) + base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), normal) + base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) if obj.WaveDirection.Value: - base.rotate(bb.Center,normal,obj.WaveDirection.Value) - n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2) + base.rotate(bb.Center, normal, obj.WaveDirection.Value) + n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value * 2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: - FreeCAD.Console.PrintError(translate("Arch","Error computing shape of")+" "+obj.Label+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Error computing shape of") + " " + obj.Label + "\n" + ) return False if base and (obj.Sheets > 1) and normal and thickness: bases = [base] - for i in range(1,obj.Sheets): - n = FreeCAD.Vector(normal).normalize().multiply(i*thickness) + for i in range(1, obj.Sheets): + n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) - if base and normal and hasattr(obj,"Offset"): + if base and normal and hasattr(obj, "Offset"): if obj.Offset.Value: - v = DraftVecUtils.scaleTo(normal,obj.Offset.Value) + v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) base.translate(v) # process subshapes - base = self.processSubShapes(obj,base,pl) + base = self.processSubShapes(obj, base, pl) # applying if base: @@ -445,7 +558,9 @@ class _Panel(ArchComponent.Component): if base.Volume < 0: base.reverse() if base.Volume < 0: - FreeCAD.Console.PrintError(translate("Arch","Could not compute a shape")) + FreeCAD.Console.PrintError( + translate("Arch", "Could not compute a shape") + ) return base = base.removeSplitter() obj.Shape = base @@ -454,52 +569,66 @@ class _Panel(ArchComponent.Component): class _ViewProviderPanel(ArchComponent.ViewProviderComponent): - "A View Provider for the Panel object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) vobj.ShapeColor = ArchCommands.getDefaultColor("Panel") def getIcon(self): - #import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + # import Arch_rc + if hasattr(self, "Object"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Panel_Clone.svg" return ":/icons/Arch_Panel_Tree.svg" - def updateData(self,obj,prop): + def updateData(self, obj, prop): - if prop in ["Placement","Shape","Material"]: - if hasattr(obj,"Material"): + if prop in ["Placement", "Shape", "Material"]: + if hasattr(obj, "Material"): if obj.Material: - if hasattr(obj.Material,"Materials"): - activematerials = [obj.Material.Materials[i] for i in range(len(obj.Material.Materials)) if obj.Material.Thicknesses[i] >= 0] + if hasattr(obj.Material, "Materials"): + activematerials = [ + obj.Material.Materials[i] + for i in range(len(obj.Material.Materials)) + if obj.Material.Thicknesses[i] >= 0 + ] if len(activematerials) == len(obj.Shape.Solids): cols = [] - for i,mat in enumerate(activematerials): + for i, mat in enumerate(activematerials): c = obj.ViewObject.ShapeColor - c = (c[0],c[1],c[2],1.0-obj.ViewObject.Transparency/100.0) - if 'DiffuseColor' in mat.Material: - if "(" in mat.Material['DiffuseColor']: - c = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) - if 'Transparency' in mat.Material: - c = (c[0],c[1],c[2],1.0-float(mat.Material['Transparency'])) + c = (c[0], c[1], c[2], 1.0 - obj.ViewObject.Transparency / 100.0) + if "DiffuseColor" in mat.Material: + if "(" in mat.Material["DiffuseColor"]: + c = tuple( + [ + float(f) + for f in mat.Material["DiffuseColor"] + .strip("()") + .split(",") + ] + ) + if "Transparency" in mat.Material: + c = ( + c[0], + c[1], + c[2], + 1.0 - float(mat.Material["Transparency"]), + ) cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))]) if obj.ViewObject.DiffuseColor != cols: obj.ViewObject.DiffuseColor = cols - ArchComponent.ViewProviderComponent.updateData(self,obj,prop) + ArchComponent.ViewProviderComponent.updateData(self, obj, prop) class PanelCut(Draft.DraftObject): - "A flat, 2D view of an Arch Panel" def __init__(self, obj): - Draft.DraftObject.__init__(self,obj) + Draft.DraftObject.__init__(self, obj) obj.Proxy = self # setProperties of ArchComponent will be overwritten @@ -508,33 +637,100 @@ class PanelCut(Draft.DraftObject): self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Source" in pl: - obj.addProperty("App::PropertyLink","Source","PanelCut",QT_TRANSLATE_NOOP("App::Property","The linked object"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Source", + "PanelCut", + QT_TRANSLATE_NOOP("App::Property", "The linked object"), + locked=True, + ) if not "TagText" in pl: - obj.addProperty("App::PropertyString","TagText","PanelCut",QT_TRANSLATE_NOOP("App::Property","The text to display. Can be %tag%, %label% or %description% to display the panel tag or label"), locked=True) + obj.addProperty( + "App::PropertyString", + "TagText", + "PanelCut", + QT_TRANSLATE_NOOP( + "App::Property", + "The text to display. Can be %tag%, %label% or %description% to display the panel tag or label", + ), + locked=True, + ) obj.TagText = "%tag%" if not "TagSize" in pl: - obj.addProperty("App::PropertyLength","TagSize","PanelCut",QT_TRANSLATE_NOOP("App::Property","The size of the tag text"), locked=True) + obj.addProperty( + "App::PropertyLength", + "TagSize", + "PanelCut", + QT_TRANSLATE_NOOP("App::Property", "The size of the tag text"), + locked=True, + ) obj.TagSize = 10 if not "TagPosition" in pl: - obj.addProperty("App::PropertyVector","TagPosition","PanelCut",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for center position"), locked=True) + obj.addProperty( + "App::PropertyVector", + "TagPosition", + "PanelCut", + QT_TRANSLATE_NOOP( + "App::Property", + "The position of the tag text. Keep (0,0,0) for center position", + ), + locked=True, + ) if not "TagRotation" in pl: - obj.addProperty("App::PropertyAngle","TagRotation","PanelCut",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text"), locked=True) + obj.addProperty( + "App::PropertyAngle", + "TagRotation", + "PanelCut", + QT_TRANSLATE_NOOP("App::Property", "The rotation of the tag text"), + locked=True, + ) if not "FontFile" in pl: - obj.addProperty("App::PropertyFile","FontFile","PanelCut",QT_TRANSLATE_NOOP("App::Property","The font of the tag text"), locked=True) + obj.addProperty( + "App::PropertyFile", + "FontFile", + "PanelCut", + QT_TRANSLATE_NOOP("App::Property", "The font of the tag text"), + locked=True, + ) obj.FontFile = params.get_param("ShapeStringFontFile") if not "MakeFace" in pl: - obj.addProperty("App::PropertyBool","MakeFace","PanelCut",QT_TRANSLATE_NOOP("App::Property","If True, the object is rendered as a face, if possible."), locked=True) + obj.addProperty( + "App::PropertyBool", + "MakeFace", + "PanelCut", + QT_TRANSLATE_NOOP( + "App::Property", "If True, the object is rendered as a face, if possible." + ), + locked=True, + ) if not "AllowedAngles" in pl: - obj.addProperty("App::PropertyFloatList","AllowedAngles","PanelCut",QT_TRANSLATE_NOOP("App::Property","The allowed angles this object can be rotated to when placed on sheets"), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "AllowedAngles", + "PanelCut", + QT_TRANSLATE_NOOP( + "App::Property", + "The allowed angles this object can be rotated to when placed on sheets", + ), + locked=True, + ) self.Type = "PanelCut" if not "CutOffset" in pl: - obj.addProperty("App::PropertyDistance","CutOffset","PanelCut",QT_TRANSLATE_NOOP("App::Property","An offset value to move the cut plane from the center point"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "CutOffset", + "PanelCut", + QT_TRANSLATE_NOOP( + "App::Property", "An offset value to move the cut plane from the center point" + ), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) @@ -547,13 +743,14 @@ class PanelCut(Draft.DraftObject): if Draft.getType(obj.Source) == "Panel": import DraftGeomUtils import Part + baseobj = None if obj.Source.CloneOf: baseobj = obj.Source.CloneOf.Base if obj.Source.Base: baseobj = obj.Source.Base if baseobj: - if hasattr(baseobj,'Shape'): + if hasattr(baseobj, "Shape"): if baseobj.Shape.Solids: center = baseobj.Shape.BoundBox.Center diag = baseobj.Shape.BoundBox.DiagonalLength @@ -562,42 +759,42 @@ class PanelCut(Draft.DraftObject): elif baseobj.isDerivedFrom("Part::Extrusion"): n = baseobj.Dir if not n: - n = Vector(0,0,1) - if hasattr(obj,"CutOffset") and obj.CutOffset.Value: + n = Vector(0, 0, 1) + if hasattr(obj, "CutOffset") and obj.CutOffset.Value: l = obj.CutOffset.Value d = Vector(n) d.multiply(l) center = center.add(d) - plane = Part.makePlane(diag,diag,center,n) + plane = Part.makePlane(diag, diag, center, n) plane.translate(center.sub(plane.BoundBox.Center)) wires = [] for sol in baseobj.Shape.Solids: s = sol.section(plane) wires.extend(DraftGeomUtils.findWires(s.Edges)) if wires: - base = self.buildCut(obj,wires) + base = self.buildCut(obj, wires) else: - base = self.buildCut(obj,baseobj.Shape.Wires) + base = self.buildCut(obj, baseobj.Shape.Wires) for w in base.Wires: n = DraftGeomUtils.getNormal(w) if n: break if not n: - n = Vector(0,0,1) + n = Vector(0, 0, 1) if base and n: base.translate(base.BoundBox.Center.negative()) - r = FreeCAD.Rotation(n,Vector(0,0,1)) - base.rotate(Vector(0,0,0),r.Axis,math.degrees(r.Angle)) + r = FreeCAD.Rotation(n, Vector(0, 0, 1)) + base.rotate(Vector(0, 0, 0), r.Axis, math.degrees(r.Angle)) elif baseobj.isDerivedFrom("Mesh::Feature"): return else: - l2 = obj.Source.Length/2 - w2 = obj.Source.Width/2 - v1 = Vector(-l2,-w2,0) - v2 = Vector(l2,-w2,0) - v3 = Vector(l2,w2,0) - v4 = Vector(-l2,w2,0) - base = Part.makePolygon([v1,v2,v3,v4,v1]) + l2 = obj.Source.Length / 2 + w2 = obj.Source.Width / 2 + v1 = Vector(-l2, -w2, 0) + v2 = Vector(l2, -w2, 0) + v3 = Vector(l2, w2, 0) + v4 = Vector(-l2, w2, 0) + base = Part.makePolygon([v1, v2, v3, v4, v1]) if base: self.outline = base if obj.FontFile and obj.TagText and obj.TagSize.Value: @@ -614,24 +811,26 @@ class PanelCut(Draft.DraftObject): else: string = obj.TagText chars = [] - for char in Part.makeWireString(string,obj.FontFile,obj.TagSize.Value,0): + for char in Part.makeWireString(string, obj.FontFile, obj.TagSize.Value, 0): chars.extend(char) textshape = Part.Compound(chars) textshape.translate(pos.sub(textshape.BoundBox.Center)) - textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value) + textshape.rotate( + textshape.BoundBox.Center, Vector(0, 0, 1), obj.TagRotation.Value + ) self.tag = textshape - base = Part.Compound([base,textshape]) + base = Part.Compound([base, textshape]) else: base = Part.Compound([base]) obj.Shape = base obj.Placement = pl - def buildCut(self,obj,wires): - + def buildCut(self, obj, wires): """buildCut(obj,wires): builds the object shape""" import Part - if hasattr(obj,"MakeFace"): + + if hasattr(obj, "MakeFace"): if obj.MakeFace: face = None if len(wires) > 1: @@ -654,7 +853,6 @@ class PanelCut(Draft.DraftObject): return Part.makeCompound(wires) def getWires(self, obj): - """getWires(obj): returns a tuple containing 3 shapes that define the panel outline, the panel holes, and tags (engravings): (outline,holes,tags). Any of these can @@ -663,12 +861,12 @@ class PanelCut(Draft.DraftObject): tag = None outl = None inl = None - if not hasattr(self,"outline"): + if not hasattr(self, "outline"): self.execute(obj) - if not hasattr(self,"outline"): + if not hasattr(self, "outline"): return None outl = self.outline.copy() - if hasattr(self,"tag"): + if hasattr(self, "tag"): tag = self.tag.copy() if tag: tag.Placement = obj.Placement.multiply(tag.Placement) @@ -693,35 +891,47 @@ class PanelCut(Draft.DraftObject): class ViewProviderPanelCut(Draft.ViewProviderDraft): - "a view provider for the panel cut object" - def __init__(self,vobj): + def __init__(self, vobj): - Draft.ViewProviderDraft.__init__(self,vobj) + Draft.ViewProviderDraft.__init__(self, vobj) self.setProperties(vobj) - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList if not "Margin" in pl: - vobj.addProperty("App::PropertyLength","Margin","Arch",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "Margin", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "A margin inside the boundary"), + locked=True, + ) if not "ShowMargin" in pl: - vobj.addProperty("App::PropertyBool","ShowMargin","Arch",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowMargin", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "Turns the display of the margin on/off"), + locked=True, + ) - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) - def attach(self,vobj): + def attach(self, vobj): - Draft.ViewProviderDraft.attach(self,vobj) + Draft.ViewProviderDraft.attach(self, vobj) from pivy import coin + self.coords = coin.SoCoordinate3() self.lineset = coin.SoLineSet() self.lineset.numVertices.setValue(-1) lineStyle = coin.SoDrawStyle() - lineStyle.linePattern = 0x0f0f + lineStyle.linePattern = 0x0F0F self.color = coin.SoBaseColor() self.switch = coin.SoSwitch() sep = coin.SoSeparator() @@ -732,13 +942,13 @@ class ViewProviderPanelCut(Draft.ViewProviderDraft): sep.addChild(self.lineset) self.switch.addChild(sep) vobj.Annotation.addChild(self.switch) - self.onChanged(vobj,"ShowMargin") - self.onChanged(vobj,"LineColor") + self.onChanged(vobj, "ShowMargin") + self.onChanged(vobj, "LineColor") - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - if prop in ["Margin","ShowMargin"]: - if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"): + if prop in ["Margin", "ShowMargin"]: + if hasattr(vobj, "Margin") and hasattr(vobj, "ShowMargin"): if (vobj.Margin.Value > 0) and vobj.Object.Shape and vobj.ShowMargin: self.lineset.numVertices.setValue(-1) if vobj.Object.Shape.Wires: @@ -753,7 +963,7 @@ class ViewProviderPanelCut(Draft.ViewProviderDraft): verts = [] for v in ow.OrderedVertexes: v = vobj.Object.Placement.inverse().multVec(v.Point) - verts.append((v.x,v.y,v.z)) + verts.append((v.x, v.y, v.z)) if dw.isClosed(): verts.append(verts[0]) self.coords.point.setValues(verts) @@ -762,68 +972,155 @@ class ViewProviderPanelCut(Draft.ViewProviderDraft): else: self.switch.whichChild = -1 elif prop == "LineColor": - if hasattr(vobj,"LineColor"): + if hasattr(vobj, "LineColor"): c = vobj.LineColor - self.color.rgb.setValue(c[0],c[1],c[2]) - Draft.ViewProviderDraft.onChanged(self,vobj,prop) + self.color.rgb.setValue(c[0], c[1], c[2]) + Draft.ViewProviderDraft.onChanged(self, vobj, prop) - def updateData(self,obj,prop): + def updateData(self, obj, prop): if prop in ["Shape"]: - self.onChanged(obj.ViewObject,"Margin") - Draft.ViewProviderDraft.updateData(self,obj,prop) + self.onChanged(obj.ViewObject, "Margin") + Draft.ViewProviderDraft.updateData(self, obj, prop) - def doubleClicked(self,vobj): + def doubleClicked(self, vobj): # See setEdit in ViewProviderDraft. FreeCADGui.runCommand("Std_TransformManip") return True -class PanelSheet(Draft.DraftObject): +class PanelSheet(Draft.DraftObject): "A collection of Panel cuts under a sheet" def __init__(self, obj): - Draft.DraftObject.__init__(self,obj) + Draft.DraftObject.__init__(self, obj) obj.Proxy = self self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Group" in pl: - obj.addProperty("App::PropertyLinkList","Group","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The linked Panel cuts"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Group", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The linked Panel cuts"), + locked=True, + ) if not "TagText" in pl: - obj.addProperty("App::PropertyString","TagText","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The tag text to display"), locked=True) + obj.addProperty( + "App::PropertyString", + "TagText", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The tag text to display"), + locked=True, + ) if not "TagSize" in pl: - obj.addProperty("App::PropertyLength","TagSize","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The size of the tag text"), locked=True) + obj.addProperty( + "App::PropertyLength", + "TagSize", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The size of the tag text"), + locked=True, + ) obj.TagSize = 10 if not "TagPosition" in pl: - obj.addProperty("App::PropertyVector","TagPosition","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for center position"), locked=True) + obj.addProperty( + "App::PropertyVector", + "TagPosition", + "PanelSheet", + QT_TRANSLATE_NOOP( + "App::Property", + "The position of the tag text. Keep (0,0,0) for center position", + ), + locked=True, + ) if not "TagRotation" in pl: - obj.addProperty("App::PropertyAngle","TagRotation","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text"), locked=True) + obj.addProperty( + "App::PropertyAngle", + "TagRotation", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The rotation of the tag text"), + locked=True, + ) if not "FontFile" in pl: - obj.addProperty("App::PropertyFile","FontFile","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The font of the tag text"), locked=True) + obj.addProperty( + "App::PropertyFile", + "FontFile", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The font of the tag text"), + locked=True, + ) obj.FontFile = params.get_param("ShapeStringFontFile") if not "Width" in pl: - obj.addProperty("App::PropertyLength","Width","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The width of the sheet"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The width of the sheet"), + locked=True, + ) obj.Width = params.get_param_arch("PanelLength") if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The height of the sheet"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The height of the sheet"), + locked=True, + ) obj.Height = params.get_param_arch("PanelWidth") if not "FillRatio" in pl: - obj.addProperty("App::PropertyPercent","FillRatio","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The fill ratio of this sheet"), locked=True) - obj.setEditorMode("FillRatio",2) + obj.addProperty( + "App::PropertyPercent", + "FillRatio", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "The fill ratio of this sheet"), + locked=True, + ) + obj.setEditorMode("FillRatio", 2) if not "MakeFace" in pl: - obj.addProperty("App::PropertyBool","MakeFace","PanelSheet",QT_TRANSLATE_NOOP("App::Property","If True, the object is rendered as a face, if possible."), locked=True) + obj.addProperty( + "App::PropertyBool", + "MakeFace", + "PanelSheet", + QT_TRANSLATE_NOOP( + "App::Property", "If True, the object is rendered as a face, if possible." + ), + locked=True, + ) if not "GrainDirection" in pl: - obj.addProperty("App::PropertyAngle","GrainDirection","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Specifies an angle for the wood grain (Clockwise, 0 is North)"), locked=True) + obj.addProperty( + "App::PropertyAngle", + "GrainDirection", + "PanelSheet", + QT_TRANSLATE_NOOP( + "App::Property", "Specifies an angle for the wood grain (Clockwise, 0 is North)" + ), + locked=True, + ) if not "Scale" in pl: - obj.addProperty("App::PropertyFloat","Scale","PanelSheet", QT_TRANSLATE_NOOP("App::Property","Specifies the scale applied to each panel view."), locked=True) + obj.addProperty( + "App::PropertyFloat", + "Scale", + "PanelSheet", + QT_TRANSLATE_NOOP( + "App::Property", "Specifies the scale applied to each panel view." + ), + locked=True, + ) obj.Scale = 1.0 if not "Rotations" in pl: - obj.addProperty("App::PropertyFloatList","Rotations","PanelSheet", QT_TRANSLATE_NOOP("App::Property","A list of possible rotations for the nester"), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Rotations", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "A list of possible rotations for the nester"), + locked=True, + ) self.Type = "PanelSheet" def onDocumentRestored(self, obj): @@ -833,18 +1130,19 @@ class PanelSheet(Draft.DraftObject): def execute(self, obj): import Part + self.sheettag = None self.sheetborder = None pl = obj.Placement if obj.Width.Value and obj.Height.Value: - l2 = obj.Width.Value/2 - w2 = obj.Height.Value/2 - v1 = Vector(-l2,-w2,0) - v2 = Vector(l2,-w2,0) - v3 = Vector(l2,w2,0) - v4 = Vector(-l2,w2,0) - base = Part.makePolygon([v1,v2,v3,v4,v1]) - if hasattr(obj,"MakeFace"): + l2 = obj.Width.Value / 2 + w2 = obj.Height.Value / 2 + v1 = Vector(-l2, -w2, 0) + v2 = Vector(l2, -w2, 0) + v3 = Vector(l2, w2, 0) + v4 = Vector(-l2, w2, 0) + base = Part.makePolygon([v1, v2, v3, v4, v1]) + if hasattr(obj, "MakeFace"): if obj.MakeFace: base = Part.Face(base) self.sheetborder = base @@ -852,7 +1150,7 @@ class PanelSheet(Draft.DraftObject): area = obj.Width.Value * obj.Height.Value subarea = 0 for v in obj.Group: - if hasattr(v,'Shape'): + if hasattr(v, "Shape"): wires.extend(v.Shape.Wires) if Draft.getType(v) == "PanelCut": if v.Source: @@ -863,23 +1161,22 @@ class PanelSheet(Draft.DraftObject): f = Part.Face(w) subarea += f.Area if wires: - base = Part.Compound([base]+wires) + base = Part.Compound([base] + wires) if obj.FontFile and obj.TagText and obj.TagSize.Value: chars = [] - for char in Part.makeWireString(obj.TagText,obj.FontFile,obj.TagSize.Value,0): + for char in Part.makeWireString(obj.TagText, obj.FontFile, obj.TagSize.Value, 0): chars.extend(char) textshape = Part.Compound(chars) textshape.translate(obj.TagPosition) - textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value) + textshape.rotate(textshape.BoundBox.Center, Vector(0, 0, 1), obj.TagRotation.Value) self.sheettag = textshape - base = Part.Compound([base,textshape]) + base = Part.Compound([base, textshape]) base.scale(obj.Scale, FreeCAD.Vector()) obj.Shape = base obj.Placement = pl - obj.FillRatio = int((subarea/area)*100) - - def getOutlines(self,obj,transform=False): + obj.FillRatio = int((subarea / area) * 100) + def getOutlines(self, obj, transform=False): """getOutlines(obj,transform=False): returns a list of compounds whose wires define the outlines of the panels in this sheet. If transform is True, the placement of the sheet will be added to each wire""" @@ -887,8 +1184,8 @@ class PanelSheet(Draft.DraftObject): outp = [] for p in obj.Group: ispanel = False - if hasattr(p,"Proxy"): - if hasattr(p.Proxy,"getWires"): + if hasattr(p, "Proxy"): + if hasattr(p.Proxy, "getWires"): ispanel = True w = p.Proxy.getWires(p) if w[0]: @@ -898,7 +1195,7 @@ class PanelSheet(Draft.DraftObject): w.Placement = obj.Placement.multiply(w.Placement) outp.append(w) if not ispanel: - if hasattr(p,'Shape'): + if hasattr(p, "Shape"): for w in p.Shape.Wires: w.scale(obj.Scale, FreeCAD.Vector()) if transform: @@ -906,16 +1203,15 @@ class PanelSheet(Draft.DraftObject): outp.append(w) return outp - def getHoles(self,obj,transform=False): - + def getHoles(self, obj, transform=False): """getHoles(obj,transform=False): returns a list of compound whose wires define the holes contained in the panels in this sheet. If transform is True, the placement of the sheet will be added to each wire""" outp = [] for p in obj.Group: - if hasattr(p,"Proxy"): - if hasattr(p.Proxy,"getWires"): + if hasattr(p, "Proxy"): + if hasattr(p.Proxy, "getWires"): w = p.Proxy.getWires(p) if w[1]: w = w[1] @@ -925,8 +1221,7 @@ class PanelSheet(Draft.DraftObject): outp.append(w) return outp - def getTags(self,obj,transform=False): - + def getTags(self, obj, transform=False): """getTags(obj,transform=False): returns a list of compounds whose wires define the tags (engravings) contained in the panels in this sheet and the sheet intself. If transform is True, the placement of the sheet will be added to each wire. @@ -935,8 +1230,8 @@ class PanelSheet(Draft.DraftObject): outp = [] for p in obj.Group: - if hasattr(p,"Proxy"): - if hasattr(p.Proxy,"getWires"): + if hasattr(p, "Proxy"): + if hasattr(p.Proxy, "getWires"): w = p.Proxy.getWires(p) if w[2]: w = w[2] @@ -955,26 +1250,45 @@ class PanelSheet(Draft.DraftObject): class ViewProviderPanelSheet(Draft.ViewProviderDraft): - "a view provider for the panel sheet object" - def __init__(self,vobj): + def __init__(self, vobj): - Draft.ViewProviderDraft.__init__(self,vobj) + Draft.ViewProviderDraft.__init__(self, vobj) self.setProperties(vobj) vobj.PatternSize = 0.0035 - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList if not "Margin" in pl: - vobj.addProperty("App::PropertyLength","Margin","PanelSheet",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "Margin", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "A margin inside the boundary"), + locked=True, + ) if not "ShowMargin" in pl: - vobj.addProperty("App::PropertyBool","ShowMargin","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowMargin", + "PanelSheet", + QT_TRANSLATE_NOOP("App::Property", "Turns the display of the margin on/off"), + locked=True, + ) if not "ShowGrain" in pl: - vobj.addProperty("App::PropertyBool","ShowGrain","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Turns the display of the wood grain texture on/off"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowGrain", + "PanelSheet", + QT_TRANSLATE_NOOP( + "App::Property", "Turns the display of the wood grain texture on/off" + ), + locked=True, + ) - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) @@ -998,15 +1312,16 @@ class ViewProviderPanelSheet(Draft.ViewProviderDraft): FreeCADGui.Control.closeDialog() return True - def attach(self,vobj): + def attach(self, vobj): - Draft.ViewProviderDraft.attach(self,vobj) + Draft.ViewProviderDraft.attach(self, vobj) from pivy import coin + self.coords = coin.SoCoordinate3() self.lineset = coin.SoLineSet() self.lineset.numVertices.setValue(-1) lineStyle = coin.SoDrawStyle() - lineStyle.linePattern = 0x0f0f + lineStyle.linePattern = 0x0F0F self.color = coin.SoBaseColor() self.switch = coin.SoSwitch() sep = coin.SoSeparator() @@ -1017,58 +1332,65 @@ class ViewProviderPanelSheet(Draft.ViewProviderDraft): sep.addChild(self.lineset) self.switch.addChild(sep) vobj.Annotation.addChild(self.switch) - self.onChanged(vobj,"ShowMargin") - self.onChanged(vobj,"LineColor") + self.onChanged(vobj, "ShowMargin") + self.onChanged(vobj, "LineColor") - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - if prop in ["Margin","ShowMargin"]: - if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"): - if (vobj.Margin.Value > 0) and (vobj.Margin.Value < vobj.Object.Width.Value/2) and (vobj.Margin.Value < vobj.Object.Height.Value/2): - l2 = vobj.Object.Width.Value/2 - w2 = vobj.Object.Height.Value/2 + if prop in ["Margin", "ShowMargin"]: + if hasattr(vobj, "Margin") and hasattr(vobj, "ShowMargin"): + if ( + (vobj.Margin.Value > 0) + and (vobj.Margin.Value < vobj.Object.Width.Value / 2) + and (vobj.Margin.Value < vobj.Object.Height.Value / 2) + ): + l2 = vobj.Object.Width.Value / 2 + w2 = vobj.Object.Height.Value / 2 v = vobj.Margin.Value - v1 = (-l2+v,-w2+v,0) - v2 = (l2-v,-w2+v,0) - v3 = (l2-v,w2-v,0) - v4 = (-l2+v,w2-v,0) - self.coords.point.setValues([v1,v2,v3,v4,v1]) + v1 = (-l2 + v, -w2 + v, 0) + v2 = (l2 - v, -w2 + v, 0) + v3 = (l2 - v, w2 - v, 0) + v4 = (-l2 + v, w2 - v, 0) + self.coords.point.setValues([v1, v2, v3, v4, v1]) self.lineset.numVertices.setValue(5) if vobj.ShowMargin: self.switch.whichChild = 0 else: self.switch.whichChild = -1 elif prop == "LineColor": - if hasattr(vobj,"LineColor"): + if hasattr(vobj, "LineColor"): c = vobj.LineColor - self.color.rgb.setValue(c[0],c[1],c[2]) + self.color.rgb.setValue(c[0], c[1], c[2]) elif prop == "ShowGrain": - if hasattr(vobj,"ShowGrain"): + if hasattr(vobj, "ShowGrain"): if vobj.ShowGrain: vobj.Pattern = "woodgrain" else: vobj.Pattern = "None" - Draft.ViewProviderDraft.onChanged(self,vobj,prop) + Draft.ViewProviderDraft.onChanged(self, vobj, prop) + def updateData(self, obj, prop): - def updateData(self,obj,prop): - - if prop in ["Width","Height"]: - self.onChanged(obj.ViewObject,"Margin") + if prop in ["Width", "Height"]: + self.onChanged(obj.ViewObject, "Margin") elif prop == "GrainDirection": - if hasattr(self,"texcoords"): + if hasattr(self, "texcoords"): if self.texcoords: s = FreeCAD.Vector(self.texcoords.directionS.getValue().getValue()).Length - vS = DraftVecUtils.rotate(FreeCAD.Vector(s,0,0),-math.radians(obj.GrainDirection.Value)) - vT = DraftVecUtils.rotate(FreeCAD.Vector(0,s,0),-math.radians(obj.GrainDirection.Value)) - self.texcoords.directionS.setValue(vS.x,vS.y,vS.z) - self.texcoords.directionT.setValue(vT.x,vT.y,vT.z) - Draft.ViewProviderDraft.updateData(self,obj,prop) + vS = DraftVecUtils.rotate( + FreeCAD.Vector(s, 0, 0), -math.radians(obj.GrainDirection.Value) + ) + vT = DraftVecUtils.rotate( + FreeCAD.Vector(0, s, 0), -math.radians(obj.GrainDirection.Value) + ) + self.texcoords.directionS.setValue(vS.x, vS.y, vS.z) + self.texcoords.directionT.setValue(vT.x, vT.y, vT.z) + Draft.ViewProviderDraft.updateData(self, obj, prop) class SheetTaskPanel(ArchComponent.ComponentTaskPanel): - def __init__(self,obj): + def __init__(self, obj): ArchComponent.ComponentTaskPanel.__init__(self) self.obj = obj @@ -1080,7 +1402,7 @@ class SheetTaskPanel(ArchComponent.ComponentTaskPanel): self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit views positions", None)) lay.addWidget(self.editButton) QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes) - self.form = [self.form,self.optwid] + self.form = [self.form, self.optwid] def editNodes(self): diff --git a/src/Mod/BIM/ArchPipe.py b/src/Mod/BIM/ArchPipe.py index 87f8139fb3..4fdd595f45 100644 --- a/src/Mod/BIM/ArchPipe.py +++ b/src/Mod/BIM/ArchPipe.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "Arch Pipe tools" +__title__ = "Arch Pipe tools" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchPipe # \ingroup ARCH @@ -46,61 +46,134 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _ArchPipe(ArchComponent.Component): - - "the Arch Pipe object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Pipe" self.setProperties(obj) # IfcPipeSegment is new in IFC4 from ArchIFC import IfcTypes + if "Pipe Segment" in IfcTypes: obj.IfcType = "Pipe Segment" else: # IFC2x3 does not know a Pipe Segment obj.IfcType = "Building Element Proxy" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Diameter" in pl: - obj.addProperty("App::PropertyLength", "Diameter", "Pipe", QT_TRANSLATE_NOOP("App::Property","The diameter of this pipe, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Diameter", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "The diameter of this pipe, if not based on a profile" + ), + locked=True, + ) if not "Width" in pl: - obj.addProperty("App::PropertyLength", "Width", "Pipe", QT_TRANSLATE_NOOP("App::Property","The width of this pipe, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "The width of this pipe, if not based on a profile" + ), + locked=True, + ) obj.setPropertyStatus("Width", "Hidden") if not "Height" in pl: - obj.addProperty("App::PropertyLength", "Height", "Pipe", QT_TRANSLATE_NOOP("App::Property","The height of this pipe, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "The height of this pipe, if not based on a profile" + ), + locked=True, + ) obj.setPropertyStatus("Height", "Hidden") if not "Length" in pl: - obj.addProperty("App::PropertyLength", "Length", "Pipe", QT_TRANSLATE_NOOP("App::Property","The length of this pipe, if not based on an edge"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Length", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "The length of this pipe, if not based on an edge" + ), + locked=True, + ) if not "Profile" in pl: - obj.addProperty("App::PropertyLink", "Profile", "Pipe", QT_TRANSLATE_NOOP("App::Property","An optional closed profile to base this pipe on"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Profile", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "An optional closed profile to base this pipe on" + ), + locked=True, + ) if not "OffsetStart" in pl: - obj.addProperty("App::PropertyLength", "OffsetStart", "Pipe", QT_TRANSLATE_NOOP("App::Property","Offset from the start point"), locked=True) + obj.addProperty( + "App::PropertyLength", + "OffsetStart", + "Pipe", + QT_TRANSLATE_NOOP("App::Property", "Offset from the start point"), + locked=True, + ) if not "OffsetEnd" in pl: - obj.addProperty("App::PropertyLength", "OffsetEnd", "Pipe", QT_TRANSLATE_NOOP("App::Property","Offset from the end point"), locked=True) + obj.addProperty( + "App::PropertyLength", + "OffsetEnd", + "Pipe", + QT_TRANSLATE_NOOP("App::Property", "Offset from the end point"), + locked=True, + ) if not "WallThickness" in pl: - obj.addProperty("App::PropertyLength", "WallThickness","Pipe", QT_TRANSLATE_NOOP("App::Property","The wall thickness of this pipe, if not based on a profile"), locked=True) + obj.addProperty( + "App::PropertyLength", + "WallThickness", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", "The wall thickness of this pipe, if not based on a profile" + ), + locked=True, + ) if not "ProfileType" in pl: - obj.addProperty("App::PropertyEnumeration", "ProfileType", "Pipe", QT_TRANSLATE_NOOP("App::Property","If not based on a profile, this controls the profile of this pipe"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "ProfileType", + "Pipe", + QT_TRANSLATE_NOOP( + "App::Property", + "If not based on a profile, this controls the profile of this pipe", + ), + locked=True, + ) obj.ProfileType = ["Circle", "Square", "Rectangle"] - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - if obj.ProfileType == "Rectangle" and FreeCAD.ActiveDocument.getProgramVersion().split()[0] <= "1.1R42474": + if ( + obj.ProfileType == "Rectangle" + and FreeCAD.ActiveDocument.getProgramVersion().split()[0] <= "1.1R42474" + ): # in older versions Height and Width are inverted obj.Height, obj.Width = obj.Width, obj.Height FreeCAD.ActiveDocument.recompute() @@ -112,7 +185,7 @@ class _ArchPipe(ArchComponent.Component): + "\n" ) - def loads(self,state): + def loads(self, state): self.Type = "Pipe" @@ -135,74 +208,76 @@ class _ArchPipe(ArchComponent.Component): obj.setPropertyStatus("Diameter", "-Hidden") obj.setPropertyStatus("Width", "Hidden") - def execute(self,obj): + def execute(self, obj): import math import Part import DraftGeomUtils + if self.clone(obj): return pl = obj.Placement w = self.getWire(obj) if not w: - FreeCAD.Console.PrintError(translate("Arch","Unable to build the base path")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Unable to build the base path") + "\n") return if obj.OffsetStart.Value: e = w.Edges[0] v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(obj.OffsetStart.Value) - e = Part.LineSegment(e.Vertexes[0].Point.add(v),e.Vertexes[-1].Point).toShape() - w = Part.Wire([e]+w.Edges[1:]) + e = Part.LineSegment(e.Vertexes[0].Point.add(v), e.Vertexes[-1].Point).toShape() + w = Part.Wire([e] + w.Edges[1:]) if obj.OffsetEnd.Value: e = w.Edges[-1] v = e.Vertexes[0].Point.sub(e.Vertexes[-1].Point).normalize() v.multiply(obj.OffsetEnd.Value) - e = Part.LineSegment(e.Vertexes[-1].Point.add(v),e.Vertexes[0].Point).toShape() - w = Part.Wire(w.Edges[:-1]+[e]) + e = Part.LineSegment(e.Vertexes[-1].Point.add(v), e.Vertexes[0].Point).toShape() + w = Part.Wire(w.Edges[:-1] + [e]) p = self.getProfile(obj) if not p: - FreeCAD.Console.PrintError(translate("Arch","Unable to build the profile")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Unable to build the profile") + "\n") return # move and rotate the profile to the first point - if hasattr(p,"CenterOfMass"): + if hasattr(p, "CenterOfMass"): c = p.CenterOfMass else: c = p.BoundBox.Center - delta = w.Vertexes[0].Point-c + delta = w.Vertexes[0].Point - c p.translate(delta) import Draft + if Draft.getType(obj.Base) == "BezCurve": - v1 = obj.Base.Placement.multVec(obj.Base.Points[1])-w.Vertexes[0].Point + v1 = obj.Base.Placement.multVec(obj.Base.Points[1]) - w.Vertexes[0].Point else: - v1 = w.Vertexes[1].Point-w.Vertexes[0].Point - #v2 = DraftGeomUtils.getNormal(p) - #rot = FreeCAD.Rotation(v2,v1) + v1 = w.Vertexes[1].Point - w.Vertexes[0].Point + # v2 = DraftGeomUtils.getNormal(p) + # rot = FreeCAD.Rotation(v2,v1) # rotate keeping up vector - if v1.getAngle(FreeCAD.Vector(0,0,1)) > 0.01: - up = FreeCAD.Vector(0,0,1) + if v1.getAngle(FreeCAD.Vector(0, 0, 1)) > 0.01: + up = FreeCAD.Vector(0, 0, 1) else: - up = FreeCAD.Vector(0,1,0) + up = FreeCAD.Vector(0, 1, 0) v1y = up.cross(v1) v1x = v1.cross(v1y) - rot = FreeCAD.Rotation(v1x,v1y,v1,"ZYX") - p.rotate(w.Vertexes[0].Point,rot.Axis,math.degrees(rot.Angle)) - p.rotate(w.Vertexes[0].Point,v1,90) + rot = FreeCAD.Rotation(v1x, v1y, v1, "ZYX") + p.rotate(w.Vertexes[0].Point, rot.Axis, math.degrees(rot.Angle)) + p.rotate(w.Vertexes[0].Point, v1, 90) shapes = [] try: if p.Faces: for f in p.Faces: - sh = w.makePipeShell([f.OuterWire],True,False,2) + sh = w.makePipeShell([f.OuterWire], True, False, 2) for shw in f.Wires: if shw.hashCode() != f.OuterWire.hashCode(): - sh2 = w.makePipeShell([shw],True,False,2) + sh2 = w.makePipeShell([shw], True, False, 2) sh = sh.cut(sh2) shapes.append(sh) elif p.Wires: for pw in p.Wires: - sh = w.makePipeShell([pw],True,False,2) + sh = w.makePipeShell([pw], True, False, 2) shapes.append(sh) except Exception: - FreeCAD.Console.PrintError(translate("Arch","Unable to build the pipe")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Unable to build the pipe") + "\n") else: if len(shapes) == 0: return @@ -210,66 +285,120 @@ class _ArchPipe(ArchComponent.Component): sh = shapes[0] else: sh = Part.makeCompound(shapes) - obj.Shape = self.processSubShapes(obj,sh,pl) + obj.Shape = self.processSubShapes(obj, sh, pl) if obj.Base: obj.Length = w.Length else: obj.Placement = pl - def getWire(self,obj): + def getWire(self, obj): import Part + if obj.Base: - if not hasattr(obj.Base,'Shape'): - FreeCAD.Console.PrintError(translate("Arch","The base object is not a Part")+"\n") + if not hasattr(obj.Base, "Shape"): + FreeCAD.Console.PrintError( + translate("Arch", "The base object is not a Part") + "\n" + ) return if len(obj.Base.Shape.Wires) != 1: - FreeCAD.Console.PrintError(translate("Arch","Too many wires in the base shape")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Too many wires in the base shape") + "\n" + ) return if obj.Base.Shape.Wires[0].isClosed(): - FreeCAD.Console.PrintError(translate("Arch","The base wire is closed")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "The base wire is closed") + "\n") return w = obj.Base.Shape.Wires[0] else: if obj.Length.Value == 0: return - w = Part.Wire([Part.LineSegment(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,obj.Length.Value)).toShape()]) + w = Part.Wire( + [ + Part.LineSegment( + FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, obj.Length.Value) + ).toShape() + ] + ) return w - def getProfile(self,obj): + def getProfile(self, obj): import Part + if obj.Profile: if not obj.Profile.getLinkedObject().isDerivedFrom("Part::Part2DObject"): - FreeCAD.Console.PrintError(translate("Arch","The profile is not a 2D Part")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "The profile is not a 2D Part") + "\n") return if not obj.Profile.Shape.Wires[0].isClosed(): - FreeCAD.Console.PrintError(translate("Arch","The profile is not closed")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "The profile is not closed") + "\n") return p = obj.Profile.Shape.Wires[0] else: if obj.ProfileType == "Square": if obj.Width.Value == 0: return - p = Part.makePlane(obj.Width.Value, obj.Width.Value,FreeCAD.Vector(-obj.Width.Value/2,-obj.Width.Value/2,0)) - if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Width.Value/2): - p2 = Part.makePlane(obj.Width.Value-obj.WallThickness.Value*2,obj.Width.Value-obj.WallThickness.Value*2,FreeCAD.Vector(obj.WallThickness.Value-obj.Width.Value/2,obj.WallThickness.Value-obj.Width.Value/2,0)) + p = Part.makePlane( + obj.Width.Value, + obj.Width.Value, + FreeCAD.Vector(-obj.Width.Value / 2, -obj.Width.Value / 2, 0), + ) + if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Width.Value / 2): + p2 = Part.makePlane( + obj.Width.Value - obj.WallThickness.Value * 2, + obj.Width.Value - obj.WallThickness.Value * 2, + FreeCAD.Vector( + obj.WallThickness.Value - obj.Width.Value / 2, + obj.WallThickness.Value - obj.Width.Value / 2, + 0, + ), + ) p = p.cut(p2) elif obj.ProfileType == "Rectangle": if obj.Width.Value == 0: return if obj.Height.Value == 0: return - p = Part.makePlane(obj.Width.Value, obj.Height.Value,FreeCAD.Vector(-obj.Width.Value/2,-obj.Height.Value/2,0)) - if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Height.Value/2) and (obj.WallThickness.Value < obj.Width.Value/2): - p2 = Part.makePlane(obj.Width.Value-obj.WallThickness.Value*2,obj.Height.Value-obj.WallThickness.Value*2,FreeCAD.Vector(obj.WallThickness.Value-obj.Width.Value/2,obj.WallThickness.Value-obj.Height.Value/2,0)) + p = Part.makePlane( + obj.Width.Value, + obj.Height.Value, + FreeCAD.Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0), + ) + if ( + obj.WallThickness.Value + and (obj.WallThickness.Value < obj.Height.Value / 2) + and (obj.WallThickness.Value < obj.Width.Value / 2) + ): + p2 = Part.makePlane( + obj.Width.Value - obj.WallThickness.Value * 2, + obj.Height.Value - obj.WallThickness.Value * 2, + FreeCAD.Vector( + obj.WallThickness.Value - obj.Width.Value / 2, + obj.WallThickness.Value - obj.Height.Value / 2, + 0, + ), + ) p = p.cut(p2) else: if obj.Diameter.Value == 0: return - p = Part.Wire([Part.Circle(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),obj.Diameter.Value/2).toShape()]) - if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Diameter.Value/2): - p2 = Part.Wire([Part.Circle(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),(obj.Diameter.Value/2-obj.WallThickness.Value)).toShape()]) + p = Part.Wire( + [ + Part.Circle( + FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), obj.Diameter.Value / 2 + ).toShape() + ] + ) + if obj.WallThickness.Value and (obj.WallThickness.Value < obj.Diameter.Value / 2): + p2 = Part.Wire( + [ + Part.Circle( + FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + (obj.Diameter.Value / 2 - obj.WallThickness.Value), + ).toShape() + ] + ) p = Part.Face(p) p2 = Part.Face(p2) p = p.cut(p2) @@ -277,65 +406,83 @@ class _ArchPipe(ArchComponent.Component): class _ViewProviderPipe(ArchComponent.ViewProviderComponent): - - "A View Provider for the Pipe object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Pipe_Tree.svg" class _ArchPipeConnector(ArchComponent.Component): - - "the Arch Pipe Connector object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Pipe Fitting" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Radius" in pl: - obj.addProperty("App::PropertyLength", "Radius", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The curvature radius of this connector"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Radius", + "PipeConnector", + QT_TRANSLATE_NOOP("App::Property", "The curvature radius of this connector"), + locked=True, + ) if not "Pipes" in pl: - obj.addProperty("App::PropertyLinkList", "Pipes", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The pipes linked by this connector"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Pipes", + "PipeConnector", + QT_TRANSLATE_NOOP("App::Property", "The pipes linked by this connector"), + locked=True, + ) if not "ConnectorType" in pl: - obj.addProperty("App::PropertyEnumeration", "ConnectorType", "PipeConnector", QT_TRANSLATE_NOOP("App::Property","The type of this connector"), locked=True) - obj.ConnectorType = ["Corner","Tee"] - obj.setEditorMode("ConnectorType",1) + obj.addProperty( + "App::PropertyEnumeration", + "ConnectorType", + "PipeConnector", + QT_TRANSLATE_NOOP("App::Property", "The type of this connector"), + locked=True, + ) + obj.ConnectorType = ["Corner", "Tee"] + obj.setEditorMode("ConnectorType", 1) self.Type = "PipeConnector" - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return - tol = 1 # tolerance for alignment. This is only visual, we can keep it low... - ptol = 0.001 # tolerance for coincident points + tol = 1 # tolerance for alignment. This is only visual, we can keep it low... + ptol = 0.001 # tolerance for coincident points import math import Part import DraftGeomUtils import ArchCommands + if len(obj.Pipes) < 2: return if len(obj.Pipes) > 3: - FreeCAD.Console.PrintWarning(translate("Arch","Only the 3 first wires will be connected")+"\n") + FreeCAD.Console.PrintWarning( + translate("Arch", "Only the 3 first wires will be connected") + "\n" + ) if obj.Radius.Value == 0: return wires = [] @@ -343,19 +490,19 @@ class _ArchPipeConnector(ArchComponent.Component): for o in obj.Pipes: wires.append(o.Proxy.getWire(o)) if wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol: - order = ["start","start"] + order = ["start", "start"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol: - order = ["start","end"] + order = ["start", "end"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol: - order = ["end","end"] + order = ["end", "end"] point = wires[0].Vertexes[-1].Point elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol: - order = ["end","start"] + order = ["end", "start"] point = wires[0].Vertexes[-1].Point else: - FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Common vertex not found") + "\n") return if order[0] == "start": v1 = wires[0].Vertexes[1].Point.sub(wires[0].Vertexes[0].Point).normalize() @@ -375,40 +522,42 @@ class _ArchPipeConnector(ArchComponent.Component): if len(obj.Pipes) == 2: if obj.ConnectorType != "Corner": obj.ConnectorType = "Corner" - if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]: - FreeCAD.Console.PrintError(translate("Arch","Pipes are already aligned")+"\n") + if round(v1.getAngle(v2), tol) in [0, round(math.pi, tol)]: + FreeCAD.Console.PrintError(translate("Arch", "Pipes are already aligned") + "\n") return normal = v2.cross(v1) - offset = math.tan(math.pi/2-v1.getAngle(v2)/2)*obj.Radius.Value + offset = math.tan(math.pi / 2 - v1.getAngle(v2) / 2) * obj.Radius.Value v1.multiply(offset) v2.multiply(offset) - self.setOffset(obj.Pipes[0],order[0],offset) - self.setOffset(obj.Pipes[1],order[1],offset) + self.setOffset(obj.Pipes[0], order[0], offset) + self.setOffset(obj.Pipes[1], order[1], offset) # find center perp = v1.cross(normal).normalize() perp.multiply(obj.Radius.Value) center = point.add(v1).add(perp) # move and rotate the profile to the first point - delta = point.add(v1)-p.CenterOfMass + delta = point.add(v1) - p.CenterOfMass p.translate(delta) - #vp = DraftGeomUtils.getNormal(p) - #rot = FreeCAD.Rotation(vp,v1) + # vp = DraftGeomUtils.getNormal(p) + # rot = FreeCAD.Rotation(vp,v1) # rotate keeping up vector - if v1.getAngle(FreeCAD.Vector(0,0,1)) > 0.01: - up = FreeCAD.Vector(0,0,1) + if v1.getAngle(FreeCAD.Vector(0, 0, 1)) > 0.01: + up = FreeCAD.Vector(0, 0, 1) else: - up = FreeCAD.Vector(0,1,0) + up = FreeCAD.Vector(0, 1, 0) v1y = up.cross(v1) v1x = v1.cross(v1y) - rot = FreeCAD.Rotation(v1x,v1y,v1,"ZYX") - p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) - p.rotate(p.CenterOfMass,v1,90) + rot = FreeCAD.Rotation(v1x, v1y, v1, "ZYX") + p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) + p.rotate(p.CenterOfMass, v1, 90) try: - sh = p.revolve(center,normal,math.degrees(math.pi-v1.getAngle(v2))) + sh = p.revolve(center, normal, math.degrees(math.pi - v1.getAngle(v2))) except: - FreeCAD.Console.PrintError(translate("Arch","Unable to revolve this connector")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Unable to revolve this connector") + "\n" + ) return - #sh = Part.makeCompound([sh]+[Part.Vertex(point),Part.Vertex(point.add(v1)),Part.Vertex(center),Part.Vertex(point.add(v2))]) + # sh = Part.makeCompound([sh]+[Part.Vertex(point),Part.Vertex(point.add(v1)),Part.Vertex(center),Part.Vertex(point.add(v2))]) else: if obj.ConnectorType != "Tee": obj.ConnectorType = "Tee" @@ -417,53 +566,53 @@ class _ArchPipeConnector(ArchComponent.Component): elif wires[0].Vertexes[-1].Point == point: order.append("end") else: - FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Common vertex not found") + "\n") if order[2] == "start": v3 = wires[2].Vertexes[1].Point.sub(wires[2].Vertexes[0].Point).normalize() else: v3 = wires[2].Vertexes[-2].Point.sub(wires[2].Vertexes[-1].Point).normalize() - if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]: - pair = [v1,v2,v3] - elif round(v1.getAngle(v3),tol) in [0,round(math.pi,tol)]: - pair = [v1,v3,v2] - elif round(v2.getAngle(v3),tol) in [0,round(math.pi,tol)]: - pair = [v2,v3,v1] + if round(v1.getAngle(v2), tol) in [0, round(math.pi, tol)]: + pair = [v1, v2, v3] + elif round(v1.getAngle(v3), tol) in [0, round(math.pi, tol)]: + pair = [v1, v3, v2] + elif round(v2.getAngle(v3), tol) in [0, round(math.pi, tol)]: + pair = [v2, v3, v1] else: - FreeCAD.Console.PrintError(translate("Arch","At least 2 pipes must align")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "At least 2 pipes must align") + "\n") return offset = obj.Radius.Value v1.multiply(offset) v2.multiply(offset) v3.multiply(offset) - self.setOffset(obj.Pipes[0],order[0],offset) - self.setOffset(obj.Pipes[1],order[1],offset) - self.setOffset(obj.Pipes[2],order[2],offset) + self.setOffset(obj.Pipes[0], order[0], offset) + self.setOffset(obj.Pipes[1], order[1], offset) + self.setOffset(obj.Pipes[2], order[2], offset) normal = pair[0].cross(pair[2]) # move and rotate the profile to the first point - delta = point.add(pair[0])-p.CenterOfMass + delta = point.add(pair[0]) - p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) - rot = FreeCAD.Rotation(vp,pair[0]) - p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) + rot = FreeCAD.Rotation(vp, pair[0]) + p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) t1 = p.extrude(pair[1].multiply(2)) # move and rotate the profile to the second point - delta = point.add(pair[2])-p.CenterOfMass + delta = point.add(pair[2]) - p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) - rot = FreeCAD.Rotation(vp,pair[2]) - p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) + rot = FreeCAD.Rotation(vp, pair[2]) + p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) t2 = p.extrude(pair[2].negative().multiply(2)) # create a cut plane - cp = Part.makePolygon([point,point.add(pair[0]),point.add(normal),point]) + cp = Part.makePolygon([point, point.add(pair[0]), point.add(normal), point]) cp = Part.Face(cp) - if cp.normalAt(0,0).getAngle(pair[2]) < math.pi/2: + if cp.normalAt(0, 0).getAngle(pair[2]) < math.pi / 2: cp.reverse() - cf, cv, invcv = ArchCommands.getCutVolume(cp,t2) + cf, cv, invcv = ArchCommands.getCutVolume(cp, t2) t2 = t2.cut(cv) sh = t1.fuse(t2) obj.Shape = sh - def setOffset(self,pipe,pos,offset): + def setOffset(self, pipe, pos, offset): if pos == "start": if pipe.OffsetStart != offset: diff --git a/src/Mod/BIM/ArchPrecast.py b/src/Mod/BIM/ArchPrecast.py index 40906941d6..f1a13b384a 100644 --- a/src/Mod/BIM/ArchPrecast.py +++ b/src/Mod/BIM/ArchPrecast.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Precast concrete module" +__title__ = "FreeCAD Precast concrete module" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchPrecast # \ingroup ARCH @@ -48,78 +48,126 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _Precast(ArchComponent.Component): - "The base Precast class" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Precast" - _Precast.setProperties(self,obj) + _Precast.setProperties(self, obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Length" in pl: - obj.addProperty("App::PropertyDistance","Length","Structure",QT_TRANSLATE_NOOP("App::Property","The length of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Length", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The length of this element"), + locked=True, + ) if not "Width" in pl: - obj.addProperty("App::PropertyDistance","Width","Structure",QT_TRANSLATE_NOOP("App::Property","The width of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Width", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The width of this element"), + locked=True, + ) if not "Height" in pl: - obj.addProperty("App::PropertyDistance","Height","Structure",QT_TRANSLATE_NOOP("App::Property","The height of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Height", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The height of this element"), + 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) + obj.addProperty( + "App::PropertyVectorList", + "Nodes", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The structural nodes of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) - _Precast.setProperties(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) + _Precast.setProperties(self, obj) - def loads(self,state): + def loads(self, state): self.Type = "Precast" - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return class _PrecastBeam(_Precast): - "The Precast Beam" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Beam" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Chamfer" in pl: - obj.addProperty("App::PropertyDistance","Chamfer","Beam",QT_TRANSLATE_NOOP("App::Property","The size of the chamfer of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Chamfer", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The size of the chamfer of this element"), + locked=True, + ) if not "DentLength" in pl: - obj.addProperty("App::PropertyDistance","DentLength","Beam",QT_TRANSLATE_NOOP("App::Property","The dent length of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "DentLength", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The dent length of this element"), + locked=True, + ) if not "DentHeight" in pl: - obj.addProperty("App::PropertyDistance","DentHeight","Beam",QT_TRANSLATE_NOOP("App::Property","The dent height of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "DentHeight", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The dent height of this element"), + locked=True, + ) if not "Dents" in pl: - obj.addProperty("App::PropertyStringList","Dents","Beam",QT_TRANSLATE_NOOP("App::Property","The dents of this element"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Dents", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The dents of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -133,46 +181,51 @@ class _PrecastBeam(_Precast): dentheight = obj.DentHeight.Value dents = obj.Dents - if length == 0 \ - or width == 0 \ - or height == 0 \ - or chamfer >= width / 2 \ - or chamfer >= height / 2: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if ( + length == 0 + or width == 0 + or height == 0 + or chamfer >= width / 2 + or chamfer >= height / 2 + ): + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return import Part + p = [] if chamfer > 0: - p.append(Vector(0,chamfer,0)) - p.append(Vector(0,width-chamfer,0)) - p.append(Vector(0,width,chamfer)) - p.append(Vector(0,width,height-chamfer)) - p.append(Vector(0,width-chamfer,height)) - p.append(Vector(0,chamfer,height)) - p.append(Vector(0,0,height-chamfer)) - p.append(Vector(0,0,chamfer)) + p.append(Vector(0, chamfer, 0)) + p.append(Vector(0, width - chamfer, 0)) + p.append(Vector(0, width, chamfer)) + p.append(Vector(0, width, height - chamfer)) + p.append(Vector(0, width - chamfer, height)) + p.append(Vector(0, chamfer, height)) + p.append(Vector(0, 0, height - chamfer)) + p.append(Vector(0, 0, chamfer)) else: - p.append(Vector(0,0,0)) - p.append(Vector(0,width,0)) - p.append(Vector(0,width,height)) - p.append(Vector(0,0,height)) + p.append(Vector(0, 0, 0)) + p.append(Vector(0, width, 0)) + p.append(Vector(0, width, height)) + p.append(Vector(0, 0, height)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(length,0,0)) + shape = f.extrude(Vector(length, 0, 0)) if (dentlength > 0) and (dentheight > 0): p = [] - p.append(Vector(0,0,0)) - p.append(Vector(dentlength,0,0)) - p.append(Vector(dentlength,width,0)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(dentlength, 0, 0)) + p.append(Vector(dentlength, width, 0)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - d1 = f.extrude(Vector(0,0,dentheight)) + d1 = f.extrude(Vector(0, 0, dentheight)) d2 = d1.copy() - d2.translate(Vector(length-dentlength,0,0)) + d2.translate(Vector(length - dentlength, 0, 0)) shape = shape.cut(d1) shape = shape.cut(d2) for dent in dents: @@ -191,53 +244,64 @@ class _PrecastBeam(_Precast): if dentslant >= dentheight: continue p = [] - p.append(Vector(0-dentchamfer,0,0)) - p.append(Vector(dentlength,0,dentslant)) - p.append(Vector(dentlength,0,dentheight)) - p.append(Vector(0-dentchamfer,0,dentheight)) + p.append(Vector(0 - dentchamfer, 0, 0)) + p.append(Vector(dentlength, 0, dentslant)) + p.append(Vector(dentlength, 0, dentheight)) + p.append(Vector(0 - dentchamfer, 0, dentheight)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - dentshape = f.extrude(Vector(0,dentwidth,0)) - dentshape.rotate(Vector(0,0,0),Vector(0,0,1),dentrotation) + dentshape = f.extrude(Vector(0, dentwidth, 0)) + dentshape.rotate(Vector(0, 0, 0), Vector(0, 0, 1), dentrotation) if dentrotation == 0: - dentshape.translate(Vector(length,dentoffset,0)) + dentshape.translate(Vector(length, dentoffset, 0)) elif dentrotation == 90: - dentshape.translate(Vector(length-dentoffset,width,0)) + dentshape.translate(Vector(length - dentoffset, width, 0)) elif dentrotation == 180: - dentshape.translate(Vector(0,width-dentoffset,0)) + dentshape.translate(Vector(0, width - dentoffset, 0)) elif dentrotation == 270: - dentshape.translate(Vector(dentoffset,0,0)) - dentshape.translate(Vector(0,0,dentlevel)) + dentshape.translate(Vector(dentoffset, 0, 0)) + dentshape.translate(Vector(0, 0, dentlevel)) shape = shape.fuse(dentshape) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _PrecastIbeam(_Precast): - "The Precast Ibeam" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Beam" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Chamfer" in pl: - obj.addProperty("App::PropertyDistance","Chamfer","Beam",QT_TRANSLATE_NOOP("App::Property","The chamfer length of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Chamfer", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The chamfer length of this element"), + locked=True, + ) if not "BeamBase" in pl: - obj.addProperty("App::PropertyDistance","BeamBase","Beam",QT_TRANSLATE_NOOP("App::Property","The base length of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "BeamBase", + "Beam", + QT_TRANSLATE_NOOP("App::Property", "The base length of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -249,69 +313,111 @@ class _PrecastIbeam(_Precast): base = obj.BeamBase.Value slant = obj.Chamfer.Value - if length == 0 \ - or width == 0 \ - or height == 0 \ - or slant * 2 >= width \ - or base * 2 + slant * 2 >= height: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if ( + length == 0 + or width == 0 + or height == 0 + or slant * 2 >= width + or base * 2 + slant * 2 >= height + ): + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return import Part + p = [] - p.append(Vector(0,0,0)) - p.append(Vector(0,0,base)) - p.append(Vector(0,slant,base+slant)) - p.append(Vector(0,slant,height-(base+slant))) - p.append(Vector(0,0,height-base)) - p.append(Vector(0,0,height)) - p.append(Vector(0,width,height)) - p.append(Vector(0,width,height-base)) - p.append(Vector(0,width-slant,height-(base+slant))) - p.append(Vector(0,width-slant,base+slant)) - p.append(Vector(0,width,base)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(0, 0, base)) + p.append(Vector(0, slant, base + slant)) + p.append(Vector(0, slant, height - (base + slant))) + p.append(Vector(0, 0, height - base)) + p.append(Vector(0, 0, height)) + p.append(Vector(0, width, height)) + p.append(Vector(0, width, height - base)) + p.append(Vector(0, width - slant, height - (base + slant))) + p.append(Vector(0, width - slant, base + slant)) + p.append(Vector(0, width, base)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(length,0,0)) + shape = f.extrude(Vector(length, 0, 0)) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _PrecastPillar(_Precast): - "The Precast Pillar" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Column" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Chamfer" in pl: - obj.addProperty("App::PropertyDistance","Chamfer","Column",QT_TRANSLATE_NOOP("App::Property","The size of the chamfer of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Chamfer", + "Column", + QT_TRANSLATE_NOOP("App::Property", "The size of the chamfer of this element"), + locked=True, + ) if not "GrooveDepth" in pl: - obj.addProperty("App::PropertyDistance","GrooveDepth","Column",QT_TRANSLATE_NOOP("App::Property","The groove depth of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "GrooveDepth", + "Column", + QT_TRANSLATE_NOOP("App::Property", "The groove depth of this element"), + locked=True, + ) if not "GrooveHeight" in pl: - obj.addProperty("App::PropertyDistance","GrooveHeight","Column",QT_TRANSLATE_NOOP("App::Property","The groove height of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "GrooveHeight", + "Column", + QT_TRANSLATE_NOOP("App::Property", "The groove height of this element"), + locked=True, + ) if not "GrooveSpacing" in pl: - obj.addProperty("App::PropertyDistance","GrooveSpacing","Column",QT_TRANSLATE_NOOP("App::Property","The spacing between the grooves of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "GrooveSpacing", + "Column", + QT_TRANSLATE_NOOP( + "App::Property", "The spacing between the grooves of this element" + ), + locked=True, + ) if not "GrooveNumber" in pl: - obj.addProperty("App::PropertyInteger","GrooveNumber","Column",QT_TRANSLATE_NOOP("App::Property","The number of grooves of this element"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "GrooveNumber", + "Column", + QT_TRANSLATE_NOOP("App::Property", "The number of grooves of this element"), + locked=True, + ) if not "Dents" in pl: - obj.addProperty("App::PropertyStringList","Dents","Column",QT_TRANSLATE_NOOP("App::Property","The dents of this element"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Dents", + "Column", + QT_TRANSLATE_NOOP("App::Property", "The dents of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -327,58 +433,70 @@ class _PrecastPillar(_Precast): number = obj.GrooveNumber dents = obj.Dents - if length == 0 \ - or width == 0 \ - or height == 0 \ - or chamfer >= width / 2 \ - or chamfer >= length / 2: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if ( + length == 0 + or width == 0 + or height == 0 + or chamfer >= width / 2 + or chamfer >= length / 2 + ): + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return import Part + p = [] if chamfer > 0: - p.append(Vector(chamfer,0,0)) - p.append(Vector(length-chamfer,0,0)) - p.append(Vector(length,chamfer,0)) - p.append(Vector(length,width-chamfer,0)) - p.append(Vector(length-chamfer,width,0)) - p.append(Vector(chamfer,width,0)) - p.append(Vector(0,width-chamfer,0)) - p.append(Vector(0,chamfer,0)) + p.append(Vector(chamfer, 0, 0)) + p.append(Vector(length - chamfer, 0, 0)) + p.append(Vector(length, chamfer, 0)) + p.append(Vector(length, width - chamfer, 0)) + p.append(Vector(length - chamfer, width, 0)) + p.append(Vector(chamfer, width, 0)) + p.append(Vector(0, width - chamfer, 0)) + p.append(Vector(0, chamfer, 0)) else: - p.append(Vector(0,0,0)) - p.append(Vector(length,0,0)) - p.append(Vector(length,width,0)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(length, 0, 0)) + p.append(Vector(length, width, 0)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(0,0,height)) + shape = f.extrude(Vector(0, 0, height)) - if (groovedepth > 0) and (grooveheight > 0) and (spacing > 0) and (number > 0) and (groovedepth < length/2) and (groovedepth < width/2): + if ( + (groovedepth > 0) + and (grooveheight > 0) + and (spacing > 0) + and (number > 0) + and (groovedepth < length / 2) + and (groovedepth < width / 2) + ): p1 = [] - p1.append(Vector(0,0,0)) - p1.append(Vector(length,0,0)) - p1.append(Vector(length,width,0)) - p1.append(Vector(0,width,0)) + p1.append(Vector(0, 0, 0)) + p1.append(Vector(length, 0, 0)) + p1.append(Vector(length, width, 0)) + p1.append(Vector(0, width, 0)) p1.append(p1[0]) p1 = Part.makePolygon(p1) f1 = Part.Face(p1) - groove = f1.extrude(Vector(0,0,grooveheight)) + groove = f1.extrude(Vector(0, 0, grooveheight)) p2 = [] - p2.append(Vector(groovedepth,groovedepth,0)) - p2.append(Vector(length-groovedepth,groovedepth,0)) - p2.append(Vector(length-groovedepth,width-groovedepth,0)) - p2.append(Vector(groovedepth,width-groovedepth,0)) + p2.append(Vector(groovedepth, groovedepth, 0)) + p2.append(Vector(length - groovedepth, groovedepth, 0)) + p2.append(Vector(length - groovedepth, width - groovedepth, 0)) + p2.append(Vector(groovedepth, width - groovedepth, 0)) p2.append(p2[0]) p2 = Part.makePolygon(p2) f2 = Part.Face(p2) - s = f2.extrude(Vector(0,0,grooveheight)) + s = f2.extrude(Vector(0, 0, grooveheight)) groove = groove.cut(s) for i in range(number): g = groove.copy() - g.translate(Vector(0,0,spacing + i*(spacing+grooveheight))) + g.translate(Vector(0, 0, spacing + i * (spacing + grooveheight))) shape = shape.cut(g) for dent in dents: @@ -397,56 +515,73 @@ class _PrecastPillar(_Precast): if dentslant >= dentheight: continue p = [] - p.append(Vector(0-dentchamfer,0,0)) - p.append(Vector(dentlength,0,dentslant)) - p.append(Vector(dentlength,0,dentheight)) - p.append(Vector(0-dentchamfer,0,dentheight)) + p.append(Vector(0 - dentchamfer, 0, 0)) + p.append(Vector(dentlength, 0, dentslant)) + p.append(Vector(dentlength, 0, dentheight)) + p.append(Vector(0 - dentchamfer, 0, dentheight)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - dentshape = f.extrude(Vector(0,dentwidth,0)) - dentshape.rotate(Vector(0,0,0),Vector(0,0,1),dentrotation) + dentshape = f.extrude(Vector(0, dentwidth, 0)) + dentshape.rotate(Vector(0, 0, 0), Vector(0, 0, 1), dentrotation) if dentrotation == 0: - dentshape.translate(Vector(length,dentoffset,0)) + dentshape.translate(Vector(length, dentoffset, 0)) elif dentrotation == 90: - dentshape.translate(Vector(length-dentoffset,width,0)) + dentshape.translate(Vector(length - dentoffset, width, 0)) elif dentrotation == 180: - dentshape.translate(Vector(0,width-dentoffset,0)) + dentshape.translate(Vector(0, width - dentoffset, 0)) elif dentrotation == 270: - dentshape.translate(Vector(dentoffset,0,0)) - dentshape.translate(Vector(0,0,dentlevel)) + dentshape.translate(Vector(dentoffset, 0, 0)) + dentshape.translate(Vector(0, 0, dentlevel)) shape = shape.fuse(dentshape) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _PrecastPanel(_Precast): - "The Precast Panel" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Plate" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Chamfer" in pl: - obj.addProperty("App::PropertyDistance","Chamfer","Panel",QT_TRANSLATE_NOOP("App::Property","The size of the chamfer of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Chamfer", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The size of the chamfer of this element"), + locked=True, + ) if not "DentWidth" in pl: - obj.addProperty("App::PropertyDistance","DentWidth","Panel",QT_TRANSLATE_NOOP("App::Property","The dent width of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "DentWidth", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The dent width of this element"), + locked=True, + ) if not "DentHeight" in pl: - obj.addProperty("App::PropertyDistance","DentHeight","Panel",QT_TRANSLATE_NOOP("App::Property","The dent height of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "DentHeight", + "Panel", + QT_TRANSLATE_NOOP("App::Property", "The dent height of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -459,112 +594,152 @@ class _PrecastPanel(_Precast): dentheight = obj.DentHeight.Value dentwidth = obj.DentWidth.Value - if length == 0 \ - or width == 0 \ - or height == 0 \ - or chamfer + dentwidth >= width \ - or dentheight >= height: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if ( + length == 0 + or width == 0 + or height == 0 + or chamfer + dentwidth >= width + or dentheight >= height + ): + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return import Part + p = [] - p.append(Vector(0,0,0)) - p.append(Vector(length,0,0)) - p.append(Vector(length,width,0)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(length, 0, 0)) + p.append(Vector(length, width, 0)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(0,0,height)) + shape = f.extrude(Vector(0, 0, height)) if chamfer > 0: p = [] - p.append(Vector(0,width-chamfer,0)) - p.append(Vector(chamfer,width,0)) - p.append(Vector(0,width,0)) + p.append(Vector(0, width - chamfer, 0)) + p.append(Vector(chamfer, width, 0)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - s = f.extrude(Vector(0,0,height)) + s = f.extrude(Vector(0, 0, height)) shape = shape.cut(s) p = [] - p.append(Vector(length,width-chamfer,0)) - p.append(Vector(length-chamfer,width,0)) - p.append(Vector(length,width,0)) + p.append(Vector(length, width - chamfer, 0)) + p.append(Vector(length - chamfer, width, 0)) + p.append(Vector(length, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - s = f.extrude(Vector(0,0,height)) + s = f.extrude(Vector(0, 0, height)) shape = shape.cut(s) p = [] - p.append(Vector(0,width-chamfer,0)) - p.append(Vector(0,width,chamfer)) - p.append(Vector(0,width,0)) + p.append(Vector(0, width - chamfer, 0)) + p.append(Vector(0, width, chamfer)) + p.append(Vector(0, width, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - s = f.extrude(Vector(length,0,0)) + s = f.extrude(Vector(length, 0, 0)) shape = shape.cut(s) p = [] - p.append(Vector(0,width-chamfer,height)) - p.append(Vector(0,width,height-chamfer)) - p.append(Vector(0,width,height)) + p.append(Vector(0, width - chamfer, height)) + p.append(Vector(0, width, height - chamfer)) + p.append(Vector(0, width, height)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - s = f.extrude(Vector(length,0,0)) + s = f.extrude(Vector(length, 0, 0)) shape = shape.cut(s) if (dentheight > 0) and (dentwidth > 0): p = [] - p.append(Vector(0,((width-chamfer)-dentwidth)/2,0)) - p.append(Vector(0,((width-chamfer)-dentwidth)/2+dentwidth,0)) - p.append(Vector(0,((width-chamfer)-dentwidth)/2+dentwidth,dentheight)) - p.append(Vector(0,((width-chamfer)-dentwidth)/2,dentheight)) + p.append(Vector(0, ((width - chamfer) - dentwidth) / 2, 0)) + p.append(Vector(0, ((width - chamfer) - dentwidth) / 2 + dentwidth, 0)) + p.append(Vector(0, ((width - chamfer) - dentwidth) / 2 + dentwidth, dentheight)) + p.append(Vector(0, ((width - chamfer) - dentwidth) / 2, dentheight)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - s = f.extrude(Vector(length,0,0)) + s = f.extrude(Vector(length, 0, 0)) shape = shape.cut(s) - s.translate(Vector(0,0,height)) + s.translate(Vector(0, 0, height)) shape = shape.fuse(s) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _PrecastSlab(_Precast): - "The Precast Slab" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Slab" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "SlabType" in pl: - obj.addProperty("App::PropertyEnumeration","SlabType","Slab",QT_TRANSLATE_NOOP("App::Property","The type of this slab"), locked=True) - obj.SlabType = ["Champagne","Hat"] + obj.addProperty( + "App::PropertyEnumeration", + "SlabType", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The type of this slab"), + locked=True, + ) + obj.SlabType = ["Champagne", "Hat"] if not "SlabBase" in pl: - obj.addProperty("App::PropertyDistance","SlabBase","Slab",QT_TRANSLATE_NOOP("App::Property","The size of the base of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "SlabBase", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The size of the base of this element"), + locked=True, + ) if not "HoleNumber" in pl: - obj.addProperty("App::PropertyInteger","HoleNumber","Slab",QT_TRANSLATE_NOOP("App::Property","The number of holes in this element"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "HoleNumber", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The number of holes in this element"), + locked=True, + ) if not "HoleMajor" in pl: - obj.addProperty("App::PropertyDistance","HoleMajor","Slab",QT_TRANSLATE_NOOP("App::Property","The major radius of the holes of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "HoleMajor", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The major radius of the holes of this element"), + locked=True, + ) if not "HoleMinor" in pl: - obj.addProperty("App::PropertyDistance","HoleMinor","Slab",QT_TRANSLATE_NOOP("App::Property","The minor radius of the holes of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "HoleMinor", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The minor radius of the holes of this element"), + locked=True, + ) if not "HoleSpacing" in pl: - obj.addProperty("App::PropertyDistance","HoleSpacing","Slab",QT_TRANSLATE_NOOP("App::Property","The spacing between the holes of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "HoleSpacing", + "Slab", + QT_TRANSLATE_NOOP("App::Property", "The spacing between the holes of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -580,97 +755,127 @@ class _PrecastSlab(_Precast): holeminor = obj.HoleMinor.Value holespacing = obj.HoleSpacing.Value - slant = (height - base) / 3 # this gives the inclination of the vertical walls + slant = (height - base) / 3 # this gives the inclination of the vertical walls if base == 0: base = min(height / 4, 50) obj.SlabBase = base - if length == 0 \ - or width == 0 \ - or height == 0 \ - or width <= slant * 2 \ - or height < base * 2 \ - or (holenumber > 0 and (holespacing == 0 or holemajor == 0 or holeminor == 0)) \ - or holemajor < holeminor: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if ( + length == 0 + or width == 0 + or height == 0 + or width <= slant * 2 + or height < base * 2 + or (holenumber > 0 and (holespacing == 0 or holemajor == 0 or holeminor == 0)) + or holemajor < holeminor + ): + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return import Part + p = [] if slabtype == "Champagne": - p.append(Vector(0,0,0)) - p.append(Vector(0,slant,height-base)) - p.append(Vector(0,0,height-base)) - p.append(Vector(0,0,height)) - p.append(Vector(0,width,height)) - p.append(Vector(0,width,height-base)) - p.append(Vector(0,width-slant,height-base)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(0, slant, height - base)) + p.append(Vector(0, 0, height - base)) + p.append(Vector(0, 0, height)) + p.append(Vector(0, width, height)) + p.append(Vector(0, width, height - base)) + p.append(Vector(0, width - slant, height - base)) + p.append(Vector(0, width, 0)) elif slabtype == "Hat": - p.append(Vector(0,0,0)) - p.append(Vector(0,0,base)) - p.append(Vector(0,slant,base)) - p.append(Vector(0,slant*2,height)) - p.append(Vector(0,width-slant*2,height)) - p.append(Vector(0,width-slant,base)) - p.append(Vector(0,width,base)) - p.append(Vector(0,width,0)) + p.append(Vector(0, 0, 0)) + p.append(Vector(0, 0, base)) + p.append(Vector(0, slant, base)) + p.append(Vector(0, slant * 2, height)) + p.append(Vector(0, width - slant * 2, height)) + p.append(Vector(0, width - slant, base)) + p.append(Vector(0, width, base)) + p.append(Vector(0, width, 0)) else: return None p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(length,0,0)) + shape = f.extrude(Vector(length, 0, 0)) if holenumber > 0: holespan = holenumber * holeminor + (holenumber - 1) * holespacing - holestart = (width/2 - holespan/2) + holeminor/2 + holestart = (width / 2 - holespan / 2) + holeminor / 2 if holeminor != holemajor: - e = Part.Ellipse(Vector(0,0,0),Vector(0,holeminor/2,0),Vector(0,0,holemajor/2)).toShape() - e.translate(Vector(0,0,-holemajor/2)) + e = Part.Ellipse( + Vector(0, 0, 0), Vector(0, holeminor / 2, 0), Vector(0, 0, holemajor / 2) + ).toShape() + e.translate(Vector(0, 0, -holemajor / 2)) else: - e = Part.Circle(Vector(0,0,0),Vector(1,0,0),holemajor/2).toShape() + e = Part.Circle(Vector(0, 0, 0), Vector(1, 0, 0), holemajor / 2).toShape() w = Part.Wire([e]) f = Part.Face(w) - tube = f.extrude(Vector(length,0,0)) + tube = f.extrude(Vector(length, 0, 0)) for i in range(holenumber): - x = holestart + i*(holeminor+holespacing) + x = holestart + i * (holeminor + holespacing) s = tube.copy() - s.translate(Vector(0,x,height/2)) + s.translate(Vector(0, x, height / 2)) shape = shape.cut(s) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _PrecastStairs(_Precast): - "The Precast Stairs" - def __init__(self,obj): + def __init__(self, obj): - _Precast.__init__(self,obj) + _Precast.__init__(self, obj) self.setProperties(obj) obj.IfcType = "Stair" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "DownLength" in pl: - obj.addProperty("App::PropertyDistance","DownLength","Stairs",QT_TRANSLATE_NOOP("App::Property","The length of the down floor of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "DownLength", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The length of the down floor of this element"), + locked=True, + ) if not "RiserNumber" in pl: - obj.addProperty("App::PropertyInteger","RiserNumber","Stairs",QT_TRANSLATE_NOOP("App::Property","The number of risers in this element"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "RiserNumber", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The number of risers in this element"), + locked=True, + ) if not "Riser" in pl: - obj.addProperty("App::PropertyDistance","Riser","Stairs",QT_TRANSLATE_NOOP("App::Property","The riser height of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Riser", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The riser height of this element"), + locked=True, + ) if not "Tread" in pl: - obj.addProperty("App::PropertyDistance","Tread","Stairs",QT_TRANSLATE_NOOP("App::Property","The tread depth of this element"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Tread", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The tread depth of this element"), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - _Precast.onDocumentRestored(self,obj) + _Precast.onDocumentRestored(self, obj) self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -684,72 +889,70 @@ class _PrecastStairs(_Precast): riser = obj.Riser.Value tread = obj.Tread.Value - if width == 0 \ - or steps == 0 \ - or riser == 0 \ - or tread == 0 \ - or height == 0: - FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch", "has a null shape") + "\n") + if width == 0 or steps == 0 or riser == 0 or tread == 0 or height == 0: + FreeCAD.Console.PrintWarning( + obj.Label + " " + translate("Arch", "has a null shape") + "\n" + ) return if length < tread: - length = tread # minimum + length = tread # minimum import math import Part - p = [Vector(0,0,0)] # relative moves + p = [Vector(0, 0, 0)] # relative moves if downlength: - p.append(Vector(0,downlength,0)) - for i in range(steps-1): - p.append(Vector(0,0,riser)) - p.append(Vector(0,tread,0)) - p.append(Vector(0,0,riser)) - p.append(Vector(0,length,0)) - ang1 = math.atan(riser/tread) - ang2 = ang1/2 - rdist = math.tan(ang2)*height - if length > (tread+rdist): - p.append(Vector(0,0,-height)) - p.append(Vector(0,-(length-(tread+rdist)))) + p.append(Vector(0, downlength, 0)) + for i in range(steps - 1): + p.append(Vector(0, 0, riser)) + p.append(Vector(0, tread, 0)) + p.append(Vector(0, 0, riser)) + p.append(Vector(0, length, 0)) + ang1 = math.atan(riser / tread) + ang2 = ang1 / 2 + rdist = math.tan(ang2) * height + if length > (tread + rdist): + p.append(Vector(0, 0, -height)) + p.append(Vector(0, -(length - (tread + rdist)))) else: - rest = length-(tread+rdist) - addh = math.tan(ang1)*rest - p.append(Vector(0,0,-(height+addh))) + rest = length - (tread + rdist) + addh = math.tan(ang1) * rest + p.append(Vector(0, 0, -(height + addh))) # absolutize r = [p[0]] for m in p[1:]: r.append(r[-1].add(m)) p = r if downlength: - bdist = math.tan(ang1)*height - p.append(Vector(0,downlength+bdist,-height)) - p.append(Vector(0,0,-height)) + bdist = math.tan(ang1) * height + p.append(Vector(0, downlength + bdist, -height)) + p.append(Vector(0, 0, -height)) else: - hdist = height*(math.tan(ang1)) - p.append(Vector(0,hdist,0)) + hdist = height * (math.tan(ang1)) + p.append(Vector(0, hdist, 0)) p.append(p[0]) p = Part.makePolygon(p) f = Part.Face(p) - shape = f.extrude(Vector(width,0,0)) + shape = f.extrude(Vector(width, 0, 0)) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) class _ViewProviderPrecast(ArchComponent.ViewProviderComponent): - "The View Provider of the Precast object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) vobj.ShapeColor = ArchCommands.getDefaultColor("Structure") def getIcon(self): import Arch_rc - if hasattr(self,"Object"): + + if hasattr(self, "Object"): if self.Object.CloneOf: return ":/icons/Arch_Structure_Clone.svg" return ":/icons/Arch_Structure_Tree.svg" @@ -759,14 +962,15 @@ class _ViewProviderPrecast(ArchComponent.ViewProviderComponent): return None import FreeCADGui + taskd = ArchComponent.ComponentTaskPanel() taskd.obj = self.Object taskd.update() - if hasattr(self.Object,"Dents"): + if hasattr(self.Object, "Dents"): self.dentd = _DentsTaskPanel() self.dentd.form.show() self.dentd.fillDents(self.Object.Dents) - taskd.form = [taskd.form,self.dentd.form] + taskd.form = [taskd.form, self.dentd.form] FreeCADGui.Control.showDialog(taskd) return True @@ -775,7 +979,8 @@ class _ViewProviderPrecast(ArchComponent.ViewProviderComponent): return None import FreeCADGui - if hasattr(self,"dentd"): + + if hasattr(self, "dentd"): self.Object.Dents = self.dentd.getValues() del self.dentd FreeCADGui.Control.closeDialog() @@ -783,132 +988,158 @@ class _ViewProviderPrecast(ArchComponent.ViewProviderComponent): class _PrecastTaskPanel: - - '''The TaskPanel for precast creation''' + """The TaskPanel for precast creation""" def __init__(self): import FreeCADGui from PySide import QtCore, QtGui, QtSvgWidgets + self.form = QtGui.QWidget() self.grid = QtGui.QGridLayout(self.form) - self.PrecastTypes = ["Beam","I-Beam","Pillar","Panel","Slab","Stairs"] - self.SlabTypes = ["Champagne","Hat"] + self.PrecastTypes = ["Beam", "I-Beam", "Pillar", "Panel", "Slab", "Stairs"] + self.SlabTypes = ["Champagne", "Hat"] # image display self.preview = QtSvgWidgets.QSvgWidget(":/ui/ParametersBeam.svg") self.preview.setMaximumWidth(200) self.preview.setMinimumHeight(120) - self.grid.addWidget(self.preview,0,0,1,2) + self.grid.addWidget(self.preview, 0, 0, 1, 2) # parameters self.labelSlabType = QtGui.QLabel() self.valueSlabType = QtGui.QComboBox() self.valueSlabType.addItems(self.SlabTypes) self.valueSlabType.setCurrentIndex(0) - self.grid.addWidget(self.labelSlabType,1,0,1,1) - self.grid.addWidget(self.valueSlabType,1,1,1,1) + self.grid.addWidget(self.labelSlabType, 1, 0, 1, 1) + self.grid.addWidget(self.valueSlabType, 1, 1, 1, 1) self.labelChamfer = QtGui.QLabel() self.valueChamfer = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelChamfer,2,0,1,1) - self.grid.addWidget(self.valueChamfer,2,1,1,1) + self.grid.addWidget(self.labelChamfer, 2, 0, 1, 1) + self.grid.addWidget(self.valueChamfer, 2, 1, 1, 1) self.labelDentLength = QtGui.QLabel() self.valueDentLength = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelDentLength,3,0,1,1) - self.grid.addWidget(self.valueDentLength,3,1,1,1) + self.grid.addWidget(self.labelDentLength, 3, 0, 1, 1) + self.grid.addWidget(self.valueDentLength, 3, 1, 1, 1) self.labelDentWidth = QtGui.QLabel() self.valueDentWidth = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelDentWidth,4,0,1,1) - self.grid.addWidget(self.valueDentWidth,4,1,1,1) + self.grid.addWidget(self.labelDentWidth, 4, 0, 1, 1) + self.grid.addWidget(self.valueDentWidth, 4, 1, 1, 1) self.labelDentHeight = QtGui.QLabel() self.valueDentHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelDentHeight,5,0,1,1) - self.grid.addWidget(self.valueDentHeight,5,1,1,1) + self.grid.addWidget(self.labelDentHeight, 5, 0, 1, 1) + self.grid.addWidget(self.valueDentHeight, 5, 1, 1, 1) self.labelBase = QtGui.QLabel() self.valueBase = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelBase,6,0,1,1) - self.grid.addWidget(self.valueBase,6,1,1,1) + self.grid.addWidget(self.labelBase, 6, 0, 1, 1) + self.grid.addWidget(self.valueBase, 6, 1, 1, 1) self.labelHoleNumber = QtGui.QLabel() self.valueHoleNumber = QtGui.QSpinBox() - self.grid.addWidget(self.labelHoleNumber,7,0,1,1) - self.grid.addWidget(self.valueHoleNumber,7,1,1,1) + self.grid.addWidget(self.labelHoleNumber, 7, 0, 1, 1) + self.grid.addWidget(self.valueHoleNumber, 7, 1, 1, 1) self.labelHoleMajor = QtGui.QLabel() self.valueHoleMajor = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelHoleMajor,8,0,1,1) - self.grid.addWidget(self.valueHoleMajor,8,1,1,1) + self.grid.addWidget(self.labelHoleMajor, 8, 0, 1, 1) + self.grid.addWidget(self.valueHoleMajor, 8, 1, 1, 1) self.labelHoleMinor = QtGui.QLabel() self.valueHoleMinor = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelHoleMinor,9,0,1,1) - self.grid.addWidget(self.valueHoleMinor,9,1,1,1) + self.grid.addWidget(self.labelHoleMinor, 9, 0, 1, 1) + self.grid.addWidget(self.valueHoleMinor, 9, 1, 1, 1) self.labelHoleSpacing = QtGui.QLabel() self.valueHoleSpacing = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelHoleSpacing,10,0,1,1) - self.grid.addWidget(self.valueHoleSpacing,10,1,1,1) + self.grid.addWidget(self.labelHoleSpacing, 10, 0, 1, 1) + self.grid.addWidget(self.valueHoleSpacing, 10, 1, 1, 1) self.labelGrooveNumber = QtGui.QLabel() self.valueGrooveNumber = QtGui.QSpinBox() - self.grid.addWidget(self.labelGrooveNumber,11,0,1,1) - self.grid.addWidget(self.valueGrooveNumber,11,1,1,1) + self.grid.addWidget(self.labelGrooveNumber, 11, 0, 1, 1) + self.grid.addWidget(self.valueGrooveNumber, 11, 1, 1, 1) self.labelGrooveDepth = QtGui.QLabel() self.valueGrooveDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelGrooveDepth,12,0,1,1) - self.grid.addWidget(self.valueGrooveDepth,12,1,1,1) + self.grid.addWidget(self.labelGrooveDepth, 12, 0, 1, 1) + self.grid.addWidget(self.valueGrooveDepth, 12, 1, 1, 1) self.labelGrooveHeight = QtGui.QLabel() self.valueGrooveHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelGrooveHeight,13,0,1,1) - self.grid.addWidget(self.valueGrooveHeight,13,1,1,1) + self.grid.addWidget(self.labelGrooveHeight, 13, 0, 1, 1) + self.grid.addWidget(self.valueGrooveHeight, 13, 1, 1, 1) self.labelGrooveSpacing = QtGui.QLabel() self.valueGrooveSpacing = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelGrooveSpacing,14,0,1,1) - self.grid.addWidget(self.valueGrooveSpacing,14,1,1,1) + self.grid.addWidget(self.labelGrooveSpacing, 14, 0, 1, 1) + self.grid.addWidget(self.valueGrooveSpacing, 14, 1, 1, 1) self.labelRiserNumber = QtGui.QLabel() self.valueRiserNumber = QtGui.QSpinBox() - self.grid.addWidget(self.labelRiserNumber,15,0,1,1) - self.grid.addWidget(self.valueRiserNumber,15,1,1,1) + self.grid.addWidget(self.labelRiserNumber, 15, 0, 1, 1) + self.grid.addWidget(self.valueRiserNumber, 15, 1, 1, 1) self.labelDownLength = QtGui.QLabel() self.valueDownLength = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelDownLength,16,0,1,1) - self.grid.addWidget(self.valueDownLength,16,1,1,1) + self.grid.addWidget(self.labelDownLength, 16, 0, 1, 1) + self.grid.addWidget(self.valueDownLength, 16, 1, 1, 1) self.labelRiser = QtGui.QLabel() self.valueRiser = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelRiser,17,0,1,1) - self.grid.addWidget(self.valueRiser,17,1,1,1) + self.grid.addWidget(self.labelRiser, 17, 0, 1, 1) + self.grid.addWidget(self.valueRiser, 17, 1, 1, 1) self.labelTread = QtGui.QLabel() self.valueTread = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelTread,18,0,1,1) - self.grid.addWidget(self.valueTread,18,1,1,1) + self.grid.addWidget(self.labelTread, 18, 0, 1, 1) + self.grid.addWidget(self.valueTread, 18, 1, 1, 1) # signals/slots - QtCore.QObject.connect(self.valueChamfer,QtCore.SIGNAL("valueChanged(double)"),self.setChamfer) - QtCore.QObject.connect(self.valueDentLength,QtCore.SIGNAL("valueChanged(double)"),self.setDentLength) - QtCore.QObject.connect(self.valueDentWidth,QtCore.SIGNAL("valueChanged(double)"),self.setDentWidth) - QtCore.QObject.connect(self.valueDentHeight,QtCore.SIGNAL("valueChanged(double)"),self.setDentHeight) - QtCore.QObject.connect(self.valueBase,QtCore.SIGNAL("valueChanged(double)"),self.setBase) - QtCore.QObject.connect(self.valueHoleMajor,QtCore.SIGNAL("valueChanged(double)"),self.setHoleMajor) - QtCore.QObject.connect(self.valueHoleMinor,QtCore.SIGNAL("valueChanged(double)"),self.setHoleMinor) - QtCore.QObject.connect(self.valueHoleSpacing,QtCore.SIGNAL("valueChanged(double)"),self.setHoleSpacing) - QtCore.QObject.connect(self.valueGrooveDepth,QtCore.SIGNAL("valueChanged(double)"),self.setGrooveDepth) - QtCore.QObject.connect(self.valueGrooveHeight,QtCore.SIGNAL("valueChanged(double)"),self.setGrooveHeight) - QtCore.QObject.connect(self.valueGrooveSpacing,QtCore.SIGNAL("valueChanged(double)"),self.setGrooveSpacing) - QtCore.QObject.connect(self.valueDownLength,QtCore.SIGNAL("valueChanged(double)"),self.setDownLength) - QtCore.QObject.connect(self.valueRiser,QtCore.SIGNAL("valueChanged(double)"),self.setRiser) - QtCore.QObject.connect(self.valueTread,QtCore.SIGNAL("valueChanged(double)"),self.setTread) + QtCore.QObject.connect( + self.valueChamfer, QtCore.SIGNAL("valueChanged(double)"), self.setChamfer + ) + QtCore.QObject.connect( + self.valueDentLength, QtCore.SIGNAL("valueChanged(double)"), self.setDentLength + ) + QtCore.QObject.connect( + self.valueDentWidth, QtCore.SIGNAL("valueChanged(double)"), self.setDentWidth + ) + QtCore.QObject.connect( + self.valueDentHeight, QtCore.SIGNAL("valueChanged(double)"), self.setDentHeight + ) + QtCore.QObject.connect(self.valueBase, QtCore.SIGNAL("valueChanged(double)"), self.setBase) + QtCore.QObject.connect( + self.valueHoleMajor, QtCore.SIGNAL("valueChanged(double)"), self.setHoleMajor + ) + QtCore.QObject.connect( + self.valueHoleMinor, QtCore.SIGNAL("valueChanged(double)"), self.setHoleMinor + ) + QtCore.QObject.connect( + self.valueHoleSpacing, QtCore.SIGNAL("valueChanged(double)"), self.setHoleSpacing + ) + QtCore.QObject.connect( + self.valueGrooveDepth, QtCore.SIGNAL("valueChanged(double)"), self.setGrooveDepth + ) + QtCore.QObject.connect( + self.valueGrooveHeight, QtCore.SIGNAL("valueChanged(double)"), self.setGrooveHeight + ) + QtCore.QObject.connect( + self.valueGrooveSpacing, QtCore.SIGNAL("valueChanged(double)"), self.setGrooveSpacing + ) + QtCore.QObject.connect( + self.valueDownLength, QtCore.SIGNAL("valueChanged(double)"), self.setDownLength + ) + QtCore.QObject.connect( + self.valueRiser, QtCore.SIGNAL("valueChanged(double)"), self.setRiser + ) + QtCore.QObject.connect( + self.valueTread, QtCore.SIGNAL("valueChanged(double)"), self.setTread + ) # restore presets self.restoreValue(self.valueChamfer, params.get_param_arch("PrecastChamfer")) @@ -952,68 +1183,69 @@ class _PrecastTaskPanel: d["DownLength"] = self.DownLength d["Riser"] = self.Riser d["Tread"] = self.Tread - if hasattr(self,"Dents"): + if hasattr(self, "Dents"): d["Dents"] = self.Dents.getValues() return d - def setChamfer(self,value): + def setChamfer(self, value): self.Chamfer = value - params.set_param_arch("PrecastChamfer",value) + params.set_param_arch("PrecastChamfer", value) - def setDentLength(self,value): + def setDentLength(self, value): self.DentLength = value - params.set_param_arch("PrecastDentLength",value) + params.set_param_arch("PrecastDentLength", value) - def setDentWidth(self,value): + def setDentWidth(self, value): self.DentWidth = value - params.set_param_arch("PrecastDentWidth",value) + params.set_param_arch("PrecastDentWidth", value) - def setDentHeight(self,value): + def setDentHeight(self, value): self.DentHeight = value - params.set_param_arch("PrecastDentHeight",value) + params.set_param_arch("PrecastDentHeight", value) - def setBase(self,value): + def setBase(self, value): self.Base = value - params.set_param_arch("PrecastBase",value) + params.set_param_arch("PrecastBase", value) - def setHoleMajor(self,value): + def setHoleMajor(self, value): self.HoleMajor = value - params.set_param_arch("PrecastHoleMajor",value) + params.set_param_arch("PrecastHoleMajor", value) - def setHoleMinor(self,value): + def setHoleMinor(self, value): self.HoleMinor = value - params.set_param_arch("PrecastHoleMinor",value) + params.set_param_arch("PrecastHoleMinor", value) - def setHoleSpacing(self,value): + def setHoleSpacing(self, value): self.HoleSpacing = value - params.set_param_arch("PrecastHoleSpacing",value) + params.set_param_arch("PrecastHoleSpacing", value) - def setGrooveDepth(self,value): + def setGrooveDepth(self, value): self.GrooveDepth = value - params.set_param_arch("PrecastGrooveDepth",value) + params.set_param_arch("PrecastGrooveDepth", value) - def setGrooveHeight(self,value): + def setGrooveHeight(self, value): self.GrooveHeight = value - params.set_param_arch("PrecastGrooveHeight",value) + params.set_param_arch("PrecastGrooveHeight", value) - def setGrooveSpacing(self,value): + def setGrooveSpacing(self, value): self.GrooveSpacing = value - params.set_param_arch("PrecastGrooveSpacing",value) + params.set_param_arch("PrecastGrooveSpacing", value) - def setDownLength(self,value): + def setDownLength(self, value): self.DownLength = value - params.set_param_arch("PrecastDownLength",value) + params.set_param_arch("PrecastDownLength", value) - def setRiser(self,value): + def setRiser(self, value): self.Riser = value - params.set_param_arch("PrecastRiser",value) + params.set_param_arch("PrecastRiser", value) - def setTread(self,value): + def setTread(self, value): self.Tread = value - params.set_param_arch("PrecastTread",value) + params.set_param_arch("PrecastTread", value) def retranslateUi(self, dialog): from PySide import QtGui + self.form.setWindowTitle(translate("Arch", "Precast elements")) self.labelSlabType.setText(translate("Arch", "Slab type")) self.labelChamfer.setText(translate("Arch", "Chamfer")) @@ -1034,7 +1266,7 @@ class _PrecastTaskPanel: self.labelRiser.setText(translate("Arch", "Height of risers")) self.labelTread.setText(translate("Arch", "Depth of treads")) - def setPreset(self,preset): + def setPreset(self, preset): self.preview.hide() if preset == "Beam": self.preview.load(":/ui/ParametersBeam.svg") @@ -1269,154 +1501,217 @@ class _PrecastTaskPanel: class _DentsTaskPanel: - - '''The TaskPanel for dent creation''' + """The TaskPanel for dent creation""" def __init__(self): import FreeCADGui from PySide import QtCore, QtGui, QtSvgWidgets + self.form = QtGui.QWidget() self.grid = QtGui.QGridLayout(self.form) - self.Rotations = ["N","S","E","O"] - self.RotationAngles = [90,270,0,180] + self.Rotations = ["N", "S", "E", "O"] + self.RotationAngles = [90, 270, 0, 180] # dents list self.labelDents = QtGui.QLabel() self.listDents = QtGui.QListWidget() - self.grid.addWidget(self.labelDents,0,0,1,2) - self.grid.addWidget(self.listDents,1,0,1,2) + self.grid.addWidget(self.labelDents, 0, 0, 1, 2) + self.grid.addWidget(self.listDents, 1, 0, 1, 2) # buttons self.buttonAdd = QtGui.QPushButton() self.buttonRemove = QtGui.QPushButton() - self.grid.addWidget(self.buttonAdd,2,0,1,1) - self.grid.addWidget(self.buttonRemove,2,1,1,1) + self.grid.addWidget(self.buttonAdd, 2, 0, 1, 1) + self.grid.addWidget(self.buttonRemove, 2, 1, 1, 1) # image display self.preview = QtSvgWidgets.QSvgWidget(":/ui/ParametersDent.svg") self.preview.setMaximumWidth(200) self.preview.setMinimumHeight(120) - self.grid.addWidget(self.preview,3,0,1,2) + self.grid.addWidget(self.preview, 3, 0, 1, 2) # parameters self.labelLength = QtGui.QLabel() self.valueLength = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelLength,4,0,1,1) - self.grid.addWidget(self.valueLength,4,1,1,1) + self.grid.addWidget(self.labelLength, 4, 0, 1, 1) + self.grid.addWidget(self.valueLength, 4, 1, 1, 1) self.labelWidth = QtGui.QLabel() self.valueWidth = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelWidth,5,0,1,1) - self.grid.addWidget(self.valueWidth,5,1,1,1) + self.grid.addWidget(self.labelWidth, 5, 0, 1, 1) + self.grid.addWidget(self.valueWidth, 5, 1, 1, 1) self.labelHeight = QtGui.QLabel() self.valueHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelHeight,6,0,1,1) - self.grid.addWidget(self.valueHeight,6,1,1,1) + self.grid.addWidget(self.labelHeight, 6, 0, 1, 1) + self.grid.addWidget(self.valueHeight, 6, 1, 1, 1) self.labelSlant = QtGui.QLabel() self.valueSlant = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelSlant,7,0,1,1) - self.grid.addWidget(self.valueSlant,7,1,1,1) + self.grid.addWidget(self.labelSlant, 7, 0, 1, 1) + self.grid.addWidget(self.valueSlant, 7, 1, 1, 1) self.labelLevel = QtGui.QLabel() self.valueLevel = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelLevel,8,0,1,1) - self.grid.addWidget(self.valueLevel,8,1,1,1) + self.grid.addWidget(self.labelLevel, 8, 0, 1, 1) + self.grid.addWidget(self.valueLevel, 8, 1, 1, 1) self.labelRotation = QtGui.QLabel() self.valueRotation = QtGui.QComboBox() self.valueRotation.addItems(self.Rotations) self.valueRotation.setCurrentIndex(0) - self.grid.addWidget(self.labelRotation,9,0,1,1) - self.grid.addWidget(self.valueRotation,9,1,1,1) + self.grid.addWidget(self.labelRotation, 9, 0, 1, 1) + self.grid.addWidget(self.valueRotation, 9, 1, 1, 1) self.labelOffset = QtGui.QLabel() self.valueOffset = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.grid.addWidget(self.labelOffset,10,0,1,1) - self.grid.addWidget(self.valueOffset,10,1,1,1) + self.grid.addWidget(self.labelOffset, 10, 0, 1, 1) + self.grid.addWidget(self.valueOffset, 10, 1, 1, 1) # signals/slots - QtCore.QObject.connect(self.valueLength,QtCore.SIGNAL("valueChanged(double)"),self.setLength) - QtCore.QObject.connect(self.valueWidth,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) - QtCore.QObject.connect(self.valueHeight,QtCore.SIGNAL("valueChanged(double)"),self.setHeight) - QtCore.QObject.connect(self.valueSlant,QtCore.SIGNAL("valueChanged(double)"),self.setSlant) - QtCore.QObject.connect(self.valueLevel,QtCore.SIGNAL("valueChanged(double)"),self.setLevel) - QtCore.QObject.connect(self.valueRotation,QtCore.SIGNAL("currentIndexChanged(int)"),self.setDent) - QtCore.QObject.connect(self.valueOffset,QtCore.SIGNAL("valueChanged(double)"),self.setOffset) - QtCore.QObject.connect(self.buttonAdd,QtCore.SIGNAL("clicked()"),self.addDent) - QtCore.QObject.connect(self.buttonRemove,QtCore.SIGNAL("clicked()"),self.removeDent) - QtCore.QObject.connect(self.listDents,QtCore.SIGNAL("itemClicked(QListWidgetItem*)"),self.editDent) + QtCore.QObject.connect( + self.valueLength, QtCore.SIGNAL("valueChanged(double)"), self.setLength + ) + QtCore.QObject.connect( + self.valueWidth, QtCore.SIGNAL("valueChanged(double)"), self.setWidth + ) + QtCore.QObject.connect( + self.valueHeight, QtCore.SIGNAL("valueChanged(double)"), self.setHeight + ) + QtCore.QObject.connect( + self.valueSlant, QtCore.SIGNAL("valueChanged(double)"), self.setSlant + ) + QtCore.QObject.connect( + self.valueLevel, QtCore.SIGNAL("valueChanged(double)"), self.setLevel + ) + QtCore.QObject.connect( + self.valueRotation, QtCore.SIGNAL("currentIndexChanged(int)"), self.setDent + ) + QtCore.QObject.connect( + self.valueOffset, QtCore.SIGNAL("valueChanged(double)"), self.setOffset + ) + QtCore.QObject.connect(self.buttonAdd, QtCore.SIGNAL("clicked()"), self.addDent) + QtCore.QObject.connect(self.buttonRemove, QtCore.SIGNAL("clicked()"), self.removeDent) + QtCore.QObject.connect( + self.listDents, QtCore.SIGNAL("itemClicked(QListWidgetItem*)"), self.editDent + ) self.retranslateUi(self.form) self.form.hide() - def setLength(self,value): + def setLength(self, value): self.Length = value self.setDent() - def setWidth(self,value): + def setWidth(self, value): self.Width = value self.setDent() - def setHeight(self,value): + def setHeight(self, value): self.Height = value self.setDent() - def setSlant(self,value): + def setSlant(self, value): self.Slant = value self.setDent() - def setLevel(self,value): + def setLevel(self, value): self.Level = value self.setDent() - def setOffset(self,value): + def setOffset(self, value): self.Offset = value self.setDent() - def fillDents(self,dents): + def fillDents(self, dents): self.listDents.clear() i = 1 for d in dents: - s = "Dent "+str(i)+" :"+d + s = "Dent " + str(i) + " :" + d self.listDents.addItem(s) i += 1 - def setDent(self,i=0): + def setDent(self, i=0): if self.listDents.currentItem(): - num = str(self.listDents.currentRow()+1) + num = str(self.listDents.currentRow() + 1) rot = self.RotationAngles[self.valueRotation.currentIndex()] - s = "Dent "+num+" :"+str(self.Length)+";"+str(self.Width)+";"+str(self.Height)+";"+str(self.Slant)+";"+str(self.Level)+";"+str(rot)+";"+str(self.Offset) + s = ( + "Dent " + + num + + " :" + + str(self.Length) + + ";" + + str(self.Width) + + ";" + + str(self.Height) + + ";" + + str(self.Slant) + + ";" + + str(self.Level) + + ";" + + str(rot) + + ";" + + str(self.Offset) + ) self.listDents.currentItem().setText(s) def addDent(self): - num = str(self.listDents.count()+1) + num = str(self.listDents.count() + 1) rot = self.RotationAngles[self.valueRotation.currentIndex()] - s = "Dent "+num+" :"+str(self.Length)+";"+str(self.Width)+";"+str(self.Height)+";"+str(self.Slant)+";"+str(self.Level)+";"+str(rot)+";"+str(self.Offset) + s = ( + "Dent " + + num + + " :" + + str(self.Length) + + ";" + + str(self.Width) + + ";" + + str(self.Height) + + ";" + + str(self.Slant) + + ";" + + str(self.Level) + + ";" + + str(rot) + + ";" + + str(self.Offset) + ) self.listDents.addItem(s) - self.listDents.setCurrentRow(self.listDents.count()-1) + self.listDents.setCurrentRow(self.listDents.count() - 1) self.editDent() def removeDent(self): if self.listDents.currentItem(): self.listDents.takeItem(self.listDents.currentRow()) - def editDent(self,item=None): + def editDent(self, item=None): if self.listDents.currentItem(): s = self.listDents.currentItem().text() s = s.split(":")[1] s = s.split(";") - self.valueLength.setText(FreeCAD.Units.Quantity(float(s[0]),FreeCAD.Units.Length).UserString) - self.valueWidth.setText(FreeCAD.Units.Quantity(float(s[1]),FreeCAD.Units.Length).UserString) - self.valueHeight.setText(FreeCAD.Units.Quantity(float(s[2]),FreeCAD.Units.Length).UserString) - self.valueSlant.setText(FreeCAD.Units.Quantity(float(s[3]),FreeCAD.Units.Length).UserString) - self.valueLevel.setText(FreeCAD.Units.Quantity(float(s[4]),FreeCAD.Units.Length).UserString) + self.valueLength.setText( + FreeCAD.Units.Quantity(float(s[0]), FreeCAD.Units.Length).UserString + ) + self.valueWidth.setText( + FreeCAD.Units.Quantity(float(s[1]), FreeCAD.Units.Length).UserString + ) + self.valueHeight.setText( + FreeCAD.Units.Quantity(float(s[2]), FreeCAD.Units.Length).UserString + ) + self.valueSlant.setText( + FreeCAD.Units.Quantity(float(s[3]), FreeCAD.Units.Length).UserString + ) + self.valueLevel.setText( + FreeCAD.Units.Quantity(float(s[4]), FreeCAD.Units.Length).UserString + ) self.valueRotation.setCurrentIndex(self.RotationAngles.index(int(s[5]))) - self.valueOffset.setText(FreeCAD.Units.Quantity(float(s[6]),FreeCAD.Units.Length).UserString) + self.valueOffset.setText( + FreeCAD.Units.Quantity(float(s[6]), FreeCAD.Units.Length).UserString + ) def retranslateUi(self, dialog): from PySide import QtGui + self.form.setWindowTitle(translate("Arch", "Precast options")) self.labelDents.setText(translate("Arch", "Dents list")) self.buttonAdd.setText(translate("Arch", "Add dent")) @@ -1437,12 +1732,35 @@ class _DentsTaskPanel: return l -def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0,dentlength=0,dentwidth=0,dentheight=0,dents=[],base=0,holenumber=0,holemajor=0,holeminor=0,holespacing=0,groovenumber=0,groovedepth=0,grooveheight=0,groovespacing=0,risernumber=0,downlength=0,riser=0,tread=0): - +def makePrecast( + precasttype=None, + length=0, + width=0, + height=0, + slabtype="", + chamfer=0, + dentlength=0, + dentwidth=0, + dentheight=0, + dents=[], + base=0, + holenumber=0, + holemajor=0, + holeminor=0, + holespacing=0, + groovenumber=0, + groovedepth=0, + grooveheight=0, + groovespacing=0, + risernumber=0, + downlength=0, + riser=0, + tread=0, +): "Creates one of the precast objects in the current document" if precasttype == "Beam": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Beam") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Beam") _PrecastBeam(obj) obj.Length = length obj.Width = width @@ -1452,7 +1770,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.DentLength = dentlength obj.DentHeight = dentheight elif precasttype == "Pillar": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Pillar") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Pillar") _PrecastPillar(obj) obj.Length = length obj.Width = width @@ -1464,7 +1782,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.GrooveHeight = grooveheight obj.GrooveSpacing = groovespacing elif precasttype == "Panel": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Panel") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Panel") _PrecastPanel(obj) obj.Length = length obj.Width = width @@ -1473,7 +1791,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.DentWidth = dentwidth obj.DentHeight = dentheight elif precasttype == "Slab": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Slab") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Slab") _PrecastSlab(obj) obj.SlabType = slabtype obj.Length = length @@ -1485,7 +1803,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.HoleMinor = holeminor obj.HoleSpacing = holespacing elif precasttype == "I-Beam": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Beam") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Beam") _PrecastIbeam(obj) obj.Length = length obj.Width = width @@ -1493,7 +1811,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.Chamfer = chamfer obj.BeamBase = base elif precasttype == "Stairs": - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Stairs") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Stairs") _PrecastStairs(obj) obj.Length = length obj.Width = width @@ -1503,7 +1821,7 @@ def makePrecast(precasttype=None,length=0,width=0,height=0,slabtype="",chamfer=0 obj.Riser = riser obj.Tread = tread else: - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Precast") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Precast") _Precast(obj) if FreeCAD.GuiUp: _ViewProviderPrecast(obj.ViewObject) diff --git a/src/Mod/BIM/ArchProfile.py b/src/Mod/BIM/ArchProfile.py index bcd8a79d3a..216ad3bc55 100644 --- a/src/Mod/BIM/ArchProfile.py +++ b/src/Mod/BIM/ArchProfile.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Profile" +__title__ = "FreeCAD Profile" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchProfile # \ingroup ARCH @@ -49,25 +49,29 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond # Presets in the form: Class, Name, Profile type, [profile data] # Search for profiles.csv in data/Mod/Arch/Presets and in the same folder as this file # and in the user path -profilefiles = [os.path.join(FreeCAD.getResourceDir(),"Mod","BIM","Presets","profiles.csv"), - os.path.join(os.path.dirname(__file__),"Presets","profiles.csv"), - os.path.join(FreeCAD.getUserAppDataDir(),"BIM","profiles.csv")] +profilefiles = [ + os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "profiles.csv"), + os.path.join(os.path.dirname(__file__), "Presets", "profiles.csv"), + os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "profiles.csv"), +] def readPresets(): - Presets=[] - bid = 1 #Unique index + Presets = [] + bid = 1 # Unique index for profilefile in profilefiles: if os.path.exists(profilefile): try: @@ -77,40 +81,37 @@ def readPresets(): if (not row) or row[0].startswith("#"): continue try: - r=[bid, row[0], row[1], row[2]] - for i in range(3,len(row)): - r=r+[float(row[i])] + r = [bid, row[0], row[1], row[2]] + for i in range(3, len(row)): + r = r + [float(row[i])] if not r in Presets: Presets.append(r) - bid=bid+1 + bid = bid + 1 except ValueError: - print("Skipping bad line: "+str(row)) + print("Skipping bad line: " + str(row)) except IOError: - print("Could not open ",profilefile) + print("Could not open ", profilefile) return Presets - class _Profile(Draft._DraftObject): + """Superclass for Profile classes""" - '''Superclass for Profile classes''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.Profile = profile - Draft._DraftObject.__init__(self,obj,"Profile") + Draft._DraftObject.__init__(self, obj, "Profile") def dumps(self): - if hasattr(self,"Profile"): + if hasattr(self, "Profile"): return self.Profile - def loads(self,state): - if isinstance(state,list): + def loads(self, state): + if isinstance(state, list): self.Profile = state self.Type = "Profile" def cleanProperties(self, obj): - - '''Remove all Profile properties''' + """Remove all Profile properties""" for prop in obj.PropertiesList: if obj.getGroupOfProperty(prop) == "Draft": @@ -119,114 +120,200 @@ class _Profile(Draft._DraftObject): class _ProfileC(_Profile): + """A parametric circular tubeprofile. Profile data: [Outside diameter, Wall thickness]""" - '''A parametric circular tubeprofile. Profile data: [Outside diameter, Wall thickness]''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","OutDiameter","Draft",QT_TRANSLATE_NOOP("App::Property","Outside Diameter"), locked=True).OutDiameter = profile[4] - obj.addProperty("App::PropertyLength","Thickness","Draft",QT_TRANSLATE_NOOP("App::Property","Wall thickness"), locked=True).Thickness = profile[5] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "OutDiameter", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Outside Diameter"), + locked=True, + ).OutDiameter = profile[4] + obj.addProperty( + "App::PropertyLength", + "Thickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Wall thickness"), + locked=True, + ).Thickness = profile[5] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - c1=Part.Circle() - c1.Radius=obj.OutDiameter.Value/2 - c2=Part.Circle() - c2.Radius=obj.OutDiameter.Value/2-obj.Thickness.Value - cs1=c1.toShape() - cs2=c2.toShape() - p=Part.makeRuledSurface(cs2,cs1) + c1 = Part.Circle() + c1.Radius = obj.OutDiameter.Value / 2 + c2 = Part.Circle() + c2.Radius = obj.OutDiameter.Value / 2 - obj.Thickness.Value + cs1 = c1.toShape() + cs2 = c2.toShape() + p = Part.makeRuledSurface(cs2, cs1) p.reverse() obj.Shape = p obj.Placement = pl class _ProfileH(_Profile): + """A parametric H or I beam profile. Profile data: [width, height, web thickness, flange thickness] (see http://en.wikipedia.org/wiki/I-beam for reference)""" - '''A parametric H or I beam profile. Profile data: [width, height, web thickness, flange thickness] (see http://en.wikipedia.org/wiki/I-beam for reference)''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - obj.addProperty("App::PropertyLength","WebThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the web"), locked=True).WebThickness = profile[6] - obj.addProperty("App::PropertyLength","FlangeThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the flanges"), locked=True).FlangeThickness = profile[7] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + obj.addProperty( + "App::PropertyLength", + "WebThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the web"), + locked=True, + ).WebThickness = profile[6] + obj.addProperty( + "App::PropertyLength", + "FlangeThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the flanges"), + locked=True, + ).FlangeThickness = profile[7] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(-obj.Width.Value/2,-obj.Height.Value/2,0) - p2 = Vector(obj.Width.Value/2,-obj.Height.Value/2,0) - p3 = Vector(obj.Width.Value/2,(-obj.Height.Value/2)+obj.FlangeThickness.Value,0) - p4 = Vector(obj.WebThickness.Value/2,(-obj.Height.Value/2)+obj.FlangeThickness.Value,0) - p5 = Vector(obj.WebThickness.Value/2,obj.Height.Value/2-obj.FlangeThickness.Value,0) - p6 = Vector(obj.Width.Value/2,obj.Height.Value/2-obj.FlangeThickness.Value,0) - p7 = Vector(obj.Width.Value/2,obj.Height.Value/2,0) - p8 = Vector(-obj.Width.Value/2,obj.Height.Value/2,0) - p9 = Vector(-obj.Width.Value/2,obj.Height.Value/2-obj.FlangeThickness.Value,0) - p10 = Vector(-obj.WebThickness.Value/2,obj.Height.Value/2-obj.FlangeThickness.Value,0) - p11 = Vector(-obj.WebThickness.Value/2,(-obj.Height.Value/2)+obj.FlangeThickness.Value,0) - p12 = Vector(-obj.Width.Value/2,(-obj.Height.Value/2)+obj.FlangeThickness.Value,0) - p = Part.makePolygon([p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p1]) + p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p3 = Vector(obj.Width.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0) + p4 = Vector( + obj.WebThickness.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0 + ) + p5 = Vector(obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p6 = Vector(obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p7 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) + p8 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + p9 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p10 = Vector( + -obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0 + ) + p11 = Vector( + -obj.WebThickness.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0 + ) + p12 = Vector(-obj.Width.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0) + p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p1]) p = Part.Face(p) - #p.reverse() + # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileR(_Profile): + """A parametric rectangular beam profile based on [Width, Height]""" - '''A parametric rectangular beam profile based on [Width, Height]''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(-obj.Width.Value/2,-obj.Height.Value/2,0) - p2 = Vector(obj.Width.Value/2,-obj.Height.Value/2,0) - p3 = Vector(obj.Width.Value/2,obj.Height.Value/2,0) - p4 = Vector(-obj.Width.Value/2,obj.Height.Value/2,0) - p = Part.makePolygon([p1,p2,p3,p4,p1]) + p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) + p4 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + p = Part.makePolygon([p1, p2, p3, p4, p1]) p = Part.Face(p) - #p.reverse() + # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileRH(_Profile): + """A parametric Rectangular hollow beam profile. Profile data: [width, height, thickness]""" - '''A parametric Rectangular hollow beam profile. Profile data: [width, height, thickness]''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - obj.addProperty("App::PropertyLength","Thickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the sides"), locked=True).Thickness = profile[6] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + obj.addProperty( + "App::PropertyLength", + "Thickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the sides"), + locked=True, + ).Thickness = profile[6] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(-obj.Width.Value/2,-obj.Height.Value/2,0) - p2 = Vector(obj.Width.Value/2,-obj.Height.Value/2,0) - p3 = Vector(obj.Width.Value/2,obj.Height.Value/2,0) - p4 = Vector(-obj.Width.Value/2,obj.Height.Value/2,0) - q1 = Vector(-obj.Width.Value/2+obj.Thickness.Value,-obj.Height.Value/2+obj.Thickness.Value,0) - q2 = Vector(obj.Width.Value/2-obj.Thickness.Value,-obj.Height.Value/2+obj.Thickness.Value,0) - q3 = Vector(obj.Width.Value/2-obj.Thickness.Value,obj.Height.Value/2-obj.Thickness.Value,0) - q4 = Vector(-obj.Width.Value/2+obj.Thickness.Value,obj.Height.Value/2-obj.Thickness.Value,0) - p = Part.makePolygon([p1,p2,p3,p4,p1]) - q = Part.makePolygon([q1,q2,q3,q4,q1]) - #r = Part.Face([p,q]) - #r.reverse() + p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) + p4 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + q1 = Vector( + -obj.Width.Value / 2 + obj.Thickness.Value, + -obj.Height.Value / 2 + obj.Thickness.Value, + 0, + ) + q2 = Vector( + obj.Width.Value / 2 - obj.Thickness.Value, + -obj.Height.Value / 2 + obj.Thickness.Value, + 0, + ) + q3 = Vector( + obj.Width.Value / 2 - obj.Thickness.Value, obj.Height.Value / 2 - obj.Thickness.Value, 0 + ) + q4 = Vector( + -obj.Width.Value / 2 + obj.Thickness.Value, + obj.Height.Value / 2 - obj.Thickness.Value, + 0, + ) + p = Part.makePolygon([p1, p2, p3, p4, p1]) + q = Part.makePolygon([q1, q2, q3, q4, q1]) + # r = Part.Face([p,q]) + # r.reverse() p = Part.Face(p) q = Part.Face(q) r = p.cut(q) @@ -235,136 +322,264 @@ class _ProfileRH(_Profile): class _ProfileU(_Profile): + """A parametric U profile. Profile data: [width, height, web thickness, flange thickness] (see https://en.wikipedia.org/wiki/Structural_channel for reference)""" - '''A parametric U profile. Profile data: [width, height, web thickness, flange thickness] (see https://en.wikipedia.org/wiki/Structural_channel for reference)''' - - def __init__(self,obj, profile): + def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - obj.addProperty("App::PropertyLength","WebThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the webs"), locked=True).WebThickness = profile[6] - obj.addProperty("App::PropertyLength","FlangeThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the flange"), locked=True).FlangeThickness = profile[7] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + obj.addProperty( + "App::PropertyLength", + "WebThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the webs"), + locked=True, + ).WebThickness = profile[6] + obj.addProperty( + "App::PropertyLength", + "FlangeThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the flange"), + locked=True, + ).FlangeThickness = profile[7] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(-obj.Width.Value/2,-obj.Height.Value/2,0) - p2 = Vector(obj.Width.Value/2,-obj.Height.Value/2,0) - p3 = Vector(obj.Width.Value/2,obj.Height.Value/2,0) - p4 = Vector(obj.Width.Value/2-obj.FlangeThickness.Value,obj.Height.Value/2,0) - p5 = Vector(obj.Width.Value/2-obj.FlangeThickness.Value,obj.WebThickness.Value-obj.Height.Value/2,0) - p6 = Vector(-obj.Width.Value/2+obj.FlangeThickness.Value,obj.WebThickness.Value-obj.Height.Value/2,0) - p7 = Vector(-obj.Width.Value/2+obj.FlangeThickness.Value,obj.Height.Value/2,0) - p8 = Vector(-obj.Width.Value/2,obj.Height.Value/2,0) - p = Part.makePolygon([p1,p2,p3,p4,p5,p6,p7,p8,p1]) + p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) + p4 = Vector(obj.Width.Value / 2 - obj.FlangeThickness.Value, obj.Height.Value / 2, 0) + p5 = Vector( + obj.Width.Value / 2 - obj.FlangeThickness.Value, + obj.WebThickness.Value - obj.Height.Value / 2, + 0, + ) + p6 = Vector( + -obj.Width.Value / 2 + obj.FlangeThickness.Value, + obj.WebThickness.Value - obj.Height.Value / 2, + 0, + ) + p7 = Vector(-obj.Width.Value / 2 + obj.FlangeThickness.Value, obj.Height.Value / 2, 0) + p8 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p1]) p = Part.Face(p) - #p.reverse() + # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileL(_Profile): - - '''A parametric L profile. Profile data: [width, height, thickness]''' + """A parametric L profile. Profile data: [width, height, thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - obj.addProperty("App::PropertyLength","Thickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the legs"), locked=True).Thickness = profile[6] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + obj.addProperty( + "App::PropertyLength", + "Thickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the legs"), + locked=True, + ).Thickness = profile[6] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(-obj.Width.Value/2, obj.Height.Value/2, 0) - p2 = Vector(-obj.Width.Value/2, -obj.Height.Value/2, 0) - p3 = Vector(obj.Width.Value/2, -obj.Height.Value/2, 0) - p4 = Vector(obj.Width.Value/2, -obj.Height.Value/2+obj.Thickness.Value, 0) - p5 = Vector(-obj.Width.Value/2+obj.Thickness.Value, -obj.Height.Value/2+obj.Thickness.Value, 0) - p6 = Vector(-obj.Width.Value/2+obj.Thickness.Value, obj.Height.Value/2, 0) - p = Part.makePolygon([p1,p2,p3,p4,p5,p6,p1]) + p1 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + p2 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p3 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) + p4 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2 + obj.Thickness.Value, 0) + p5 = Vector( + -obj.Width.Value / 2 + obj.Thickness.Value, + -obj.Height.Value / 2 + obj.Thickness.Value, + 0, + ) + p6 = Vector(-obj.Width.Value / 2 + obj.Thickness.Value, obj.Height.Value / 2, 0) + p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p1]) p = Part.Face(p) - #p.reverse() + # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileT(_Profile): - - '''A parametric T profile. Profile data: [width, height, web thickness, flange thickness]''' + """A parametric T profile. Profile data: [width, height, web thickness, flange thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Width","Draft",QT_TRANSLATE_NOOP("App::Property","Width of the beam"), locked=True).Width = profile[4] - obj.addProperty("App::PropertyLength","Height","Draft",QT_TRANSLATE_NOOP("App::Property","Height of the beam"), locked=True).Height = profile[5] - obj.addProperty("App::PropertyLength","WebThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the web"), locked=True).WebThickness = profile[6] - obj.addProperty("App::PropertyLength","FlangeThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the flanges"), locked=True).FlangeThickness = profile[7] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Width", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), + locked=True, + ).Width = profile[4] + obj.addProperty( + "App::PropertyLength", + "Height", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), + locked=True, + ).Height = profile[5] + obj.addProperty( + "App::PropertyLength", + "WebThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the web"), + locked=True, + ).WebThickness = profile[6] + obj.addProperty( + "App::PropertyLength", + "FlangeThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the flanges"), + locked=True, + ).FlangeThickness = profile[7] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part + pl = obj.Placement - p1 = Vector(obj.WebThickness.Value/2, -obj.Height.Value/2, 0) - p2 = Vector(obj.WebThickness.Value/2, obj.Height.Value/2-obj.FlangeThickness.Value, 0) - p3 = Vector(obj.Width.Value/2, obj.Height.Value/2-obj.FlangeThickness.Value, 0) - p4 = Vector(obj.Width.Value/2, obj.Height.Value/2, 0) - p5 = Vector(-obj.Width.Value/2, obj.Height.Value/2, 0) - p6 = Vector(-obj.Width.Value/2, obj.Height.Value/2-obj.FlangeThickness.Value, 0) - p7 = Vector(-obj.WebThickness.Value/2, obj.Height.Value/2-obj.FlangeThickness.Value, 0) - p8 = Vector(-obj.WebThickness.Value/2, -obj.Height.Value/2, 0) - p = Part.makePolygon([p1,p2,p3,p4,p5,p6,p7,p8,p1]) + p1 = Vector(obj.WebThickness.Value / 2, -obj.Height.Value / 2, 0) + p2 = Vector(obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p4 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) + p5 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) + p6 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) + p7 = Vector( + -obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0 + ) + p8 = Vector(-obj.WebThickness.Value / 2, -obj.Height.Value / 2, 0) + p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p1]) p = Part.Face(p) - #p.reverse() + # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileTSLOT(_Profile): - - '''T slot profile common made of aluminum''' + """T slot profile common made of aluminum""" def __init__(self, obj, profile): self.cleanProperties(obj) - obj.addProperty("App::PropertyLength","Size","Draft",QT_TRANSLATE_NOOP("App::Property","Overall size"), locked=True).Size = profile[4] - obj.addProperty("App::PropertyLength","SlotSize","Draft",QT_TRANSLATE_NOOP("App::Property","Slot size"), locked=True).SlotSize = profile[5] - obj.addProperty("App::PropertyLength","WallThickness","Draft",QT_TRANSLATE_NOOP("App::Property","Thickness of the wall"), locked=True).WallThickness = profile[6] - obj.addProperty("App::PropertyLength","TnutSlotWidth","Draft",QT_TRANSLATE_NOOP("App::Property","T-nut slot width"), locked=True).TnutSlotWidth = profile[7] - obj.addProperty("App::PropertyLength","TnutSlotDepth","Draft",QT_TRANSLATE_NOOP("App::Property","T-nut slot depth"), locked=True).TnutSlotDepth = profile[8] - obj.addProperty("App::PropertyLength","CoreSize","Draft",QT_TRANSLATE_NOOP("App::Property","Internal core size"), locked=True).CoreSize = profile[9] - obj.addProperty("App::PropertyLength","HoleDiameter","Draft",QT_TRANSLATE_NOOP("App::Property","Internal hole diameter"), locked=True).HoleDiameter = profile[10] - obj.addProperty("App::PropertyLength","FilletRadius","Draft",QT_TRANSLATE_NOOP("App::Property","Corner fillet radius"), locked=True).FilletRadius = profile[11] - _Profile.__init__(self,obj,profile) + obj.addProperty( + "App::PropertyLength", + "Size", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Overall size"), + locked=True, + ).Size = profile[4] + obj.addProperty( + "App::PropertyLength", + "SlotSize", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Slot size"), + locked=True, + ).SlotSize = profile[5] + obj.addProperty( + "App::PropertyLength", + "WallThickness", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Thickness of the wall"), + locked=True, + ).WallThickness = profile[6] + obj.addProperty( + "App::PropertyLength", + "TnutSlotWidth", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "T-nut slot width"), + locked=True, + ).TnutSlotWidth = profile[7] + obj.addProperty( + "App::PropertyLength", + "TnutSlotDepth", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "T-nut slot depth"), + locked=True, + ).TnutSlotDepth = profile[8] + obj.addProperty( + "App::PropertyLength", + "CoreSize", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Internal core size"), + locked=True, + ).CoreSize = profile[9] + obj.addProperty( + "App::PropertyLength", + "HoleDiameter", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Internal hole diameter"), + locked=True, + ).HoleDiameter = profile[10] + obj.addProperty( + "App::PropertyLength", + "FilletRadius", + "Draft", + QT_TRANSLATE_NOOP("App::Property", "Corner fillet radius"), + locked=True, + ).FilletRadius = profile[11] + _Profile.__init__(self, obj, profile) - def execute(self,obj): + def execute(self, obj): import Part import math + # import DraftGeomUtils pl = obj.Placement - nut_depth = obj.CoreSize/2-(obj.WallThickness/math.sqrt(2)) - core_dim = obj.CoreSize/2 + nut_depth = obj.CoreSize / 2 - (obj.WallThickness / math.sqrt(2)) + core_dim = obj.CoreSize / 2 wall_dim = obj.WallThickness - slot_dim = obj.SlotSize/2 - nut_width_dim = obj.TnutSlotWidth/2 + slot_dim = obj.SlotSize / 2 + nut_width_dim = obj.TnutSlotWidth / 2 templist = list() templist.append(Vector(nut_depth, core_dim, 0.0)) - templist.append(Vector(nut_width_dim, nut_width_dim+(wall_dim/math.sqrt(2)), 0.0)) - templist.append(Vector(nut_width_dim, obj.Size/2-wall_dim, 0.0)) - templist.append(Vector(slot_dim, obj.Size/2-wall_dim, 0.0)) - templist.append(Vector(slot_dim, obj.Size/2, 0.0)) - templist.append(Vector(obj.Size/2, obj.Size/2, 0.0)) - #TODO: + templist.append(Vector(nut_width_dim, nut_width_dim + (wall_dim / math.sqrt(2)), 0.0)) + templist.append(Vector(nut_width_dim, obj.Size / 2 - wall_dim, 0.0)) + templist.append(Vector(slot_dim, obj.Size / 2 - wall_dim, 0.0)) + templist.append(Vector(slot_dim, obj.Size / 2, 0.0)) + templist.append(Vector(obj.Size / 2, obj.Size / 2, 0.0)) + # TODO: # add fillet four profile corners # sub_poly1 = Part.makePolygon(templist) # rd1=Vector(obj.Size/2-(obj.FilletRadius),obj.Size,0) # rd2=Vector(obj.Size/2-(obj.FilletRadius-obj.FilletRadius*math.sin(math.pi/4)),obj.Size/2-(obj.FilletRadius-obj.FilletRadius*math.sin(math.pi/4)),0) # rd3=Vector(obj.Size,obj.Size/2-(obj.FilletRadius),0) # templist.append(Part.Arc(rd1,rd2,rd3)) - templist.append(Vector(obj.Size/2, slot_dim, 0.0)) - templist.append(Vector(obj.Size/2-wall_dim, slot_dim, 0.0)) - templist.append(Vector(obj.Size/2-wall_dim, nut_width_dim, 0.0)) - templist.append(Vector(nut_width_dim+(wall_dim/math.sqrt(2)), nut_width_dim, 0.0)) + templist.append(Vector(obj.Size / 2, slot_dim, 0.0)) + templist.append(Vector(obj.Size / 2 - wall_dim, slot_dim, 0.0)) + templist.append(Vector(obj.Size / 2 - wall_dim, nut_width_dim, 0.0)) + templist.append(Vector(nut_width_dim + (wall_dim / math.sqrt(2)), nut_width_dim, 0.0)) templist.append(Vector(core_dim, nut_depth, 0.0)) # sub_poly2 = Part.makePolygon(templist3) # l1=Part.LineSegment(Vector(obj.SlotSize/2, obj.Size/2, 0.0),Vector(obj.Size/2, obj.Size/2, 0.0)) @@ -375,34 +590,34 @@ class _ProfileTSLOT(_Profile): # p=Part.Face(Shape.Edges) templist2 = list() for vec in reversed(templist): - templist2.append(Vector(vec.x,vec.y*-1,vec.z)) + templist2.append(Vector(vec.x, vec.y * -1, vec.z)) templist = templist + templist2 templist2.clear() for vec in reversed(templist): - templist2.append(Vector(vec.x*-1,vec.y,vec.z)) + templist2.append(Vector(vec.x * -1, vec.y, vec.z)) templist = templist + templist2 templist.append(templist[0]) poly = Part.makePolygon(templist) pf = Part.Face(poly) - hole = Part.makeCircle(obj.HoleDiameter/2, FreeCAD.Vector(0, 0, 0)) - cf=Part.Face(Part.Wire(hole)) - p=pf.cut(cf) - #p.reverse() + hole = Part.makeCircle(obj.HoleDiameter / 2, FreeCAD.Vector(0, 0, 0)) + cf = Part.Face(Part.Wire(hole)) + p = pf.cut(cf) + # p.reverse() obj.Shape = p obj.Placement = pl class ViewProviderProfile(Draft._ViewProviderDraft): + """General view provider for Profile classes""" - '''General view provider for Profile classes''' + def __init__(self, vobj): - def __init__(self,vobj): - - Draft._ViewProviderDraft.__init__(self,vobj) + Draft._ViewProviderDraft.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Profile.svg" def setEdit(self, vobj, mode): @@ -422,30 +637,29 @@ class ViewProviderProfile(Draft._ViewProviderDraft): class ProfileTaskPanel: + """The editmode TaskPanel for Profile objects""" - '''The editmode TaskPanel for Profile objects''' - - def __init__(self,obj): + def __init__(self, obj): self.obj = obj self.Profile = None - if hasattr(obj.Proxy,"Profile"): + if hasattr(obj.Proxy, "Profile"): self.Profile = obj.Proxy.Profile - if isinstance(self.obj.Proxy,_ProfileC): + if isinstance(self.obj.Proxy, _ProfileC): self.type = "C" - elif isinstance(self.obj.Proxy,_ProfileH): + elif isinstance(self.obj.Proxy, _ProfileH): self.type = "H" - elif isinstance(self.obj.Proxy,_ProfileR): + elif isinstance(self.obj.Proxy, _ProfileR): self.type = "R" - elif isinstance(self.obj.Proxy,_ProfileRH): + elif isinstance(self.obj.Proxy, _ProfileRH): self.type = "RH" - elif isinstance(self.obj.Proxy,_ProfileU): + elif isinstance(self.obj.Proxy, _ProfileU): self.type = "U" - elif isinstance(self.obj.Proxy,_ProfileL): + elif isinstance(self.obj.Proxy, _ProfileL): self.type = "L" - elif isinstance(self.obj.Proxy,_ProfileT): + elif isinstance(self.obj.Proxy, _ProfileT): self.type = "T" - elif isinstance(self.obj.Proxy,_ProfileTSLOT): + elif isinstance(self.obj.Proxy, _ProfileTSLOT): self.type = "TSLOT" else: self.type = "Building Element Proxy" @@ -455,8 +669,12 @@ class ProfileTaskPanel: layout.addWidget(self.comboCategory) self.comboProfile = QtGui.QComboBox(self.form) layout.addWidget(self.comboProfile) - QtCore.QObject.connect(self.comboCategory, QtCore.SIGNAL("currentTextChanged(QString)"), self.changeCategory) - QtCore.QObject.connect(self.comboProfile, QtCore.SIGNAL("currentIndexChanged(int)"), self.changeProfile) + QtCore.QObject.connect( + self.comboCategory, QtCore.SIGNAL("currentTextChanged(QString)"), self.changeCategory + ) + QtCore.QObject.connect( + self.comboProfile, QtCore.SIGNAL("currentIndexChanged(int)"), self.changeProfile + ) # Read preset profiles and add relevant ones self.categories = [] self.presets = readPresets() @@ -475,35 +693,37 @@ class ProfileTaskPanel: break if not self.Profile: # try to find by size - if hasattr(self.obj,"Width") and hasattr(self.obj,"Height"): + if hasattr(self.obj, "Width") and hasattr(self.obj, "Height"): for pre in self.presets: if self.type == pre[1]: - if abs(self.obj.Width - self.Profile[4]) < 0.1 and \ - abs(self.obj.Height - self.Profile[5]) < 0.1: + if ( + abs(self.obj.Width - self.Profile[4]) < 0.1 + and abs(self.obj.Height - self.Profile[5]) < 0.1 + ): self.Profile = pre break if self.Profile: - origprofile = list(self.Profile) # the operation below will change self.profile - self.comboCategory.setCurrentIndex(1+self.categories.index(origprofile[1])) + origprofile = list(self.Profile) # the operation below will change self.profile + self.comboCategory.setCurrentIndex(1 + self.categories.index(origprofile[1])) self.changeCategory(origprofile[1]) self.comboProfile.setCurrentIndex(self.currentpresets.index(origprofile)) self.retranslateUi(self.form) - def changeCategory(self,text): + def changeCategory(self, text): self.comboProfile.clear() self.currentpresets = [] for pre in self.presets: if pre[1] == text: self.currentpresets.append(pre) - f = FreeCAD.Units.Quantity(pre[4],FreeCAD.Units.Length).getUserPreferred() - d = params.get_param("Decimals",path="Units") - s1 = str(round(pre[4]/f[1],d)) - s2 = str(round(pre[5]/f[1],d)) + f = FreeCAD.Units.Quantity(pre[4], FreeCAD.Units.Length).getUserPreferred() + d = params.get_param("Decimals", path="Units") + s1 = str(round(pre[4] / f[1], d)) + s2 = str(round(pre[5] / f[1], d)) s3 = str(f[2]) - self.comboProfile.addItem(pre[2]+" ("+s1+"x"+s2+s3+")") + self.comboProfile.addItem(pre[2] + " (" + s1 + "x" + s2 + s3 + ")") - def changeProfile(self,idx): + def changeProfile(self, idx): self.Profile = self.currentpresets[idx] @@ -511,21 +731,21 @@ class ProfileTaskPanel: self.obj.Label = self.Profile[2] + "_" if self.Profile: - if self.Profile[3]=="C": + if self.Profile[3] == "C": _ProfileC(self.obj, self.Profile) - elif self.Profile[3]=="H": + elif self.Profile[3] == "H": _ProfileH(self.obj, self.Profile) - elif self.Profile[3]=="R": + elif self.Profile[3] == "R": _ProfileR(self.obj, self.Profile) - elif self.Profile[3]=="RH": + elif self.Profile[3] == "RH": _ProfileRH(self.obj, self.Profile) - elif self.Profile[3]=="U": + elif self.Profile[3] == "U": _ProfileU(self.obj, self.Profile) - elif self.Profile[3]=="L": + elif self.Profile[3] == "L": _ProfileL(self.obj, self.Profile) - elif self.Profile[3]=="T": + elif self.Profile[3] == "T": _ProfileT(self.obj, self.Profile) - elif self.Profile[3]=="TSLOT": + elif self.Profile[3] == "TSLOT": _ProfileTSLOT(self.obj, self.Profile) else: print("Profile not supported") @@ -541,4 +761,6 @@ class ProfileTaskPanel: def retranslateUi(self, TaskPanel): - self.form.setWindowTitle(self.type+" "+QtGui.QApplication.translate("Arch", "Profile", None)) + self.form.setWindowTitle( + self.type + " " + QtGui.QApplication.translate("Arch", "Profile", None) + ) diff --git a/src/Mod/BIM/ArchProject.py b/src/Mod/BIM/ArchProject.py index 6d16ad1c00..3e30b4bdd6 100644 --- a/src/Mod/BIM/ArchProject.py +++ b/src/Mod/BIM/ArchProject.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Project" +__title__ = "FreeCAD Project" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchProject # \ingroup ARCH @@ -46,9 +46,11 @@ if FreeCAD.GuiUp: import FreeCADGui from draftutils.translate import translate else: - def translate(ctxt,txt): + + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt @@ -79,7 +81,7 @@ class _Project(ArchIFC.IfcContext): ArchIFC.IfcContext.setProperties(self, obj) pl = obj.PropertiesList - if not hasattr(obj,"Group"): + if not hasattr(obj, "Group"): obj.addExtension("App::GroupExtensionPython") def onDocumentRestored(self, obj): @@ -90,12 +92,11 @@ class _Project(ArchIFC.IfcContext): return None - def loads(self,state): + def loads(self, state): self.Type = "Project" - def addObject(self,obj,child): - + def addObject(self, obj, child): "Adds an object to the group of this BuildingPart" if not child in obj.Group: @@ -103,6 +104,7 @@ class _Project(ArchIFC.IfcContext): g.append(child) obj.Group = g + class _ViewProviderProject(ArchIFCView.IfcContextView): """A View Provider for the project object. @@ -112,7 +114,7 @@ class _ViewProviderProject(ArchIFCView.IfcContextView): The view provider to turn into a project view provider. """ - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self vobj.addExtension("Gui::ViewProviderGroupExtensionPython") @@ -126,9 +128,10 @@ class _ViewProviderProject(ArchIFCView.IfcContextView): """ import Arch_rc + return ":/icons/Arch_Project_Tree.svg" - def removeDisplaymodeChildNodes(self,vobj): + def removeDisplaymodeChildNodes(self, vobj): """Remove all child nodes from the 4 default display modes. This avoids 'ghosts' of the objects in the Group property. @@ -142,11 +145,15 @@ class _ViewProviderProject(ArchIFCView.IfcContextView): if not hasattr(self, "displaymodes_cleaned"): if vobj.RootNode.getNumChildren(): - main_switch = gui_utils.find_coin_node(vobj.RootNode, coin.SoSwitch) # The display mode switch. - if main_switch is not None and main_switch.getNumChildren() == 4: # Check if all display modes are available. + main_switch = gui_utils.find_coin_node( + vobj.RootNode, coin.SoSwitch + ) # The display mode switch. + if ( + main_switch is not None and main_switch.getNumChildren() == 4 + ): # Check if all display modes are available. for node in tuple(main_switch.getChildren()): node.removeAllChildren() self.displaymodes_cleaned = True - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): self.removeDisplaymodeChildNodes(vobj) diff --git a/src/Mod/BIM/ArchRebar.py b/src/Mod/BIM/ArchRebar.py index 3b735ffc66..2d7c751686 100644 --- a/src/Mod/BIM/ArchRebar.py +++ b/src/Mod/BIM/ArchRebar.py @@ -24,9 +24,9 @@ # Modified Amritpal Singh on 07-07-2017 -__title__ = "FreeCAD Rebar" +__title__ = "FreeCAD Rebar" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchRebar # \ingroup ARCH @@ -49,62 +49,158 @@ if FreeCAD.GuiUp: from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCADGui from draftutils.translate import translate + # TODO: check if this import is still needed, and if so, whether # it can be moved made conditional on the GUI being loaded # for Rebar addon compatibility from bimcommands import BimRebar + _CommandRebar = BimRebar.Arch_Rebar else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class _Rebar(ArchComponent.Component): - "A parametric reinforcement bar (rebar) object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Rebar" self.setProperties(obj) obj.IfcType = "Reinforcing Bar" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Diameter" in pl: - obj.addProperty("App::PropertyLength","Diameter","Rebar",QT_TRANSLATE_NOOP("App::Property","The diameter of the bar"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Diameter", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "The diameter of the bar"), + locked=True, + ) if not "OffsetStart" in pl: - obj.addProperty("App::PropertyDistance","OffsetStart","Rebar",QT_TRANSLATE_NOOP("App::Property","The distance between the border of the beam and the first bar (concrete cover)."), locked=True) + obj.addProperty( + "App::PropertyDistance", + "OffsetStart", + "Rebar", + QT_TRANSLATE_NOOP( + "App::Property", + "The distance between the border of the beam and the first bar (concrete cover).", + ), + locked=True, + ) if not "OffsetEnd" in pl: - obj.addProperty("App::PropertyDistance","OffsetEnd","Rebar",QT_TRANSLATE_NOOP("App::Property","The distance between the border of the beam and the last bar (concrete cover)."), locked=True) + obj.addProperty( + "App::PropertyDistance", + "OffsetEnd", + "Rebar", + QT_TRANSLATE_NOOP( + "App::Property", + "The distance between the border of the beam and the last bar (concrete cover).", + ), + locked=True, + ) if not "Amount" in pl: - obj.addProperty("App::PropertyInteger","Amount","Rebar",QT_TRANSLATE_NOOP("App::Property","The amount of bars"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "Amount", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "The amount of bars"), + locked=True, + ) if not "Spacing" in pl: - obj.addProperty("App::PropertyLength","Spacing","Rebar",QT_TRANSLATE_NOOP("App::Property","The spacing between the bars"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Spacing", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "The spacing between the bars"), + locked=True, + ) obj.setEditorMode("Spacing", 1) if not "Distance" in pl: - obj.addProperty("App::PropertyLength","Distance","Rebar",QT_TRANSLATE_NOOP("App::Property","The total distance to span the rebars over. Keep 0 to automatically use the host shape size."), locked=True) + obj.addProperty( + "App::PropertyLength", + "Distance", + "Rebar", + QT_TRANSLATE_NOOP( + "App::Property", + "The total distance to span the rebars over. Keep 0 to automatically use the host shape size.", + ), + locked=True, + ) if not "Direction" in pl: - obj.addProperty("App::PropertyVector","Direction","Rebar",QT_TRANSLATE_NOOP("App::Property","The direction to use to spread the bars. Keep (0,0,0) for automatic direction."), locked=True) + obj.addProperty( + "App::PropertyVector", + "Direction", + "Rebar", + QT_TRANSLATE_NOOP( + "App::Property", + "The direction to use to spread the bars. Keep (0,0,0) for automatic direction.", + ), + locked=True, + ) if not "Rounding" in pl: - obj.addProperty("App::PropertyFloat","Rounding","Rebar",QT_TRANSLATE_NOOP("App::Property","The fillet to apply to the angle of the base profile. This value is multiplied by the bar diameter."), locked=True) + obj.addProperty( + "App::PropertyFloat", + "Rounding", + "Rebar", + QT_TRANSLATE_NOOP( + "App::Property", + "The fillet to apply to the angle of the base profile. This value is multiplied by the bar diameter.", + ), + locked=True, + ) if not "PlacementList" in pl: - obj.addProperty("App::PropertyPlacementList","PlacementList","Rebar",QT_TRANSLATE_NOOP("App::Property","List of placement of all the bars"), locked=True) + obj.addProperty( + "App::PropertyPlacementList", + "PlacementList", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "List of placement of all the bars"), + locked=True, + ) if not "Host" in pl: - obj.addProperty("App::PropertyLink","Host","Rebar",QT_TRANSLATE_NOOP("App::Property","The structure object that hosts this rebar"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Host", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "The structure object that hosts this rebar"), + locked=True, + ) if not "CustomSpacing" in pl: - obj.addProperty("App::PropertyString", "CustomSpacing", "Rebar", QT_TRANSLATE_NOOP("App::Property","The custom spacing of rebar"), locked=True) + obj.addProperty( + "App::PropertyString", + "CustomSpacing", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "The custom spacing of rebar"), + locked=True, + ) if not "Length" in pl: - obj.addProperty("App::PropertyDistance", "Length", "Rebar", QT_TRANSLATE_NOOP("App::Property","Length of a single rebar"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "Length", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "Length of a single rebar"), + locked=True, + ) obj.setEditorMode("Length", 1) if not "TotalLength" in pl: - obj.addProperty("App::PropertyDistance", "TotalLength", "Rebar", QT_TRANSLATE_NOOP("App::Property","Total length of all rebars"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "TotalLength", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "Total length of all rebars"), + locked=True, + ) obj.setEditorMode("TotalLength", 1) if not "Mark" in pl: obj.addProperty( @@ -115,40 +211,40 @@ class _Rebar(ArchComponent.Component): locked=True, ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Rebar" - def getBaseAndAxis(self,wire): - + def getBaseAndAxis(self, wire): "returns a base point and orientation axis from the base wire" import DraftGeomUtils + if wire: e = wire.Edges[0] - #v = DraftGeomUtils.vec(e).normalize() + # v = DraftGeomUtils.vec(e).normalize() v = e.tangentAt(e.FirstParameter) - return e.Vertexes[0].Point,v + return e.Vertexes[0].Point, v if obj.Base: if obj.Base.Shape: if obj.Base.Shape.Wires: e = obj.Base.Shape.Wires[0].Edges[0] - #v = DraftGeomUtils.vec(e).normalize() + # v = DraftGeomUtils.vec(e).normalize() v = e.tangentAt(e.FirstParameter) - return e.Vertexes[0].Point,v - return None,None + return e.Vertexes[0].Point, v + return None, None - def getRebarData(self,obj): + def getRebarData(self, obj): - #if not obj.Host: + # if not obj.Host: # return - #if Draft.getType(obj.Host) != "Structure": + # if Draft.getType(obj.Host) != "Structure": # return - #if not obj.Host.Shape: + # if not obj.Host.Shape: # return if not obj.Base: return @@ -165,37 +261,39 @@ class _Rebar(ArchComponent.Component): father = obj.Host wire = obj.Base.Shape.Wires[0] axis = None - if Draft.getType(obj.Base) == "Wire": # Draft Wires can have "wrong" placement + if Draft.getType(obj.Base) == "Wire": # Draft Wires can have "wrong" placement import DraftGeomUtils + axis = DraftGeomUtils.getNormal(obj.Base.Shape) if not axis: - axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) - if hasattr(obj,"Direction"): + axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) + if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() size = 0 if father: - size = (ArchCommands.projectToVector(father.Shape.copy(),axis)).Length - if hasattr(obj,"Distance"): + size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length + if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value - if hasattr(obj,"Rounding"): + if hasattr(obj, "Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils - wire = DraftGeomUtils.filletWire(wire,radius) + + wire = DraftGeomUtils.filletWire(wire, radius) wires = [] if obj.Amount == 1: if size and father: - offset = DraftVecUtils.scaleTo(axis,size/2) + offset = DraftVecUtils.scaleTo(axis, size / 2) else: offset = FreeCAD.Vector() wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: - baseoffset = DraftVecUtils.scaleTo(axis,obj.OffsetStart.Value) + baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": @@ -203,7 +301,7 @@ class _Rebar(ArchComponent.Component): else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) - vinterval = DraftVecUtils.scaleTo(axis,interval) + vinterval = DraftVecUtils.scaleTo(axis, interval) for i in range(obj.Amount): if i == 0: if baseoffset: @@ -213,20 +311,20 @@ class _Rebar(ArchComponent.Component): wire = wire.copy() wire.translate(vinterval) wires.append(wire) - return [wires,obj.Diameter.Value/2] + return [wires, obj.Diameter.Value / 2] - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): if prop == "IfcType": root = ArchIFC.IfcProduct() root.setupIfcAttributes(obj) root.setupIfcComplexAttributes(obj) elif prop == "Host": - if hasattr(obj,"Host"): + if hasattr(obj, "Host"): if obj.Host: # mark host to recompute so it can detect this object obj.Host.touch() - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -234,39 +332,34 @@ class _Rebar(ArchComponent.Component): return if not obj.Base: # let pass without error so that object can receive a shape directly - #FreeCAD.Console.PrintError( + # FreeCAD.Console.PrintError( # "No Base, return without a rebar shape for {}.\n" # .format(obj.Name) - #) + # ) return - if not hasattr(obj.Base,"Shape") or (not obj.Base.Shape) or obj.Base.Shape.isNull(): + if not hasattr(obj.Base, "Shape") or (not obj.Base.Shape) or obj.Base.Shape.isNull(): FreeCAD.Console.PrintError( - "No Shape in Base, return without a rebar shape for {}.\n" - .format(obj.Name) + "No Shape in Base, return without a rebar shape for {}.\n".format(obj.Name) ) return if obj.Base.Shape.Faces: FreeCAD.Console.PrintError( - "Faces in Shape of Base, return without a rebar shape for {}.\n" - .format(obj.Name) + "Faces in Shape of Base, return without a rebar shape for {}.\n".format(obj.Name) ) return if not obj.Base.Shape.Edges: FreeCAD.Console.PrintError( - "No Edges in Shape of Base, return without a rebar shape for {}.\n" - .format(obj.Name) + "No Edges in Shape of Base, return without a rebar shape for {}.\n".format(obj.Name) ) return if not obj.Diameter.Value: FreeCAD.Console.PrintError( - "No Diameter Value, return without a rebar shape for {}.\n" - .format(obj.Name) + "No Diameter Value, return without a rebar shape for {}.\n".format(obj.Name) ) return if not obj.Amount: FreeCAD.Console.PrintError( - "No Amount, return without a rebar shape for {}.\n" - .format(obj.Name) + "No Amount, return without a rebar shape for {}.\n".format(obj.Name) ) return father = obj.Host @@ -274,14 +367,15 @@ class _Rebar(ArchComponent.Component): if not father: # support for old-style rebars if obj.InList: - if hasattr(obj.InList[0],"Armatures"): + if hasattr(obj.InList[0], "Armatures"): if obj in obj.InList[0].Armatures: father = obj.InList[0] if father: - if hasattr(father,'Shape'): + if hasattr(father, "Shape"): fathershape = father.Shape import Part + # corner cases: # compound from more Wires # compound without Wires but with multiple Edges @@ -291,29 +385,30 @@ class _Rebar(ArchComponent.Component): wire = Part.Wire(obj.Base.Shape.Edges[0]) else: wire = obj.Base.Shape.Wires[0] - if hasattr(obj,"Rounding"): - #print(obj.Rounding) + if hasattr(obj, "Rounding"): + # print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value from DraftGeomUtils import filletWire - wire = filletWire(wire,radius) + + wire = filletWire(wire, radius) bpoint, bvec = self.getBaseAndAxis(wire) if not bpoint: return - axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) + axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) if fathershape: - size = (ArchCommands.projectToVector(fathershape.copy(),axis)).Length + size = (ArchCommands.projectToVector(fathershape.copy(), axis)).Length else: size = 1 - if hasattr(obj,"Direction"): + if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if fathershape: - size = (ArchCommands.projectToVector(fathershape.copy(),axis)).Length + size = (ArchCommands.projectToVector(fathershape.copy(), axis)).Length else: size = 1 - if hasattr(obj,"Distance"): + if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value spacinglist = None @@ -330,10 +425,10 @@ class _Rebar(ArchComponent.Component): if length: obj.Length = length pl = obj.Placement - circle = Part.makeCircle(obj.Diameter.Value/2,bpoint,bvec) + circle = Part.makeCircle(obj.Diameter.Value / 2, bpoint, bvec) circle = Part.Wire(circle) try: - bar = wire.makePipeShell([circle],True,False,2) + bar = wire.makePipeShell([circle], True, False, 2) basewire = wire.copy() except Part.OCCError: print("Arch: error sweeping rebar profile along the base sketch") @@ -345,15 +440,34 @@ class _Rebar(ArchComponent.Component): rot = FreeCAD.Rotation() if obj.Amount == 1: if hasattr(obj, "RebarShape"): - barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.RebarShape) + barplacement = CalculatePlacement( + obj.Amount, + 1, + obj.Diameter.Value, + size, + axis, + rot, + obj.OffsetStart.Value, + obj.OffsetEnd.Value, + obj.RebarShape, + ) else: - barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) + barplacement = CalculatePlacement( + obj.Amount, + 1, + obj.Diameter.Value, + size, + axis, + rot, + obj.OffsetStart.Value, + obj.OffsetEnd.Value, + ) placementlist.append(barplacement) - if hasattr(obj,"Spacing"): + if hasattr(obj, "Spacing"): obj.Spacing = 0 else: if obj.OffsetStart.Value: - baseoffset = DraftVecUtils.scaleTo(axis,obj.OffsetStart.Value) + baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": @@ -363,27 +477,57 @@ class _Rebar(ArchComponent.Component): interval = interval / (obj.Amount - 1) for i in range(obj.Amount): if hasattr(obj, "RebarShape"): - barplacement = CalculatePlacement(obj.Amount, i+1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.RebarShape) + barplacement = CalculatePlacement( + obj.Amount, + i + 1, + obj.Diameter.Value, + size, + axis, + rot, + obj.OffsetStart.Value, + obj.OffsetEnd.Value, + obj.RebarShape, + ) else: - barplacement = CalculatePlacement(obj.Amount, i+1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) + barplacement = CalculatePlacement( + obj.Amount, + i + 1, + obj.Diameter.Value, + size, + axis, + rot, + obj.OffsetStart.Value, + obj.OffsetEnd.Value, + ) placementlist.append(barplacement) - if hasattr(obj,"Spacing"): + if hasattr(obj, "Spacing"): obj.Spacing = interval # Calculate placement of bars from custom spacing. if spacinglist: placementlist[:] = [] if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": - reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) + reqInfluenceArea = size - ( + obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value + ) else: reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) # Avoid unnecessary checks to pass like. For eg.: when we have values # like influenceArea is 100.00001 and reqInflueneArea is 100 if round(influenceArea) > round(reqInfluenceArea): - FreeCAD.Console.PrintWarning("Influence area of rebars is greater than "+ str(reqInfluenceArea) + ".\n") + FreeCAD.Console.PrintWarning( + "Influence area of rebars is greater than " + str(reqInfluenceArea) + ".\n" + ) elif round(influenceArea) < round(reqInfluenceArea): FreeCAD.Console.PrintWarning("Last span is greater that end offset.\n") for i in range(len(spacinglist)): - barplacement = CustomSpacingPlacement(spacinglist, i+1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) + barplacement = CustomSpacingPlacement( + spacinglist, + i + 1, + axis, + father.Placement.Rotation, + obj.OffsetStart.Value, + obj.OffsetEnd.Value, + ) placementlist.append(barplacement) obj.Amount = len(spacinglist) obj.Spacing = 0 @@ -400,30 +544,37 @@ class _Rebar(ArchComponent.Component): obj.Placement = pl obj.TotalLength = obj.Length * len(obj.PlacementList) -class _ViewProviderRebar(ArchComponent.ViewProviderComponent): +class _ViewProviderRebar(ArchComponent.ViewProviderComponent): "A View Provider for the Rebar object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) self.setProperties(vobj) vobj.ShapeColor = ArchCommands.getDefaultColor("Rebar") - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList if not "RebarShape" in pl: - vobj.addProperty("App::PropertyString","RebarShape","Rebar",QT_TRANSLATE_NOOP("App::Property","Shape of rebar"), locked=True).RebarShape - vobj.setEditorMode("RebarShape",2) + vobj.addProperty( + "App::PropertyString", + "RebarShape", + "Rebar", + QT_TRANSLATE_NOOP("App::Property", "Shape of rebar"), + locked=True, + ).RebarShape + vobj.setEditorMode("RebarShape", 2) - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Rebar_Tree.svg" def setEdit(self, vobj, mode): @@ -454,23 +605,24 @@ class _ViewProviderRebar(ArchComponent.ViewProviderComponent): module.editDialog(vobj) return False - def updateData(self,obj,prop): + def updateData(self, obj, prop): if prop == "Shape": - if hasattr(self,"centerline"): + if hasattr(self, "centerline"): if self.centerline: self.centerlinegroup.removeChild(self.centerline) - if hasattr(obj.Proxy,"wires"): + if hasattr(obj.Proxy, "wires"): if obj.Proxy.wires: import re from pivy import coin import Part + self.centerline = coin.SoSeparator() comp = Part.makeCompound(obj.Proxy.wires) - buf = re.findall(r"point \[(.*?)\]",comp.writeInventor().replace("\n","")) - pts = [zip(*[iter( c.split() )]*3) for c in buf] + buf = re.findall(r"point \[(.*?)\]", comp.writeInventor().replace("\n", "")) + pts = [zip(*[iter(c.split())] * 3) for c in buf] for pt in pts: - vlist = [ [float(v[0]),float(v[1]),float(v[2])] for v in pt ] + vlist = [[float(v[0]), float(v[1]), float(v[2])] for v in pt] ps = coin.SoSeparator() coords = coin.SoCoordinate3() coords.point.setValues(vlist) @@ -480,39 +632,42 @@ class _ViewProviderRebar(ArchComponent.ViewProviderComponent): ps.addChild(ls) self.centerline.addChild(ps) self.centerlinegroup.addChild(self.centerline) - ArchComponent.ViewProviderComponent.updateData(self,obj,prop) + ArchComponent.ViewProviderComponent.updateData(self, obj, prop) - def attach(self,vobj): + def attach(self, vobj): from pivy import coin + self.centerlinegroup = coin.SoSeparator() self.centerlinegroup.setName("Centerline") self.centerlinecolor = coin.SoBaseColor() self.centerlinestyle = coin.SoDrawStyle() self.centerlinegroup.addChild(self.centerlinecolor) self.centerlinegroup.addChild(self.centerlinestyle) - vobj.addDisplayMode(self.centerlinegroup,"Centerline") - ArchComponent.ViewProviderComponent.attach(self,vobj) + vobj.addDisplayMode(self.centerlinegroup, "Centerline") + ArchComponent.ViewProviderComponent.attach(self, vobj) - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - if (prop == "LineColor") and hasattr(vobj,"LineColor"): - if hasattr(self,"centerlinecolor"): + if (prop == "LineColor") and hasattr(vobj, "LineColor"): + if hasattr(self, "centerlinecolor"): c = vobj.LineColor - self.centerlinecolor.rgb.setValue(c[0],c[1],c[2]) - elif (prop == "LineWidth") and hasattr(vobj,"LineWidth"): - if hasattr(self,"centerlinestyle"): + self.centerlinecolor.rgb.setValue(c[0], c[1], c[2]) + elif (prop == "LineWidth") and hasattr(vobj, "LineWidth"): + if hasattr(self, "centerlinestyle"): self.centerlinestyle.lineWidth = vobj.LineWidth - ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) + ArchComponent.ViewProviderComponent.onChanged(self, vobj, prop) - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): - modes=["Centerline"] - return modes+ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj) + modes = ["Centerline"] + return modes + ArchComponent.ViewProviderComponent.getDisplayModes(self, vobj) -def CalculatePlacement(baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape = ""): - """ CalculatePlacement([baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape]): +def CalculatePlacement( + baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape="" +): + """CalculatePlacement([baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape]): Calculate the placement of the bar from given values.""" if baramount == 1: interval = offsetstart @@ -527,9 +682,9 @@ def CalculatePlacement(baramount, barnumber, bardiameter, size, axis, rotation, placement = FreeCAD.Placement(barplacement, rotation) return placement -def CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): - """ CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): +def CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): + """CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): Calculate placement of the bar from custom spacing list.""" if barnumber == 1: bardistance = offsetstart @@ -542,24 +697,24 @@ def CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, placement = FreeCAD.Placement(barplacement, rotation) return placement -def strprocessOfCustomSpacing(span_string): - """ strprocessOfCustomSpacing(span_string): This function take input +def strprocessOfCustomSpacing(span_string): + """strprocessOfCustomSpacing(span_string): This function take input in specific syntax and return output in the form of list. For eg. Input: "3@100+2@200+3@100" Output: [100, 100, 100, 200, 200, 100, 100, 100]""" # import string span_st = span_string.strip() - span_sp = span_st.split('+') + span_sp = span_st.split("+") index = 0 spacinglist = [] while index < len(span_sp): # Find "@" recursively in span_sp array. # If not found, append the index value to "spacinglist" array. - if span_sp[index].find('@') == -1: + if span_sp[index].find("@") == -1: spacinglist.append(float(span_sp[index])) else: - in_sp = span_sp[index].split('@') + in_sp = span_sp[index].split("@") count = 0 while count < int(in_sp[0]): spacinglist.append(float(in_sp[1])) @@ -567,9 +722,9 @@ def strprocessOfCustomSpacing(span_string): index += 1 return spacinglist -def getLengthOfRebar(rebar): - """ getLengthOfRebar(RebarObject): Calculates the length of the rebar.""" +def getLengthOfRebar(rebar): + """getLengthOfRebar(RebarObject): Calculates the length of the rebar.""" base = rebar.Base # When rebar is derived from DWire if hasattr(base, "Length"): diff --git a/src/Mod/BIM/ArchReference.py b/src/Mod/BIM/ArchReference.py index 1c05863272..f725d856aa 100644 --- a/src/Mod/BIM/ArchReference.py +++ b/src/Mod/BIM/ArchReference.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch External Reference" +__title__ = "FreeCAD Arch External Reference" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchReference # \ingroup ARCH @@ -50,15 +50,16 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond class ArchReference: - """The Arch Reference object""" def __init__(self, obj): @@ -68,30 +69,35 @@ class ArchReference: ArchReference.setProperties(self, obj) self.reload = True - def setProperties(self, obj): pl = obj.PropertiesList if not "File" in pl: - t = QT_TRANSLATE_NOOP("App::Property","The base file this component is built upon") - obj.addProperty("App::PropertyFile","File","Reference",t, locked=True) + t = QT_TRANSLATE_NOOP("App::Property", "The base file this component is built upon") + obj.addProperty("App::PropertyFile", "File", "Reference", t, locked=True) if not "Part" in pl: - t = QT_TRANSLATE_NOOP("App::Property","The part to use from the base file") - obj.addProperty("App::PropertyString","Part","Reference",t, locked=True) + t = QT_TRANSLATE_NOOP("App::Property", "The part to use from the base file") + obj.addProperty("App::PropertyString", "Part", "Reference", t, locked=True) if not "ReferenceMode" in pl: - t = QT_TRANSLATE_NOOP("App::Property","The way the referenced objects are included in the current document. 'Normal' includes the shape, 'Transient' discards the shape when the object is switched off (smaller filesize), 'Lightweight' does not import the shape but only the OpenInventor representation") - obj.addProperty("App::PropertyEnumeration","ReferenceMode","Reference",t, locked=True) - obj.ReferenceMode = ["Normal","Transient","Lightweight"] + t = QT_TRANSLATE_NOOP( + "App::Property", + "The way the referenced objects are included in the current document. 'Normal' includes the shape, 'Transient' discards the shape when the object is switched off (smaller filesize), 'Lightweight' does not import the shape but only the OpenInventor representation", + ) + obj.addProperty( + "App::PropertyEnumeration", "ReferenceMode", "Reference", t, locked=True + ) + obj.ReferenceMode = ["Normal", "Transient", "Lightweight"] if "TransientReference" in pl: if obj.TransientReference: obj.ReferenceMode = "Transient" obj.removeProperty("TransientReference") t = translate("Arch", "TransientReference property to ReferenceMode") - FreeCAD.Console.PrintMessage(translate("Arch","Upgrading")+" "+obj.Label+" "+t+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Upgrading") + " " + obj.Label + " " + t + "\n" + ) if not "FuseArch" in pl: - t = QT_TRANSLATE_NOOP("App::Property","Fuse objects of same material") - obj.addProperty("App::PropertyBool","FuseArch", "Reference", t, locked=True) - + t = QT_TRANSLATE_NOOP("App::Property", "Fuse objects of same material") + obj.addProperty("App::PropertyBool", "FuseArch", "Reference", t, locked=True) def onDocumentRestored(self, obj): @@ -101,20 +107,17 @@ class ArchReference: if obj.ViewObject and obj.ViewObject.Proxy: obj.ViewObject.Proxy.loadInventor(obj) - def dumps(self): return None - def loads(self, state): self.Type = "Reference" - def onChanged(self, obj, prop): - if prop in ["File","Part"]: + if prop in ["File", "Part"]: self.reload = True elif prop == "ReferenceMode": if obj.ReferenceMode == "Normal": @@ -130,19 +133,20 @@ class ArchReference: elif obj.ReferenceMode == "Lightweight": self.reload = False import Part + pl = obj.Placement obj.Shape = Part.Shape() obj.Placement = pl if obj.ViewObject and obj.ViewObject.Proxy: obj.ViewObject.Proxy.loadInventor(obj) - def execute(self, obj): import Part + pl = obj.Placement filename = self.getFile(obj) - if filename and self.reload and obj.ReferenceMode in ["Normal","Transient"]: + if filename and self.reload and obj.ReferenceMode in ["Normal", "Transient"]: self.parts = self.getPartsList(obj) if self.parts: if filename.lower().endswith(".fcstd"): @@ -156,21 +160,21 @@ class ArchReference: shapedata = f.read() f.close() shapedata = shapedata.decode("utf8") - shape = self.cleanShape(shapedata,obj,self.parts[obj.Part][2]) + shape = self.cleanShape(shapedata, obj, self.parts[obj.Part][2]) self.shapes.append(shape) obj.Shape = shape if not pl.isIdentity(): obj.Placement = pl else: - t = translate("Arch","Part not found in file") - FreeCAD.Console.PrintError(t+"\n") + t = translate("Arch", "Part not found in file") + FreeCAD.Console.PrintError(t + "\n") else: for part in self.parts.values(): f = zdoc.open(part[1]) shapedata = f.read() f.close() shapedata = shapedata.decode("utf8") - shape = self.cleanShape(shapedata,obj) + shape = self.cleanShape(shapedata, obj) self.shapes.append(shape) if self.shapes: obj.Shape = Part.makeCompound(self.shapes) @@ -182,8 +186,10 @@ class ArchReference: from nativeifc import ifc_tools from nativeifc import ifc_generator except: - t = translate("Arch","NativeIFC not available - unable to process IFC files") - FreeCAD.Console.PrintError(t+"\n") + t = translate( + "Arch", "NativeIFC not available - unable to process IFC files" + ) + FreeCAD.Console.PrintError(t + "\n") return elements = self.getIFCElements(obj, ifcfile) shape, colors = ifc_generator.generate_shape(ifcfile, elements, cached=True) @@ -208,6 +214,7 @@ class ArchReference: doc = obj.Document oldobjs = list(doc.Objects) import Import + Import.readDXF(filename, doc.Name, True, loc + "/RefDxfImport") newobjs = [o for o in doc.Objects if o not in oldobjs] shapes = [o.Shape for o in newobjs if o.isDerivedFrom("Part::Feature")] @@ -220,16 +227,14 @@ class ArchReference: doc.removeObject(n) self.reload = False - def getIFCElements(self, obj, ifcfile): - """returns IFC elements for this object""" try: from nativeifc import ifc_generator except: - t = translate("Arch","NativeIFC not available - unable to process IFC files") - FreeCAD.Console.PrintError(t+"\n") + t = translate("Arch", "NativeIFC not available - unable to process IFC files") + FreeCAD.Console.PrintError(t + "\n") return if obj.Part: element = ifcfile[int(obj.Part)] @@ -239,12 +244,11 @@ class ArchReference: elements = ifc_generator.filter_types(elements) return elements - def cleanShape(self, shapedata, obj, materials=None): - """cleans the imported shape""" import Part + shape = Part.Shape() shape.importBrepFromString(shapedata) if obj.FuseArch and materials: @@ -263,8 +267,8 @@ class ArchReference: break else: shapes.append(edge) - #print("solids:",len(shape.Solids),"mattable:",materials) - for key,solindexes in materials.items(): + # print("solids:",len(shape.Solids),"mattable:",materials) + for key, solindexes in materials.items(): if key == "Undefined": # do not join objects with no defined material for solindex in [int(i) for i in solindexes.split(",")]: @@ -282,13 +286,11 @@ class ArchReference: try: shape = shape.removeSplitter() except Exception: - t = translate("Arch","Error removing splitter") - FreeCAD.Console.PrintError(obj.Label+": "+t+"\n") + t = translate("Arch", "Error removing splitter") + FreeCAD.Console.PrintError(obj.Label + ": " + t + "\n") return shape - - def exists(self,filepath): - + def exists(self, filepath): """case-insensitive version of os.path.exists. Returns the actual file path or None""" if os.path.exists(filepath): @@ -301,9 +303,7 @@ class ArchReference: return p + e.upper() return None - - def getFile(self,obj,filename=None): - + def getFile(self, obj, filename=None): """gets a valid file, if possible""" if not filename: @@ -318,7 +318,7 @@ class ArchReference: # search for the file in the current directory if not found basename = os.path.basename(filename) currentdir = os.path.dirname(obj.Document.FileName) - altfile = os.path.join(currentdir,basename) + altfile = os.path.join(currentdir, basename) if altfile == obj.Document.FileName: return None elif self.exists(altfile): @@ -328,16 +328,14 @@ class ArchReference: altfile = None subdirs = self.splitall(os.path.dirname(filename)) for i in range(len(subdirs)): - subpath = [currentdir]+subdirs[-i:]+[basename] + subpath = [currentdir] + subdirs[-i:] + [basename] altfile = os.path.join(*subpath) if self.exists(altfile): return self.exists(altfile) return None return self.exists(filename) - def getPartsList(self, obj, filename=None): - """returns a list of Part-based objects in a file""" filename = self.getFile(obj, filename) @@ -350,19 +348,15 @@ class ArchReference: elif filename.lower().endswith(".dxf"): return self.getPartsListDXF(obj, filename) - def getPartsListDXF(self, obj, filename): - """returns a list of Part-based objects in a DXF file""" # support layers - #with open(filename) as f: + # with open(filename) as f: # txt = f.read() return {} - def getPartsListIFC(self, obj, filename): - """returns a list of Part-based objects in a IFC file""" ifcfile = self.getIfcFile(filename) @@ -378,9 +372,7 @@ class ArchReference: res[str(s.id())] = [name, s, None] return res - def getPartsListFCSTD(self, obj, filename): - """returns a list of Part-based objects in a FCStd file""" parts = {} @@ -395,35 +387,35 @@ class ArchReference: for line in docf: line = line.decode("utf8") if "" in line: writemode = False elif "" in line: if name and label and part: - parts[name] = [label,part,materials] + parts[name] = [label, part, materials] name = None label = None part = None @@ -431,24 +423,20 @@ class ArchReference: writemode = False return parts - def getIfcFile(self, filename): - """Gets an IfcOpenShell object""" try: import ifcopenshell except: - t = translate("Arch","NativeIFC not available - unable to process IFC files") - FreeCAD.Console.PrintError(t+"\n") + t = translate("Arch", "NativeIFC not available - unable to process IFC files") + FreeCAD.Console.PrintError(t + "\n") return None if not getattr(self, "ifcfile", None): self.ifcfile = ifcopenshell.open(filename) return self.ifcfile - def getColors(self, obj): - """returns the Shape Appearance of the referenced object(s)""" filename = self.getFile(obj) @@ -477,7 +465,6 @@ class ArchReference: return totalcolors - def _getColorsPart(self, filename, part): zdoc = zipfile.ZipFile(filename) if not "GuiDocument.xml" in zdoc.namelist(): @@ -490,21 +477,21 @@ class ArchReference: writemode3 = False for line in docf: line = line.decode("utf8") - if (" newLn: return lst[0:newLn] @@ -68,17 +70,18 @@ def adjust_list_len (lst, newLn, val): return lst[:] + ([val] * (newLn - ln)) -def find_inters (edge1, edge2, infinite1=True, infinite2=True): - '''Future wrapper for DraftGeomUtils.findIntersection. The function now +def find_inters(edge1, edge2, infinite1=True, infinite2=True): + """Future wrapper for DraftGeomUtils.findIntersection. The function now contains a modified copy of getLineIntersections from that function. - ''' + """ + def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): # if pt1: - ## first check if we don't already have coincident endpoints ######## we do not want that here ######## - # if pt1 in [pt3, pt4]: - # return [pt1] - # elif (pt2 in [pt3, pt4]): - # return [pt2] + ## first check if we don't already have coincident endpoints ######## we do not want that here ######## + # if pt1 in [pt3, pt4]: + # return [pt1] + # elif (pt2 in [pt3, pt4]): + # return [pt2] norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) @@ -107,9 +110,11 @@ def find_inters (edge1, edge2, infinite1=True, infinite2=True): norm3 = vec1.cross(vec2) denom = norm3.x + norm3.y + norm3.z if not DraftVecUtils.isNull(norm3) and denom != 0: - k = ((pt3.z - pt1.z) * (vec2.x - vec2.y) - + (pt3.y - pt1.y) * (vec2.z - vec2.x) - + (pt3.x - pt1.x) * (vec2.y - vec2.z)) / denom + k = ( + (pt3.z - pt1.z) * (vec2.x - vec2.y) + + (pt3.y - pt1.y) * (vec2.z - vec2.x) + + (pt3.x - pt1.x) * (vec2.y - vec2.z) + ) / denom vec1.scale(k, k, k) intp = pt1.add(vec1) @@ -125,10 +130,12 @@ def find_inters (edge1, edge2, infinite1=True, infinite2=True): else: return [] # Lines aren't on same plane - pt1, pt2, pt3, pt4 = [edge1.Vertexes[0].Point, - edge1.Vertexes[1].Point, - edge2.Vertexes[0].Point, - edge2.Vertexes[1].Point] + pt1, pt2, pt3, pt4 = [ + edge1.Vertexes[0].Point, + edge1.Vertexes[1].Point, + edge2.Vertexes[0].Point, + edge2.Vertexes[1].Point, + ] return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) @@ -138,7 +145,7 @@ def face_from_points(ptLst): # Use DraftVecUtils.removeDouble after append as it does not compare the first and last vector: ptLst = DraftVecUtils.removeDoubles(ptLst) ln = len(ptLst) - if ln < 4: # at least 4 points are required for 3 edges + if ln < 4: # at least 4 points are required for 3 edges return None edgeLst = [] for i in range(ln - 1): @@ -148,9 +155,9 @@ def face_from_points(ptLst): return Part.Face(wire) - class _Roof(ArchComponent.Component): - '''The Roof object''' + """The Roof object""" + def __init__(self, obj): ArchComponent.Component.__init__(self, obj) self.Type = "Roof" @@ -160,79 +167,117 @@ class _Roof(ArchComponent.Component): def setProperties(self, obj): pl = obj.PropertiesList if not "Angles" in pl: - obj.addProperty("App::PropertyFloatList", - "Angles", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of angles of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Angles", + "Roof", + QT_TRANSLATE_NOOP("App::Property", "The list of angles of the roof segments"), + locked=True, + ) if not "Runs" in pl: - obj.addProperty("App::PropertyFloatList", - "Runs", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of horizontal length projections of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Runs", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", + "The list of horizontal length projections of the roof segments", + ), + locked=True, + ) if not "IdRel" in pl: - obj.addProperty("App::PropertyIntegerList", - "IdRel", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of IDs of the relative profiles of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyIntegerList", + "IdRel", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", "The list of IDs of the relative profiles of the roof segments" + ), + locked=True, + ) if not "Thickness" in pl: - obj.addProperty("App::PropertyFloatList", - "Thickness", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of thicknesses of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Thickness", + "Roof", + QT_TRANSLATE_NOOP("App::Property", "The list of thicknesses of the roof segments"), + locked=True, + ) if not "Overhang" in pl: - obj.addProperty("App::PropertyFloatList", - "Overhang", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of overhangs of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Overhang", + "Roof", + QT_TRANSLATE_NOOP("App::Property", "The list of overhangs of the roof segments"), + locked=True, + ) if not "Heights" in pl: - obj.addProperty("App::PropertyFloatList", - "Heights", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The list of calculated heights of the roof segments"), - locked=True) + obj.addProperty( + "App::PropertyFloatList", + "Heights", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", "The list of calculated heights of the roof segments" + ), + locked=True, + ) if not "Face" in pl: - obj.addProperty("App::PropertyInteger", - "Face", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The face number of the base object used to build the roof"), - locked=True) + obj.addProperty( + "App::PropertyInteger", + "Face", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", "The face number of the base object used to build the roof" + ), + locked=True, + ) if not "RidgeLength" in pl: - obj.addProperty("App::PropertyLength", - "RidgeLength", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The total length of the ridges and hips of the roof"), - locked=True) - obj.setEditorMode("RidgeLength",1) + obj.addProperty( + "App::PropertyLength", + "RidgeLength", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", "The total length of the ridges and hips of the roof" + ), + locked=True, + ) + obj.setEditorMode("RidgeLength", 1) if not "BorderLength" in pl: - obj.addProperty("App::PropertyLength", - "BorderLength", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "The total length of the borders of the roof"), - locked=True) - obj.setEditorMode("BorderLength",1) + obj.addProperty( + "App::PropertyLength", + "BorderLength", + "Roof", + QT_TRANSLATE_NOOP("App::Property", "The total length of the borders of the roof"), + locked=True, + ) + obj.setEditorMode("BorderLength", 1) if not "Flip" in pl: - obj.addProperty("App::PropertyBool", - "Flip", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "Specifies if the direction of the roof should be flipped"), - locked=True) + obj.addProperty( + "App::PropertyBool", + "Flip", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", "Specifies if the direction of the roof should be flipped" + ), + locked=True, + ) if not "Subvolume" in pl: - obj.addProperty("App::PropertyLink", - "Subvolume", - "Roof", - QT_TRANSLATE_NOOP("App::Property", "An optional object that defines a volume to be subtracted from walls. If field is set - it has a priority over auto-generated subvolume"), - locked=True) + obj.addProperty( + "App::PropertyLink", + "Subvolume", + "Roof", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional object that defines a volume to be subtracted from walls. If field is set - it has a priority over auto-generated subvolume", + ), + locked=True, + ) def onDocumentRestored(self, obj): ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Roof" def flipEdges(self, edges): @@ -244,31 +289,35 @@ class _Roof(ArchComponent.Component): return newEdges def calcHeight(self, id): - '''Get the height from run and angle of the given roof profile''' - htRel = self.profilsDico[id]["run"] * (math.tan(math.radians(self.profilsDico[id]["angle"]))) + """Get the height from run and angle of the given roof profile""" + htRel = self.profilsDico[id]["run"] * ( + math.tan(math.radians(self.profilsDico[id]["angle"])) + ) return htRel def calcRun(self, id): - '''Get the run from height and angle of the given roof profile''' - runRel = self.profilsDico[id]["height"] / (math.tan(math.radians(self.profilsDico[id]["angle"]))) + """Get the run from height and angle of the given roof profile""" + runRel = self.profilsDico[id]["height"] / ( + math.tan(math.radians(self.profilsDico[id]["angle"])) + ) return runRel def calcAngle(self, id): - '''Get the angle from height and run of the given roof profile''' + """Get the angle from height and run of the given roof profile""" ang = math.degrees(math.atan(self.profilsDico[id]["height"] / self.profilsDico[id]["run"])) return ang def getPerpendicular(self, vec, rotEdge, l): - '''Get the perpendicular vec of given edge on xy plane''' + """Get the perpendicular vec of given edge on xy plane""" norm = Vector(0.0, 0.0, 1.0) if hasattr(self, "normal"): if self.normal: norm = self.normal per = vec.cross(norm) - if -180.0 <= rotEdge < -90.0: + if -180.0 <= rotEdge < -90.0: per[0] = -abs(per[0]) per[1] = -abs(per[1]) - elif -90.0 <= rotEdge <= 0.0: + elif -90.0 <= rotEdge <= 0.0: per[0] = -abs(per[0]) per[1] = abs(per[1]) elif 0.0 < rotEdge <= 90.0: @@ -313,19 +362,21 @@ class _Roof(ArchComponent.Component): ptCurr = profilCurr["edge"].Vertexes[0].Point ptOpposite = profilOpposite["edge"].Vertexes[0].Point dis = ptCurr.distanceToLine(ptOpposite, profilOpposite["vec"]) - if dis < profilCurr["run"] + profilOpposite["run"]: # sum of runs is larger than dis + if dis < profilCurr["run"] + profilOpposite["run"]: # sum of runs is larger than dis angCurr = profilCurr["angle"] angOpposite = profilOpposite["angle"] - return dis / (math.tan(math.radians(angCurr)) / math.tan(math.radians(angOpposite)) + 1.0) + return dis / ( + math.tan(math.radians(angCurr)) / math.tan(math.radians(angOpposite)) + 1.0 + ) return profilCurr["run"] def calcApex(self, i, numEdges): - '''Recalculate the run and height if there is an opposite roof segment + """Recalculate the run and height if there is an opposite roof segment with a parallel edge, and if the sum of the runs of the segments is larger than the distance between the edges of the segments. - ''' + """ profilCurr = self.findProfil(i) - if 0 <= profilCurr["idrel"] < numEdges: # no apex calculation if idrel is used + if 0 <= profilCurr["idrel"] < numEdges: # no apex calculation if idrel is used return if not 0.0 < profilCurr["angle"] < 90.0: return @@ -335,13 +386,17 @@ class _Roof(ArchComponent.Component): vecNext2 = profilNext2["vec"] vecBack2 = profilBack2["vec"] runs = [] - if ((not 0 <= profilNext2["idrel"] < numEdges) + if ( + (not 0 <= profilNext2["idrel"] < numEdges) and 0.0 < profilNext2["angle"] < 90.0 - and math.isclose(vecCurr.getAngle(vecNext2), math.pi, abs_tol=1e-7)): + and math.isclose(vecCurr.getAngle(vecNext2), math.pi, abs_tol=1e-7) + ): runs.append((self.helperCalcApex(profilCurr, profilNext2))) - if ((not 0 <= profilBack2["idrel"] < numEdges) + if ( + (not 0 <= profilBack2["idrel"] < numEdges) and 0.0 < profilBack2["angle"] < 90.0 - and math.isclose(vecCurr.getAngle(vecBack2), math.pi, abs_tol=1e-7)): + and math.isclose(vecCurr.getAngle(vecBack2), math.pi, abs_tol=1e-7) + ): runs.append((self.helperCalcApex(profilCurr, profilBack2))) runs.sort() if len(runs) != 0 and runs[0] != profilCurr["run"]: @@ -357,9 +412,11 @@ class _Roof(ArchComponent.Component): if i != rel and 0 <= rel < numEdges: profilRel = self.profilsDico[rel] # do not use data from the relative profile if it in turn references a relative profile: - if (0 <= profilRel["idrel"] < numEdges # idrel of profilRel points to a profile - and rel != profilRel["idrel"] # profilRel does not reference itself - and (profilRel["angle"] == 0.0 or profilRel["run"] == 0.0)): # run or angle of profilRel is zero + if ( + 0 <= profilRel["idrel"] < numEdges # idrel of profilRel points to a profile + and rel != profilRel["idrel"] # profilRel does not reference itself + and (profilRel["angle"] == 0.0 or profilRel["run"] == 0.0) + ): # run or angle of profilRel is zero hgt = self.calcHeight(i) profilCurr["height"] = hgt elif ang == 0.0 and run == 0.0: @@ -370,7 +427,7 @@ class _Roof(ArchComponent.Component): if ang == 90.0: htRel = self.calcHeight(rel) profilCurr["height"] = htRel - else : + else: htRel = self.calcHeight(rel) profilCurr["height"] = htRel run = self.calcRun(i) @@ -380,7 +437,7 @@ class _Roof(ArchComponent.Component): profilCurr["height"] = htRel ang = self.calcAngle(i) profilCurr["angle"] = ang - else : + else: hgt = self.calcHeight(i) profilCurr["height"] = hgt else: @@ -417,7 +474,10 @@ class _Roof(ArchComponent.Component): ptInterEaves2 = ptInterEaves2Lst[0] else: ptInterEaves2 = profilCurr["eaveDraft"].Vertexes[1].Point - profilCurr["eavePtLst"] = [ptInterEaves1, ptInterEaves2] # list of points instead of edge as points can be identical + profilCurr["eavePtLst"] = [ + ptInterEaves1, + ptInterEaves2, + ] # list of points instead of edge as points can be identical def findProfil(self, i): if 0 <= i < len(self.profilsDico): @@ -433,9 +493,9 @@ class _Roof(ArchComponent.Component): else: i = 1 ptIntLst = find_inters(profilCurr["ridge"], profilOther["eaveDraft"]) - if ptIntLst: # the edges of the roof segments are not parallel + if ptIntLst: # the edges of the roof segments are not parallel ptProjLst = [ptIntLst[0]] - else: # the edges of the roof segments are parallel + else: # the edges of the roof segments are parallel ptProjLst = [profilCurr["ridge"].Vertexes[i].Point] ptProjLst = ptProjLst + [profilCurr["eavePtLst"][i]] if not isBack: @@ -446,20 +506,22 @@ class _Roof(ArchComponent.Component): def backGable(self, i): profilCurr = self.findProfil(i) profilBack = self.findProfil(i - 1) - self.helperGable(profilCurr, profilBack, isBack = True) + self.helperGable(profilCurr, profilBack, isBack=True) def nextGable(self, i): profilCurr = self.findProfil(i) profilNext = self.findProfil(i + 1) - self.helperGable(profilCurr, profilNext, isBack = False) + self.helperGable(profilCurr, profilNext, isBack=False) - def helperSloped(self, profilCurr, profilOther, ridgeCurr, ridgeOther, isBack, otherIsLower=False): + def helperSloped( + self, profilCurr, profilOther, ridgeCurr, ridgeOther, isBack, otherIsLower=False + ): if isBack: i = 0 else: i = 1 ptIntLst = find_inters(ridgeCurr, ridgeOther) - if ptIntLst: # the edges of the roof segments are not parallel + if ptIntLst: # the edges of the roof segments are not parallel ptInt = ptIntLst[0] if otherIsLower: ptRidgeLst = find_inters(profilCurr["ridge"], profilOther["ridge"]) @@ -469,24 +531,21 @@ class _Roof(ArchComponent.Component): hip = DraftGeomUtils.edg(ptInt, profilCurr["edge"].Vertexes[i].Point) ptEaveCurrLst = find_inters(hip, profilCurr["eaveDraft"]) ptEaveOtherLst = find_inters(hip, profilOther["eaveDraft"]) - if ptEaveCurrLst and ptEaveOtherLst: # both roof segments are sloped + if ptEaveCurrLst and ptEaveOtherLst: # both roof segments are sloped lenToEaveCurr = ptEaveCurrLst[0].sub(ptInt).Length lenToEaveOther = ptEaveOtherLst[0].sub(ptInt).Length if lenToEaveCurr < lenToEaveOther: ptProjLst = ptProjLst + [ptEaveCurrLst[0]] else: - ptProjLst = ptProjLst + [ptEaveOtherLst[0], - profilCurr["eavePtLst"][i]] - elif ptEaveCurrLst: # current angle is 0 + ptProjLst = ptProjLst + [ptEaveOtherLst[0], profilCurr["eavePtLst"][i]] + elif ptEaveCurrLst: # current angle is 0 ptProjLst = ptProjLst + [ptEaveCurrLst[0]] - elif ptEaveOtherLst: # other angle is 0 - ptProjLst = ptProjLst + [ptEaveOtherLst[0], - profilCurr["eavePtLst"][i]] + elif ptEaveOtherLst: # other angle is 0 + ptProjLst = ptProjLst + [ptEaveOtherLst[0], profilCurr["eavePtLst"][i]] else: print("Error determining outline") - else: # the edges of the roof segments are parallel - ptProjLst = [profilCurr["ridge"].Vertexes[i].Point, - profilCurr["eavePtLst"][i]] + else: # the edges of the roof segments are parallel + ptProjLst = [profilCurr["ridge"].Vertexes[i].Point, profilCurr["eavePtLst"][i]] if not isBack: ptProjLst.reverse() for ptProj in ptProjLst: @@ -495,20 +554,16 @@ class _Roof(ArchComponent.Component): def backSameHeight(self, i): profilCurr = self.findProfil(i) profilBack = self.findProfil(i - 1) - self.helperSloped(profilCurr, - profilBack, - profilCurr["ridge"], - profilBack["ridge"], - isBack = True) + self.helperSloped( + profilCurr, profilBack, profilCurr["ridge"], profilBack["ridge"], isBack=True + ) def nextSameHeight(self, i): profilCurr = self.findProfil(i) profilNext = self.findProfil(i + 1) - self.helperSloped(profilCurr, - profilNext, - profilCurr["ridge"], - profilNext["ridge"], - isBack = False) + self.helperSloped( + profilCurr, profilNext, profilCurr["ridge"], profilNext["ridge"], isBack=False + ) def backHigher(self, i): profilCurr = self.findProfil(i) @@ -516,11 +571,7 @@ class _Roof(ArchComponent.Component): dec = profilCurr["height"] / math.tan(math.radians(profilBack["angle"])) per = self.getPerpendicular(profilBack["vec"], profilBack["rot"], dec) edgeRidgeOnPane = DraftGeomUtils.offset(profilBack["edge"], per) - self.helperSloped(profilCurr, - profilBack, - profilCurr["ridge"], - edgeRidgeOnPane, - isBack = True) + self.helperSloped(profilCurr, profilBack, profilCurr["ridge"], edgeRidgeOnPane, isBack=True) def nextHigher(self, i): profilCurr = self.findProfil(i) @@ -528,11 +579,9 @@ class _Roof(ArchComponent.Component): dec = profilCurr["height"] / math.tan(math.radians(profilNext["angle"])) per = self.getPerpendicular(profilNext["vec"], profilNext["rot"], dec) edgeRidgeOnPane = DraftGeomUtils.offset(profilNext["edge"], per) - self.helperSloped(profilCurr, - profilNext, - profilCurr["ridge"], - edgeRidgeOnPane, - isBack = False) + self.helperSloped( + profilCurr, profilNext, profilCurr["ridge"], edgeRidgeOnPane, isBack=False + ) def backLower(self, i): profilCurr = self.findProfil(i) @@ -540,12 +589,14 @@ class _Roof(ArchComponent.Component): dec = profilBack["height"] / math.tan(math.radians(profilCurr["angle"])) per = self.getPerpendicular(profilCurr["vec"], profilCurr["rot"], dec) edgeRidgeOnPane = DraftGeomUtils.offset(profilCurr["edge"], per) - self.helperSloped(profilCurr, - profilBack, - edgeRidgeOnPane, - profilBack["ridge"], - isBack = True, - otherIsLower = True) + self.helperSloped( + profilCurr, + profilBack, + edgeRidgeOnPane, + profilBack["ridge"], + isBack=True, + otherIsLower=True, + ) def nextLower(self, i): profilCurr = self.findProfil(i) @@ -553,12 +604,14 @@ class _Roof(ArchComponent.Component): dec = profilNext["height"] / math.tan(math.radians(profilCurr["angle"])) per = self.getPerpendicular(profilCurr["vec"], profilCurr["rot"], dec) edgeRidgeOnPane = DraftGeomUtils.offset(profilCurr["edge"], per) - self.helperSloped(profilCurr, - profilNext, - edgeRidgeOnPane, - profilNext["ridge"], - isBack = False, - otherIsLower = True) + self.helperSloped( + profilCurr, + profilNext, + edgeRidgeOnPane, + profilNext["ridge"], + isBack=False, + otherIsLower=True, + ) def getRoofPaneProject(self, i): self.ptsPaneProject = [] @@ -592,12 +645,12 @@ class _Roof(ArchComponent.Component): profilCurr["points"] = self.ptsPaneProject - def createProfilShape (self, points, midpoint, rot, vec, run, diag, sol): + def createProfilShape(self, points, midpoint, rot, vec, run, diag, sol): lp = len(points) points.append(points[0]) edgesWire = [] for i in range(lp): - edge = Part.makeLine(points[i],points[i + 1]) + edge = Part.makeLine(points[i], points[i + 1]) edgesWire.append(edge) profil = Part.Wire(edgesWire) profil.translate(midpoint) @@ -611,7 +664,7 @@ class _Roof(ArchComponent.Component): profilFace = Part.Face(profil) profilShp = profilFace.extrude(vecE) profilShp = sol.common(profilShp) - #shapesList.append(profilShp) + # shapesList.append(profilShp) return profilShp def execute(self, obj): @@ -622,7 +675,7 @@ class _Roof(ArchComponent.Component): return pl = obj.Placement - #self.baseface = None + # self.baseface = None self.flip = False if hasattr(obj, "Flip"): if obj.Flip: @@ -633,10 +686,10 @@ class _Roof(ArchComponent.Component): if hasattr(obj.Base, "Shape"): if obj.Base.Shape.Solids: base = obj.Base.Shape - #pl = obj.Base.Placement + # pl = obj.Base.Placement else: - if (obj.Base.Shape.Faces and obj.Face): - baseWire = obj.Base.Shape.Faces[obj.Face-1].Wires[0] + if obj.Base.Shape.Faces and obj.Face: + baseWire = obj.Base.Shape.Faces[obj.Face - 1].Wires[0] elif obj.Base.Shape.Wires: baseWire = obj.Base.Shape.Wires[0] if baseWire: @@ -651,20 +704,27 @@ class _Roof(ArchComponent.Component): ln = len(edges) - obj.Angles = adjust_list_len(obj.Angles, ln, obj.Angles[0]) - obj.Runs = adjust_list_len(obj.Runs, ln, obj.Runs[0]) - obj.IdRel = adjust_list_len(obj.IdRel, ln, obj.IdRel[0]) + obj.Angles = adjust_list_len(obj.Angles, ln, obj.Angles[0]) + obj.Runs = adjust_list_len(obj.Runs, ln, obj.Runs[0]) + obj.IdRel = adjust_list_len(obj.IdRel, ln, obj.IdRel[0]) obj.Thickness = adjust_list_len(obj.Thickness, ln, obj.Thickness[0]) - obj.Overhang = adjust_list_len(obj.Overhang, ln, obj.Overhang[0]) + obj.Overhang = adjust_list_len(obj.Overhang, ln, obj.Overhang[0]) for i in range(ln): - self.makeRoofProfilsDic(i, obj.Angles[i], obj.Runs[i], obj.IdRel[i], obj.Overhang[i], obj.Thickness[i]) + self.makeRoofProfilsDic( + i, + obj.Angles[i], + obj.Runs[i], + obj.IdRel[i], + obj.Overhang[i], + obj.Thickness[i], + ) for i in range(ln): self.calcEdgeGeometry(i, edges[i]) for i in range(ln): - self.calcApex(i, ln) # after calcEdgeGeometry as it uses vec data + self.calcApex(i, ln) # after calcEdgeGeometry as it uses vec data for i in range(ln): - self.calcMissingData(i, ln) # after calcApex so it can use recalculated heights + self.calcMissingData(i, ln) # after calcApex so it can use recalculated heights for i in range(ln): self.calcDraftEdges(i) for i in range(ln): @@ -682,45 +742,63 @@ class _Roof(ArchComponent.Component): if face: diag = face.BoundBox.DiagonalLength midpoint = DraftGeomUtils.findMidpoint(profilCurr["edge"]) - thicknessV = profilCurr["thickness"] / (math.cos(math.radians(profilCurr["angle"]))) - overhangV = profilCurr["overhang"] * math.tan(math.radians(profilCurr["angle"])) + thicknessV = profilCurr["thickness"] / ( + math.cos(math.radians(profilCurr["angle"])) + ) + overhangV = profilCurr["overhang"] * math.tan( + math.radians(profilCurr["angle"]) + ) sol = face.extrude(Vector(0.0, 0.0, profilCurr["height"] + 1000000.0)) sol.translate(Vector(0.0, 0.0, -2.0 * overhangV)) ## baseVolume shape - ptsPaneProfil = [Vector(-profilCurr["overhang"], -overhangV, 0.0), - Vector(profilCurr["run"], profilCurr["height"], 0.0), - Vector(profilCurr["run"], profilCurr["height"] + thicknessV, 0.0), - Vector(-profilCurr["overhang"], -overhangV + thicknessV, 0.0)] - self.shps.append(self.createProfilShape(ptsPaneProfil, - midpoint, - profilCurr["rot"], - profilCurr["vec"], - profilCurr["run"], - diag, - sol)) + ptsPaneProfil = [ + Vector(-profilCurr["overhang"], -overhangV, 0.0), + Vector(profilCurr["run"], profilCurr["height"], 0.0), + Vector(profilCurr["run"], profilCurr["height"] + thicknessV, 0.0), + Vector(-profilCurr["overhang"], -overhangV + thicknessV, 0.0), + ] + self.shps.append( + self.createProfilShape( + ptsPaneProfil, + midpoint, + profilCurr["rot"], + profilCurr["vec"], + profilCurr["run"], + diag, + sol, + ) + ) ## subVolume shape - ptsSubVolProfil = [Vector(-profilCurr["overhang"], -overhangV, 0.0), - Vector(profilCurr["run"], profilCurr["height"], 0.0), - Vector(profilCurr["run"], profilCurr["height"] + 900000.0, 0.0), - Vector(-profilCurr["overhang"], profilCurr["height"] + 900000.0, 0.0)] - self.subVolShps.append(self.createProfilShape(ptsSubVolProfil, - midpoint, - profilCurr["rot"], - profilCurr["vec"], - profilCurr["run"], - diag, - sol)) + ptsSubVolProfil = [ + Vector(-profilCurr["overhang"], -overhangV, 0.0), + Vector(profilCurr["run"], profilCurr["height"], 0.0), + Vector(profilCurr["run"], profilCurr["height"] + 900000.0, 0.0), + Vector(-profilCurr["overhang"], profilCurr["height"] + 900000.0, 0.0), + ] + self.subVolShps.append( + self.createProfilShape( + ptsSubVolProfil, + midpoint, + profilCurr["rot"], + profilCurr["vec"], + profilCurr["run"], + diag, + sol, + ) + ) - if len(self.shps) == 0: # occurs if all segments have angle=90 or run=0. + if len(self.shps) == 0: # occurs if all segments have angle=90 or run=0. # create a flat roof using the eavePtLst outline: ptsPaneProject = [] for i in range(ln): ptsPaneProject.append(self.profilsDico[i]["eavePtLst"][0]) face = face_from_points(ptsPaneProject) if face: - thk = max(1.0, self.profilsDico[0]["thickness"]) # FreeCAD will crash when extruding with a null vector here + thk = max( + 1.0, self.profilsDico[0]["thickness"] + ) # FreeCAD will crash when extruding with a null vector here self.shps = [face.extrude(Vector(0.0, 0.0, thk))] self.subVolShps = [face.extrude(Vector(0.0, 0.0, 1000000.0))] @@ -729,7 +807,7 @@ class _Roof(ArchComponent.Component): for s in self.shps: base = base.fuse(s) base = self.processSubShapes(obj, base, pl) - self.applyShape(obj, base, pl, allownosolid = True) + self.applyShape(obj, base, pl, allownosolid=True) ## subVolume self.sub = self.subVolShps.pop() @@ -742,13 +820,13 @@ class _Roof(ArchComponent.Component): elif base: base = self.processSubShapes(obj, base, pl) - self.applyShape(obj, base, pl, allownosolid = True) + self.applyShape(obj, base, pl, allownosolid=True) else: FreeCAD.Console.PrintMessage(translate("Arch", "Unable to create a roof")) def getSubVolume(self, obj): - '''returns a volume to be subtracted''' - custom_subvolume = getattr(obj, 'Subvolume', None) + """returns a volume to be subtracted""" + custom_subvolume = getattr(obj, "Subvolume", None) if custom_subvolume: return custom_subvolume.Shape @@ -773,14 +851,13 @@ class _Roof(ArchComponent.Component): # BIM: Holes in roof are causing troubles # - https://github.com/FreeCAD/FreeCAD/issues/21633#issuecomment-2969640142 - faces = [] solids = [] for f in obj.Base.Shape.Faces: # obj.Base.Shape.Solids.Faces p = f.findPlane() # Curve face (surface) seems return no Plane if p: # See github issue #21633, all planes are added for safety - #if p.Axis[2] < -1e-7: # i.e. normal pointing below horizon + # if p.Axis[2] < -1e-7: # i.e. normal pointing below horizon faces.append(f) else: # TODO 2025.6.15: See github issue #21633: Find better way @@ -808,14 +885,13 @@ class _Roof(ArchComponent.Component): compound.Placement = obj.Placement return compound - sub_field = getattr(self, 'sub', None) + sub_field = getattr(self, "sub", None) if not sub_field: self.execute(obj) return self.sub - def computeAreas(self, obj): - '''computes border and ridge roof edges length''' + """computes border and ridge roof edges length""" if hasattr(obj, "RidgeLength") and hasattr(obj, "BorderLength"): rl = 0 bl = 0 @@ -833,7 +909,7 @@ class _Roof(ArchComponent.Component): except Exception: pass else: - lut={} + lut = {} if shell.Faces: for face in shell.Faces: for edge in face.Edges: @@ -851,15 +927,16 @@ class _Roof(ArchComponent.Component): rn += 1 if obj.RidgeLength.Value != rl: obj.RidgeLength = rl - #print(str(rn)+" ridge edges in roof "+obj.Name) + # print(str(rn)+" ridge edges in roof "+obj.Name) if obj.BorderLength.Value != bl: obj.BorderLength = bl - #print(str(bn)+" border edges in roof "+obj.Name) + # print(str(bn)+" border edges in roof "+obj.Name) ArchComponent.Component.computeAreas(self, obj) class _ViewProviderRoof(ArchComponent.ViewProviderComponent): - '''A View Provider for the Roof object''' + """A View Provider for the Roof object""" + def __init__(self, vobj): ArchComponent.ViewProviderComponent.__init__(self, vobj) @@ -888,7 +965,8 @@ class _ViewProviderRoof(ArchComponent.ViewProviderComponent): class _RoofTaskPanel: - '''The editmode TaskPanel for Roof objects''' + """The editmode TaskPanel for Roof objects""" + def __init__(self): self.updating = False self.obj = None @@ -903,9 +981,9 @@ class _RoofTaskPanel: self.tree = QtGui.QTreeWidget(self.form) self.grid.addWidget(self.tree, 1, 0, 1, 1) self.tree.setItemDelegate(_RoofTaskPanel_Delegate()) - self.tree.setRootIsDecorated(False) # remove 1st column's extra left margin + self.tree.setRootIsDecorated(False) # remove 1st column's extra left margin self.tree.setColumnCount(7) - self.tree.header().resizeSection(0, 37) # 37px seems to be the minimum size + self.tree.header().resizeSection(0, 37) # 37px seems to be the minimum size self.tree.header().resizeSection(1, 60) self.tree.header().resizeSection(2, 90) self.tree.header().resizeSection(3, 37) @@ -913,7 +991,9 @@ class _RoofTaskPanel: self.tree.header().resizeSection(5, 90) self.tree.header().resizeSection(6, 90) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit) + QtCore.QObject.connect( + self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit + ) self.update() def isAllowedAlterSelection(self): @@ -926,7 +1006,7 @@ class _RoofTaskPanel: return QtGui.QDialogButtonBox.Close def update(self): - '''fills the treewidget''' + """fills the treewidget""" self.updating = True if self.obj: root = self.tree.invisibleRootItem() @@ -956,7 +1036,7 @@ class _RoofTaskPanel: setattr(self.obj, prop, val_list) def edit(self, item, column): - '''transfers an edited value from the widget to the object''' + """transfers an edited value from the widget to the object""" if self.updating: return row = int(item.text(0)) @@ -986,19 +1066,31 @@ class _RoofTaskPanel: def retranslateUi(self, TaskPanel): TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Roof", None)) - self.title.setText(QtGui.QApplication.translate("Arch", "Parameters of the roof profiles:\n* Angle: slope in degrees relative to the horizontal.\n* Run: horizontal distance between the wall and the ridge.\n* IdRel: Id of the relative profile used for automatic calculations.\n* Thickness: thickness of the roof.\n* Overhang: horizontal distance between the eave and the wall.\n* Height: height of the ridge above the base (calculated automatically).\n---\nIf Angle = 0 and Run = 0 then the profile is identical to the relative profile.\nIf Angle = 0 then the angle is calculated so that the height is the same as the relative profile.\nIf Run = 0 then the run is calculated so that the height is the same as the relative profile.", None)) - self.tree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Id", None), - QtGui.QApplication.translate("Arch", "Angle", None), - QtGui.QApplication.translate("Arch", "Run", None), - QtGui.QApplication.translate("Arch", "IdRel", None), - QtGui.QApplication.translate("Arch", "Thickness", None), - QtGui.QApplication.translate("Arch", "Overhang", None), - QtGui.QApplication.translate("Arch", "Height", None)]) + self.title.setText( + QtGui.QApplication.translate( + "Arch", + "Parameters of the roof profiles:\n* Angle: slope in degrees relative to the horizontal.\n* Run: horizontal distance between the wall and the ridge.\n* IdRel: Id of the relative profile used for automatic calculations.\n* Thickness: thickness of the roof.\n* Overhang: horizontal distance between the eave and the wall.\n* Height: height of the ridge above the base (calculated automatically).\n---\nIf Angle = 0 and Run = 0 then the profile is identical to the relative profile.\nIf Angle = 0 then the angle is calculated so that the height is the same as the relative profile.\nIf Run = 0 then the run is calculated so that the height is the same as the relative profile.", + None, + ) + ) + self.tree.setHeaderLabels( + [ + QtGui.QApplication.translate("Arch", "Id", None), + QtGui.QApplication.translate("Arch", "Angle", None), + QtGui.QApplication.translate("Arch", "Run", None), + QtGui.QApplication.translate("Arch", "IdRel", None), + QtGui.QApplication.translate("Arch", "Thickness", None), + QtGui.QApplication.translate("Arch", "Overhang", None), + QtGui.QApplication.translate("Arch", "Height", None), + ] + ) if FreeCAD.GuiUp: + class _RoofTaskPanel_Delegate(QtWidgets.QStyledItemDelegate): - '''Model delegate''' + """Model delegate""" + def createEditor(self, parent, option, index): if index.column() in (0, 6): # Make these columns read-only. @@ -1012,7 +1104,7 @@ if FreeCAD.GuiUp: editor.setText(index.data()) def setModelData(self, editor, model, index): - model.setData(index,editor.text()) + model.setData(index, editor.text()) def eventFilter(self, widget, event): if event.type() == QtCore.QEvent.FocusIn: diff --git a/src/Mod/BIM/ArchSchedule.py b/src/Mod/BIM/ArchSchedule.py index a52f5761b6..71a5c45df7 100644 --- a/src/Mod/BIM/ArchSchedule.py +++ b/src/Mod/BIM/ArchSchedule.py @@ -45,19 +45,20 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") -VERBOSE = True # change this for silent recomputes +VERBOSE = True # change this for silent recomputes class _ArchScheduleDocObserver: - "doc observer to monitor all recomputes" # https://forum.freecad.org/viewtopic.php?style=3&p=553377#p553377 @@ -76,16 +77,15 @@ class _ArchScheduleDocObserver: class _ArchSchedule: - "the Arch Schedule object" - def __init__(self,obj): + def __init__(self, obj): self.setProperties(obj) obj.Proxy = self self.Type = "Schedule" - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) if hasattr(obj, "Result"): @@ -93,8 +93,9 @@ class _ArchSchedule: if hasattr(obj, "Description"): self.update_properties_1v1(obj) - def update_properties_0v21(self,obj): + def update_properties_0v21(self, obj): from draftutils.messages import _log + sp = obj.Result if sp is not None: self.setSchedulePropertySpreadsheet(sp, obj) @@ -103,37 +104,103 @@ class _ArchSchedule: if sp is not None: _log("v0.21, " + sp.Name + ", added property 'Schedule'") - def update_properties_1v1(self,obj): + def update_properties_1v1(self, obj): from draftutils.messages import _log + if obj.getTypeIdOfProperty("Description") == "App::PropertyStringList": obj.Operation = obj.Description obj.removeProperty("Description") _log("v1.1, " + obj.Name + ", renamed property 'Description' to 'Operation'") - for prop in ("Operation", "Value", "Unit", "Objects", "Filter", "CreateSpreadsheet", "DetailedResults"): - obj.setGroupOfProperty(prop,"Schedule") + for prop in ( + "Operation", + "Value", + "Unit", + "Objects", + "Filter", + "CreateSpreadsheet", + "DetailedResults", + ): + obj.setGroupOfProperty(prop, "Schedule") - def setProperties(self,obj): + def setProperties(self, obj): if not "Operation" in obj.PropertiesList: - obj.addProperty("App::PropertyStringList","Operation", "Schedule",QT_TRANSLATE_NOOP("App::Property","The operation column"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Operation", + "Schedule", + QT_TRANSLATE_NOOP("App::Property", "The operation column"), + locked=True, + ) if not "Value" in obj.PropertiesList: - obj.addProperty("App::PropertyStringList","Value", "Schedule",QT_TRANSLATE_NOOP("App::Property","The values column"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Value", + "Schedule", + QT_TRANSLATE_NOOP("App::Property", "The values column"), + locked=True, + ) if not "Unit" in obj.PropertiesList: - obj.addProperty("App::PropertyStringList","Unit", "Schedule",QT_TRANSLATE_NOOP("App::Property","The units column"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Unit", + "Schedule", + QT_TRANSLATE_NOOP("App::Property", "The units column"), + locked=True, + ) if not "Objects" in obj.PropertiesList: - obj.addProperty("App::PropertyStringList","Objects", "Schedule",QT_TRANSLATE_NOOP("App::Property","The objects column"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Objects", + "Schedule", + QT_TRANSLATE_NOOP("App::Property", "The objects column"), + locked=True, + ) if not "Filter" in obj.PropertiesList: - obj.addProperty("App::PropertyStringList","Filter", "Schedule",QT_TRANSLATE_NOOP("App::Property","The filter column"), locked=True) + obj.addProperty( + "App::PropertyStringList", + "Filter", + "Schedule", + QT_TRANSLATE_NOOP("App::Property", "The filter column"), + locked=True, + ) if not "CreateSpreadsheet" in obj.PropertiesList: - obj.addProperty("App::PropertyBool", "CreateSpreadsheet", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, a spreadsheet containing the results is recreated when needed"), locked=True) + obj.addProperty( + "App::PropertyBool", + "CreateSpreadsheet", + "Schedule", + QT_TRANSLATE_NOOP( + "App::Property", + "If True, a spreadsheet containing the results is recreated when needed", + ), + locked=True, + ) if not "DetailedResults" in obj.PropertiesList: - obj.addProperty("App::PropertyBool", "DetailedResults", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results"), locked=True) + obj.addProperty( + "App::PropertyBool", + "DetailedResults", + "Schedule", + QT_TRANSLATE_NOOP( + "App::Property", + "If True, additional lines with each individual object are added to the results", + ), + locked=True, + ) if not "AutoUpdate" in obj.PropertiesList: - obj.addProperty("App::PropertyBool", "AutoUpdate", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed"), locked=True) + obj.addProperty( + "App::PropertyBool", + "AutoUpdate", + "Schedule", + QT_TRANSLATE_NOOP( + "App::Property", + "If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed", + ), + locked=True, + ) obj.AutoUpdate = True # To add the doc observer: - self.onChanged(obj,"AutoUpdate") + self.onChanged(obj, "AutoUpdate") def setSchedulePropertySpreadsheet(self, sp, obj): if not hasattr(sp, "Schedule"): @@ -142,25 +209,26 @@ class _ArchSchedule: "Schedule", "Arch", QT_TRANSLATE_NOOP("App::Property", "The BIM Schedule that uses this spreadsheet"), - locked=True) + locked=True, + ) sp.Schedule = obj def getSpreadSheet(self, obj, force=False): - """Get the spreadsheet and store it in self.spreadsheet. If force is True the spreadsheet is created if required. """ - try: # Required as self.spreadsheet may get deleted. - if getattr(self, "spreadsheet", None) is not None \ - and getattr(self.spreadsheet, "Schedule", None) == obj: + try: # Required as self.spreadsheet may get deleted. + if ( + getattr(self, "spreadsheet", None) is not None + and getattr(self.spreadsheet, "Schedule", None) == obj + ): return self.spreadsheet except: pass else: for o in FreeCAD.ActiveDocument.Objects: - if o.TypeId == "Spreadsheet::Sheet" \ - and getattr(o, "Schedule", None) == obj: + if o.TypeId == "Spreadsheet::Sheet" and getattr(o, "Schedule", None) == obj: self.spreadsheet = o return self.spreadsheet if force: @@ -170,7 +238,7 @@ class _ArchSchedule: else: return None - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): if prop == "CreateSpreadsheet": if obj.CreateSpreadsheet: @@ -189,13 +257,12 @@ class _ArchSchedule: FreeCAD.removeDocumentObserver(self.docObserver) self.docObserver = None - def setSpreadsheetData(self,obj,force=False): - + def setSpreadsheetData(self, obj, force=False): """Fills a spreadsheet with the stored data""" - if not hasattr(self,"data"): + if not hasattr(self, "data"): self.execute(obj) - if not hasattr(self,"data"): + if not hasattr(self, "data"): return if not self.data: return @@ -213,29 +280,29 @@ class _ArchSchedule: sp.set("C1", "Unit") sp.setStyle("A1:C1", "bold", "add") # write contents - for k,v in self.data.items(): - sp.set(k,v) + for k, v in self.data.items(): + sp.set(k, v) # recompute sp.recompute() - sp.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. - for o in sp.InList: # Also recompute TechDraw views. + sp.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. + for o in sp.InList: # Also recompute TechDraw views. o.TypeId == "TechDraw::DrawViewSpreadsheet" o.recompute() - def execute(self,obj): + def execute(self, obj): # verify the data if not obj.Operation: # empty description column return - for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]: + for p in [obj.Value, obj.Unit, obj.Objects, obj.Filter]: # different number of items in each column if len(obj.Operation) != len(p): return - self.data = {} # store all results in self.data, so it lives even without spreadsheet - self.li = 1 # row index - starts at 2 to leave 2 blank rows for the title + self.data = {} # store all results in self.data, so it lives even without spreadsheet + self.li = 1 # row index - starts at 2 to leave 2 blank rows for the title for i in range(len(obj.Operation)): self.li += 1 @@ -243,12 +310,12 @@ class _ArchSchedule: # blank line continue # write description - self.data["A"+str(self.li)] = obj.Operation[i] + self.data["A" + str(self.li)] = obj.Operation[i] if VERBOSE: - l= "OPERATION: "+obj.Operation[i] + l = "OPERATION: " + obj.Operation[i] print("") - print (l) - print (len(l)*"=") + print(l) + print(len(l) * "=") # build set of valid objects @@ -261,6 +328,7 @@ class _ArchSchedule: if val: import Draft import Arch + if objs: objs = objs.split(";") objs = [FreeCAD.ActiveDocument.getObject(o) for o in objs] @@ -272,6 +340,7 @@ class _ArchSchedule: if len(objs) == 1: if hasattr(objs[0], "StepId"): from nativeifc import ifc_tools + ifcfile = ifc_tools.get_ifcfile(objs[0]) # remove object itself if the object is a group if objs[0].isDerivedFrom("App::DocumentObjectGroup"): @@ -282,7 +351,9 @@ class _ArchSchedule: # base geometry, etc) objs = Arch.pruneIncluded(objs, strict=True, silent=True) # Remove all schedules and spreadsheets: - objs = [o for o in objs if Draft.get_type(o) not in ["Schedule", "Spreadsheet::Sheet"]] + objs = [ + o for o in objs if Draft.get_type(o) not in ["Schedule", "Spreadsheet::Sheet"] + ] # filter elements @@ -324,14 +395,14 @@ class _ArchSchedule: if inv: if prop in props: csprop = o.PropertiesList[props.index(prop)] - if fval in getattr(o,csprop).upper(): + if fval in getattr(o, csprop).upper(): ok = False else: if not (prop in props): ok = False else: csprop = o.PropertiesList[props.index(prop)] - if not (fval in getattr(o,csprop).upper()): + if not (fval in getattr(o, csprop).upper()): ok = False if ok: nobjs.append(o) @@ -358,7 +429,7 @@ class _ArchSchedule: if prop == "is_a": if not fval.upper().startswith("IFC"): fval = "Ifc" + fval - fval = fval.replace(" ","") + fval = fval.replace(" ", "") if el.is_a(fval): ok = False else: @@ -373,7 +444,7 @@ class _ArchSchedule: if prop == "is_a": if not fval.upper().startswith("IFC"): fval = "Ifc" + fval - fval = fval.replace(" ","") + fval = fval.replace(" ", "") if not el.is_a(fval): ok = False else: @@ -396,12 +467,12 @@ class _ArchSchedule: if val.upper() == "COUNT": val = len(objs) if VERBOSE: - print (val, ",".join([o.Label for o in objs])) - self.data["B"+str(self.li)] = str(val) + print(val, ",".join([o.Label for o in objs])) + self.data["B" + str(self.li)] = str(val) if details: # additional blank line... self.li += 1 - self.data["A"+str(self.li)] = " " + self.data["A" + str(self.li)] = " " else: vals = val.split(".") if vals[0][0].islower(): @@ -414,11 +485,11 @@ class _ArchSchedule: unit = None q = None if unit: - unit = unit.replace("^","") # get rid of existing power symbol - unit = unit.replace("2","^2") - unit = unit.replace("3","^3") - unit = unit.replace("²","^2") - unit = unit.replace("³","^3") + unit = unit.replace("^", "") # get rid of existing power symbol + unit = unit.replace("2", "^2") + unit = unit.replace("3", "^3") + unit = unit.replace("²", "^2") + unit = unit.replace("³", "^3") if "2" in unit: tp = FreeCAD.Units.Area elif "3" in unit: @@ -429,28 +500,28 @@ class _ArchSchedule: tp = FreeCAD.Units.Length # format value - dv = params.get_param("Decimals",path="Units") - fs = "{:."+str(dv)+"f}" # format string + dv = params.get_param("Decimals", path="Units") + fs = "{:." + str(dv) + "f}" # format string for o in objs: if VERBOSE: - l = o.Name+" ("+o.Label+"):" - print (l+(40-len(l))*" ",end="") + l = o.Name + " (" + o.Label + "):" + print(l + (40 - len(l)) * " ", end="") try: d = o for v in vals: - d = getattr(d,v) - if hasattr(d,"Value"): + d = getattr(d, v) + if hasattr(d, "Value"): d = d.Value except Exception: - t = translate("Arch","Unable to retrieve value from object") - FreeCAD.Console.PrintWarning(t+": "+o.Name+"."+".".join(vals)+"\n") + t = translate("Arch", "Unable to retrieve value from object") + FreeCAD.Console.PrintWarning(t + ": " + o.Name + "." + ".".join(vals) + "\n") else: if VERBOSE: if tp and unit: - v = fs.format(FreeCAD.Units.Quantity(d,tp).getValueAs(unit).Value) - print(v,unit) + v = fs.format(FreeCAD.Units.Quantity(d, tp).getValueAs(unit).Value) + print(v, unit) elif isinstance(d, str): - if d.replace('.', '', 1).isdigit(): + if d.replace(".", "", 1).isdigit(): print(fs.format(d)) else: print(d) @@ -458,13 +529,13 @@ class _ArchSchedule: print(fs.format(d)) if details: self.li += 1 - self.data["A"+str(self.li)] = o.Name+" ("+o.Label+")" + self.data["A" + str(self.li)] = o.Name + " (" + o.Label + ")" if tp and unit: - q = FreeCAD.Units.Quantity(d,tp) - self.data["B"+str(self.li)] = str(q.getValueAs(unit).Value) - self.data["C"+str(self.li)] = unit + q = FreeCAD.Units.Quantity(d, tp) + self.data["B" + str(self.li)] = str(q.getValueAs(unit).Value) + self.data["C" + str(self.li)] = unit else: - self.data["B"+str(self.li)] = str(d) + self.data["B" + str(self.li)] = str(d) if sumval: sumval += d @@ -472,30 +543,30 @@ class _ArchSchedule: sumval = d val = sumval if tp: - q = FreeCAD.Units.Quantity(val,tp) + q = FreeCAD.Units.Quantity(val, tp) # write data if details: self.li += 1 - self.data["A"+str(self.li)] = "TOTAL" + self.data["A" + str(self.li)] = "TOTAL" if q and unit: - self.data["B"+str(self.li)] = str(q.getValueAs(unit).Value) - self.data["C"+str(self.li)] = unit + self.data["B" + str(self.li)] = str(q.getValueAs(unit).Value) + self.data["C" + str(self.li)] = unit else: - self.data["B"+str(self.li)] = str(val) + self.data["B" + str(self.li)] = str(val) if VERBOSE: if tp and unit: - v = fs.format(FreeCAD.Units.Quantity(val,tp).getValueAs(unit).Value) - print("TOTAL:"+34*" "+v+" "+unit) + v = fs.format(FreeCAD.Units.Quantity(val, tp).getValueAs(unit).Value) + print("TOTAL:" + 34 * " " + v + " " + unit) elif isinstance(val, str): - if val.replace('.', '', 1).isdigit(): + if val.replace(".", "", 1).isdigit(): v = fs.format(val) - print("TOTAL:"+34*" "+v) + print("TOTAL:" + 34 * " " + v) else: - print("TOTAL:"+34*" "+val) + print("TOTAL:" + 34 * " " + val) else: v = fs.format(val) - print("TOTAL:"+34*" "+v) + print("TOTAL:" + 34 * " " + v) def update_from_elts(self, elts, val, unit, details): """Updates the spreadsheet data from IFC elements""" @@ -503,12 +574,12 @@ class _ArchSchedule: if val.upper() == "COUNT": val = len(elts) if VERBOSE: - print ("COUNT:", val, "(", ",".join(["#"+str(e.id()) for e in elts]), ")") - self.data["B"+str(self.li)] = str(val) + print("COUNT:", val, "(", ",".join(["#" + str(e.id()) for e in elts]), ")") + self.data["B" + str(self.li)] = str(val) if details: # additional blank line... self.li += 1 - self.data["A"+str(self.li)] = " " + self.data["A" + str(self.li)] = " " else: total = 0 for el in elts: @@ -533,21 +604,21 @@ class _ArchSchedule: if details: self.li += 1 name = el.Name if el.Name else "" - self.data["A"+str(self.li)] = "#" + str(el.id()) + name - self.data["B"+str(self.li)] = str(elval) + self.data["A" + str(self.li)] = "#" + str(el.id()) + name + self.data["B" + str(self.li)] = str(elval) if VERBOSE: - print("#"+str(el.id())+"."+val+" = "+str(elval)) - if isinstance(elval, str) and elval.replace('.', '', 1).isdigit(): + print("#" + str(el.id()) + "." + val + " = " + str(elval)) + if isinstance(elval, str) and elval.replace(".", "", 1).isdigit(): total += float(elval) elif isinstance(elval, (int, float)): total += elval if total: if details: self.li += 1 - self.data["A"+str(self.li)] = "TOTAL" - self.data["B"+str(self.li)] = str(total) + self.data["A" + str(self.li)] = "TOTAL" + self.data["B" + str(self.li)] = str(total) if VERBOSE: - print("TOTAL:",str(total)) + print("TOTAL:", str(total)) def create_ifc(self, obj, ifcfile, export=False): """Creates an IFC element for this object""" @@ -557,7 +628,9 @@ class _ArchSchedule: proj = ifcfile.by_type("IfcProject")[0] elt = ifc_tools.api_run("root.create_entity", ifcfile, ifc_class="IfcControl") ifc_tools.set_attribute(ifcfile, elt, "Name", obj.Label) - ifc_tools.api_run("project.assign_declaration", ifcfile, definitions=[elt], relating_context=proj) + ifc_tools.api_run( + "project.assign_declaration", ifcfile, definitions=[elt], relating_context=proj + ) if not export: ifc_tools.add_properties(obj, ifcfile, elt) return elt @@ -567,13 +640,15 @@ class _ArchSchedule: from nativeifc import ifc_psets # lazy loading - ifc_psets.edit_pset(obj, "Operation", "::".join(obj.Operation), ifcfile=ifcfile, element=elt) + ifc_psets.edit_pset( + obj, "Operation", "::".join(obj.Operation), ifcfile=ifcfile, element=elt + ) ifc_psets.edit_pset(obj, "Value", "::".join(obj.Value), ifcfile=ifcfile, element=elt) ifc_psets.edit_pset(obj, "Unit", "::".join(obj.Unit), ifcfile=ifcfile, element=elt) ifc_psets.edit_pset(obj, "Objects", "::".join(obj.Objects), ifcfile=ifcfile, element=elt) ifc_psets.edit_pset(obj, "Filter", "::".join(obj.Filter), ifcfile=ifcfile, element=elt) - def export_ifc(self, obj, ifcfile): + def export_ifc(self, obj, ifcfile): """Exports the object to IFC (does not modify the FreeCAD object).""" elt = self.create_ifc(obj, ifcfile, export=True) @@ -584,7 +659,7 @@ class _ArchSchedule: return self.Type - def loads(self,state): + def loads(self, state): if state: self.Type = state @@ -630,23 +705,25 @@ class _ArchSchedule: # If the object is in an array, add it and the rest of its elements # to the list. array = self.getArray(obj) - for i in range(1, array): # The first element (0) was already added + for i in range(1, array): # The first element (0) was already added expandedobjs.append(obj) return expandedobjs -class _ViewProviderArchSchedule: +class _ViewProviderArchSchedule: "A View Provider for Schedules" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self def getIcon(self): if self.Object.AutoUpdate is False: import TechDrawGui + return ":/icons/TechDraw_TreePageUnsync.svg" import Arch_rc + return ":/icons/Arch_Schedule.svg" def isShow(self): @@ -662,6 +739,7 @@ class _ViewProviderArchSchedule: self.taskd = ArchScheduleTaskPanel(vobj.Object) if not self.taskd.form.isVisible(): from PySide import QtCore + QtCore.QTimer.singleShot(100, self.showEditor) return True @@ -682,26 +760,21 @@ class _ViewProviderArchSchedule: def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) if self.Object.CreateSpreadsheet is True: msg = translate("Arch", "Remove spreadsheet") else: msg = translate("Arch", "Attach spreadsheet") - actionToggleSpreadsheet = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Schedule.svg"), - msg, - menu) - QtCore.QObject.connect(actionToggleSpreadsheet, - QtCore.SIGNAL("triggered()"), - self.toggleSpreadsheet) + actionToggleSpreadsheet = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Schedule.svg"), msg, menu) + QtCore.QObject.connect( + actionToggleSpreadsheet, QtCore.SIGNAL("triggered()"), self.toggleSpreadsheet + ) menu.addAction(actionToggleSpreadsheet) def edit(self): @@ -711,31 +784,29 @@ class _ViewProviderArchSchedule: self.Object.CreateSpreadsheet = not self.Object.CreateSpreadsheet def claimChildren(self): - if hasattr(self,"Object"): + if hasattr(self, "Object"): return [self.Object.Proxy.getSpreadSheet(self.Object)] def dumps(self): return None - def loads(self,state): + def loads(self, state): return None - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): return ["Default"] def getDefaultDisplayMode(self): return "Default" - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): return mode class ArchScheduleTaskPanel: + """The editmode TaskPanel for Schedules""" - '''The editmode TaskPanel for Schedules''' - - def __init__(self,obj=None): - + def __init__(self, obj=None): """Sets the panel up""" self.obj = obj @@ -751,20 +822,20 @@ class ArchScheduleTaskPanel: self.form.buttonSelect.setIcon(QtGui.QIcon(":/icons/edit-select-all.svg")) # restore widths - self.form.list.setColumnWidth(0,params.get_param_arch("ScheduleColumnWidth0")) - self.form.list.setColumnWidth(1,params.get_param_arch("ScheduleColumnWidth1")) - self.form.list.setColumnWidth(2,params.get_param_arch("ScheduleColumnWidth2")) - self.form.list.setColumnWidth(3,params.get_param_arch("ScheduleColumnWidth3")) + self.form.list.setColumnWidth(0, params.get_param_arch("ScheduleColumnWidth0")) + self.form.list.setColumnWidth(1, params.get_param_arch("ScheduleColumnWidth1")) + self.form.list.setColumnWidth(2, params.get_param_arch("ScheduleColumnWidth2")) + self.form.list.setColumnWidth(3, params.get_param_arch("ScheduleColumnWidth3")) w = params.get_param_arch("ScheduleDialogWidth") h = params.get_param_arch("ScheduleDialogHeight") - self.form.resize(w,h) + self.form.resize(w, h) # restore default states self.form.checkAutoUpdate.setChecked(PARAMS.GetBool("ScheduleAutoUpdate", False)) # set delegate - Not using custom delegates for now... - #self.form.list.setItemDelegate(ScheduleDelegate()) - #self.form.list.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked) + # self.form.list.setItemDelegate(ScheduleDelegate()) + # self.form.list.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked) # connect slots self.form.buttonAdd.clicked.connect(self.add) @@ -779,18 +850,18 @@ class ArchScheduleTaskPanel: self.form.list.clearContents() if self.obj: - #for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]: + # for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]: # if len(obj.Operation) != len(p): # return self.form.list.setRowCount(len(obj.Operation)) for i in range(5): for j in range(len(obj.Operation)): try: - text = [obj.Operation,obj.Value,obj.Unit,obj.Objects,obj.Filter][i][j] + text = [obj.Operation, obj.Value, obj.Unit, obj.Objects, obj.Filter][i][j] except: text = "" item = QtGui.QTableWidgetItem(text) - self.form.list.setItem(j,i,item) + self.form.list.setItem(j, i, item) self.form.lineEditName.setText(self.obj.Label) self.form.checkSpreadsheet.setChecked(self.obj.CreateSpreadsheet) self.form.checkDetailed.setChecked(self.obj.DetailedResults) @@ -798,75 +869,84 @@ class ArchScheduleTaskPanel: # center over FreeCAD window mw = FreeCADGui.getMainWindow() - self.form.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center()) + self.form.move( + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() + ) self.form.show() def add(self): - """Adds a new row below the last one""" - self.form.list.insertRow(self.form.list.currentRow()+1) + self.form.list.insertRow(self.form.list.currentRow() + 1) def remove(self): - """Removes the current row""" if self.form.list.currentRow() >= 0: self.form.list.removeRow(self.form.list.currentRow()) def clear(self): - """Clears the list""" self.form.list.clearContents() self.form.list.setRowCount(0) def importCSV(self): - """Imports a CSV file""" - filename = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(), translate("Arch","Import CSV file"), None, "CSV files (*.csv *.CSV)") + filename = QtGui.QFileDialog.getOpenFileName( + QtGui.QApplication.activeWindow(), + translate("Arch", "Import CSV file"), + None, + "CSV files (*.csv *.CSV)", + ) if filename: filename = filename[0] self.form.list.clearContents() import csv - with open(filename,'r') as csvfile: + + with open(filename, "r") as csvfile: r = 0 for row in csv.reader(csvfile): self.form.list.insertRow(r) for i in range(5): if len(row) > i: t = row[i] - #t = t.replace("²","^2") - #t = t.replace("³","^3") - self.form.list.setItem(r,i,QtGui.QTableWidgetItem(t)) + # t = t.replace("²","^2") + # t = t.replace("³","^3") + self.form.list.setItem(r, i, QtGui.QTableWidgetItem(t)) r += 1 def export(self): - """Exports the results as MD or CSV""" # commit latest changes self.writeValues() # tests - if not("Up-to-date" in self.obj.State): + if not ("Up-to-date" in self.obj.State): self.obj.Proxy.execute(self.obj) - if not hasattr(self.obj.Proxy,"data"): + if not hasattr(self.obj.Proxy, "data"): return if not self.obj.Proxy.data: return - filename = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), - translate("Arch","Export CSV file"), - None, - "Comma-separated values (*.csv);;TAB-separated values (*.tsv);;Markdown (*.md)"); + filename = QtGui.QFileDialog.getSaveFileName( + QtGui.QApplication.activeWindow(), + translate("Arch", "Export CSV file"), + None, + "Comma-separated values (*.csv);;TAB-separated values (*.tsv);;Markdown (*.md)", + ) if filename: filt = filename[1] filename = filename[0] # add missing extension - if (not filename.lower().endswith(".csv")) and (not filename.lower().endswith(".tsv")) and (not filename.lower().endswith(".md")): + if ( + (not filename.lower().endswith(".csv")) + and (not filename.lower().endswith(".tsv")) + and (not filename.lower().endswith(".md")) + ): if "csv" in filt: filename += ".csv" elif "tsv" in filt: @@ -874,20 +954,21 @@ class ArchScheduleTaskPanel: else: filename += ".md" if filename.lower().endswith(".csv"): - self.exportCSV(filename,delimiter=",") + self.exportCSV(filename, delimiter=",") elif filename.lower().endswith(".tsv"): - self.exportCSV(filename,delimiter="\t") + self.exportCSV(filename, delimiter="\t") elif filename.lower().endswith(".md"): self.exportMD(filename) else: - FreeCAD.Console.PrintError(translate("Arch","Unable to recognize that file type")+":"+filename+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Unable to recognize that file type") + ":" + filename + "\n" + ) def getRows(self): - """get the rows that contain data""" rows = [] - if hasattr(self.obj.Proxy,"data") and self.obj.Proxy.data: + if hasattr(self.obj.Proxy, "data") and self.obj.Proxy.data: for key in self.obj.Proxy.data.keys(): n = key[1:] if not n in rows: @@ -895,47 +976,59 @@ class ArchScheduleTaskPanel: rows.sort(key=int) return rows - def exportCSV(self,filename,delimiter="\t"): - + def exportCSV(self, filename, delimiter="\t"): """Exports the results as a CSV/TSV file""" import csv - with open(filename, 'w') as csvfile: - csvfile = csv.writer(csvfile,delimiter=delimiter) - csvfile.writerow([translate("Arch","Operation"),translate("Arch","Value"),translate("Arch","Unit")]) + + with open(filename, "w") as csvfile: + csvfile = csv.writer(csvfile, delimiter=delimiter) + csvfile.writerow( + [ + translate("Arch", "Operation"), + translate("Arch", "Value"), + translate("Arch", "Unit"), + ] + ) if self.obj.DetailedResults: - csvfile.writerow(["","",""]) + csvfile.writerow(["", "", ""]) for i in self.getRows(): r = [] - for j in ["A","B","C"]: - if j+i in self.obj.Proxy.data: - r.append(str(self.obj.Proxy.data[j+i])) + for j in ["A", "B", "C"]: + if j + i in self.obj.Proxy.data: + r.append(str(self.obj.Proxy.data[j + i])) else: r.append("") csvfile.writerow(r) - print("successfully exported ",filename) - - def exportMD(self,filename): + print("successfully exported ", filename) + def exportMD(self, filename): """Exports the results as a Markdown file""" - with open(filename, 'w') as mdfile: - mdfile.write("| "+translate("Arch","Operation")+" | "+translate("Arch","Value")+" | "+translate("Arch","Unit")+" |\n") + with open(filename, "w") as mdfile: + mdfile.write( + "| " + + translate("Arch", "Operation") + + " | " + + translate("Arch", "Value") + + " | " + + translate("Arch", "Unit") + + " |\n" + ) mdfile.write("| --- | --- | --- |\n") if self.obj.DetailedResults: mdfile.write("| | | |\n") for i in self.getRows(): r = [] - for j in ["A","B","C"]: - if j+i in self.obj.Proxy.data: - r.append(str(self.obj.Proxy.data[j+i])) + for j in ["A", "B", "C"]: + if j + i in self.obj.Proxy.data: + r.append(str(self.obj.Proxy.data[j + i])) else: r.append("") - mdfile.write("| "+" | ".join(r)+" |\n") - print("successfully exported ",filename) + mdfile.write("| " + " | ".join(r) + " |\n") + print("successfully exported ", filename) def select(self): - """Adds selected objects to current row""" if self.form.list.currentRow() >= 0: @@ -946,19 +1039,18 @@ class ArchScheduleTaskPanel: sel += ";" sel += o.Name if sel: - self.form.list.setItem(self.form.list.currentRow(),3,QtGui.QTableWidgetItem(sel)) + self.form.list.setItem(self.form.list.currentRow(), 3, QtGui.QTableWidgetItem(sel)) def accept(self): - """Saves the changes and closes the dialog""" # store widths - params.set_param_arch("ScheduleColumnWidth0",self.form.list.columnWidth(0)) - params.set_param_arch("ScheduleColumnWidth1",self.form.list.columnWidth(1)) - params.set_param_arch("ScheduleColumnWidth2",self.form.list.columnWidth(2)) - params.set_param_arch("ScheduleColumnWidth3",self.form.list.columnWidth(3)) - params.set_param_arch("ScheduleDialogWidth",self.form.width()) - params.set_param_arch("ScheduleDialogHeight",self.form.height()) + params.set_param_arch("ScheduleColumnWidth0", self.form.list.columnWidth(0)) + params.set_param_arch("ScheduleColumnWidth1", self.form.list.columnWidth(1)) + params.set_param_arch("ScheduleColumnWidth2", self.form.list.columnWidth(2)) + params.set_param_arch("ScheduleColumnWidth3", self.form.list.columnWidth(3)) + params.set_param_arch("ScheduleDialogWidth", self.form.width()) + params.set_param_arch("ScheduleDialogHeight", self.form.height()) # store default states PARAMS.SetBool("ScheduleAutoUpdate", self.form.checkAutoUpdate.isChecked()) @@ -970,7 +1062,6 @@ class ArchScheduleTaskPanel: return True def reject(self): - """Close dialog without saving""" self.form.hide() @@ -978,16 +1069,16 @@ class ArchScheduleTaskPanel: return True def writeValues(self): - """commits values and recalculate""" if not self.obj: import Arch + self.obj = Arch.makeSchedule() - lists = [ [], [], [], [], [] ] + lists = [[], [], [], [], []] for i in range(self.form.list.rowCount()): for j in range(5): - cell = self.form.list.item(i,j) + cell = self.form.list.item(i, j) if cell: lists[j].append(cell.text()) else: diff --git a/src/Mod/BIM/ArchSectionPlane.py b/src/Mod/BIM/ArchSectionPlane.py index 880a64e581..6b1c2a2c60 100644 --- a/src/Mod/BIM/ArchSectionPlane.py +++ b/src/Mod/BIM/ArchSectionPlane.py @@ -53,46 +53,49 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -ISRENDERING = False # flag to prevent concurrent runs of the coin renderer +ISRENDERING = False # flag to prevent concurrent runs of the coin renderer def getSectionData(source): - """Returns some common data from section planes and building parts""" - if hasattr(source,"Objects"): + if hasattr(source, "Objects"): objs = source.Objects cutplane = source.Shape - elif hasattr(source,"Group"): + elif hasattr(source, "Group"): import Part + objs = source.Group - cutplane = Part.makePlane(1000,1000,FreeCAD.Vector(-500,-500,0)) + cutplane = Part.makePlane(1000, 1000, FreeCAD.Vector(-500, -500, 0)) m = 1 - if source.ViewObject and hasattr(source.ViewObject,"CutMargin"): + if source.ViewObject and hasattr(source.ViewObject, "CutMargin"): m = source.ViewObject.CutMargin.Value - cutplane.translate(FreeCAD.Vector(0,0,m)) + cutplane.translate(FreeCAD.Vector(0, 0, m)) cutplane.Placement = cutplane.Placement.multiply(source.Placement) onlySolids = True - if hasattr(source,"OnlySolids"): + if hasattr(source, "OnlySolids"): onlySolids = source.OnlySolids clip = False - if hasattr(source,"Clip"): + if hasattr(source, "Clip"): clip = source.Clip p = FreeCAD.Placement(source.Placement) - direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) + direction = p.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) if objs: objs = Draft.get_group_contents(objs, walls=True, addgroups=True) - return objs,cutplane,onlySolids,clip,direction + return objs, cutplane, onlySolids, clip, direction -def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesByObject=False): - +def getCutShapes( + objs, cutplane, onlySolids, clip, joinArch, showHidden, groupSshapesByObject=False +): """ returns a list of shapes (visible, hidden, cut lines...) obtained from performing a series of booleans against the given cut plane @@ -100,6 +103,7 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB import Part import DraftGeomUtils + shapes = [] hshapes = [] sshapes = [] @@ -109,14 +113,18 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB if joinArch: shtypes = {} for o in objs: - if Draft.getType(o) in ["Wall","Structure"]: + if Draft.getType(o) in ["Wall", "Structure"]: if o.Shape.isNull(): pass elif onlySolids: - shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).extend(o.Shape.Solids) + shtypes.setdefault( + o.Material.Name if (hasattr(o, "Material") and o.Material) else "None", [] + ).extend(o.Shape.Solids) else: - shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).append(o.Shape.copy()) - elif hasattr(o,'Shape'): + shtypes.setdefault( + o.Material.Name if (hasattr(o, "Material") and o.Material) else "None", [] + ).append(o.Shape.copy()) + elif hasattr(o, "Shape"): if o.Shape.isNull(): pass elif onlySolids: @@ -124,33 +132,33 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB objectShapes.append((o, o.Shape.Solids)) else: shapes.append(o.Shape.copy()) - objectShapes.append((o,[o.Shape.copy()])) - for k,v in shtypes.items(): + objectShapes.append((o, [o.Shape.copy()])) + for k, v in shtypes.items(): v1 = v.pop() if v: v1 = v1.multiFuse(v) v1 = v1.removeSplitter() if v1.Solids: shapes.extend(v1.Solids) - objectShapes.append((k,v1.Solids)) + objectShapes.append((k, v1.Solids)) else: print("ArchSectionPlane: Fusing Arch objects produced non-solid results") shapes.append(v1) - objectShapes.append((k,[v1])) + objectShapes.append((k, [v1])) else: for o in objs: - if hasattr(o,'Shape'): + if hasattr(o, "Shape"): if o.Shape.isNull(): pass elif onlySolids: if o.Shape.isValid(): shapes.extend(o.Shape.Solids) - objectShapes.append((o,o.Shape.Solids)) + objectShapes.append((o, o.Shape.Solids)) else: shapes.append(o.Shape) - objectShapes.append((o,[o.Shape])) + objectShapes.append((o, [o.Shape])) - cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(cutplane,shapes,clip) + cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume(cutplane, shapes, clip) shapes = [] for o, shapeList in objectShapes: tmpSshapes = [] @@ -158,7 +166,7 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB for sub in (sh.SubShapes if sh.ShapeType == "Compound" else [sh]): if cutvolume: if sub.Volume < 0: - sub = sub.reversed() # Use reversed as sub is immutable. + sub = sub.reversed() # Use reversed as sub is immutable. c = sub.cut(cutvolume) s = sub.section(cutface) try: @@ -167,7 +175,7 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB f = Part.Face(w) tmpSshapes.append(f) except Part.OCCError: - #print "ArchView: unable to get a face" + # print "ArchView: unable to get a face" tmpSshapes.append(s) shapes.extend(c.SubShapes if c.ShapeType == "Compound" else [c]) if showHidden: @@ -183,91 +191,96 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB objectSshapes.append((o, tmpSshapes)) if groupSshapesByObject: - return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes + return shapes, hshapes, sshapes, cutface, cutvolume, invcutvolume, objectSshapes else: - return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume + return shapes, hshapes, sshapes, cutface, cutvolume, invcutvolume def getFillForObject(o, defaultFill, source): - """returns a color tuple from an object's material""" - if hasattr(source, 'UseMaterialColorForFill') and source.UseMaterialColorForFill: + if hasattr(source, "UseMaterialColorForFill") and source.UseMaterialColorForFill: material = None - if hasattr(o, 'Material') and o.Material: + if hasattr(o, "Material") and o.Material: material = o.Material - elif isinstance(o,str): + elif isinstance(o, str): material = FreeCAD.ActiveDocument.getObject(o) if material: - if hasattr(material, 'SectionColor') and material.SectionColor: + if hasattr(material, "SectionColor") and material.SectionColor: return material.SectionColor - elif hasattr(material, 'Color') and material.Color: + elif hasattr(material, "Color") and material.Color: return material.Color - elif hasattr(o,"ViewObject") and hasattr(o.ViewObject,"ShapeColor"): + elif hasattr(o, "ViewObject") and hasattr(o.ViewObject, "ShapeColor"): return o.ViewObject.ShapeColor return defaultFill -def isOriented(obj,plane): - +def isOriented(obj, plane): """determines if an annotation is facing the cutplane or not""" - norm1 = plane.normalAt(0,0) - if hasattr(obj,"Placement"): - norm2 = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) - elif hasattr(obj,"Normal"): + norm1 = plane.normalAt(0, 0) + if hasattr(obj, "Placement"): + norm2 = obj.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) + elif hasattr(obj, "Normal"): norm2 = obj.Normal if norm2.Length < 0.01: return True else: return True a = norm1.getAngle(norm2) - if (a < 0.01) or (abs(a-math.pi) < 0.01): + if (a < 0.01) or (abs(a - math.pi) < 0.01): return True return False + def update_svg_cache(source, renderMode, showHidden, showFill, fillSpaces, joinArch, allOn, objs): """ Returns None or cached SVG, clears shape cache if required """ svgcache = None - if hasattr(source,"Proxy"): - if hasattr(source.Proxy,"svgcache") and source.Proxy.svgcache: + if hasattr(source, "Proxy"): + if hasattr(source.Proxy, "svgcache") and source.Proxy.svgcache: # TODO check array bounds svgcache = source.Proxy.svgcache[0] # empty caches if we want to force-recalculate for certain properties - if (source.Proxy.svgcache[1] != renderMode - or source.Proxy.svgcache[2] != showHidden - or source.Proxy.svgcache[3] != showFill - or source.Proxy.svgcache[4] != fillSpaces - or source.Proxy.svgcache[5] != joinArch - or source.Proxy.svgcache[6] != allOn - or source.Proxy.svgcache[7] != set(objs)): + if ( + source.Proxy.svgcache[1] != renderMode + or source.Proxy.svgcache[2] != showHidden + or source.Proxy.svgcache[3] != showFill + or source.Proxy.svgcache[4] != fillSpaces + or source.Proxy.svgcache[5] != joinArch + or source.Proxy.svgcache[6] != allOn + or source.Proxy.svgcache[7] != set(objs) + ): svgcache = None - if (source.Proxy.svgcache[4] != fillSpaces - or source.Proxy.svgcache[5] != joinArch - or source.Proxy.svgcache[6] != allOn - or source.Proxy.svgcache[7] != set(objs)): + if ( + source.Proxy.svgcache[4] != fillSpaces + or source.Proxy.svgcache[5] != joinArch + or source.Proxy.svgcache[6] != allOn + or source.Proxy.svgcache[7] != set(objs) + ): source.Proxy.shapecache = None return svgcache -def getSVG(source, - renderMode="Wireframe", - allOn=False, - showHidden=False, - scale=1, - rotation=0, - linewidth=1, - lineColor=(0.0, 0.0, 0.0), - fontsize=1, - linespacing=None, - showFill=False, - fillColor=(1.0, 1.0, 1.0), - techdraw=False, - fillSpaces=False, - cutlinewidth=0, - joinArch=False): +def getSVG( + source, + renderMode="Wireframe", + allOn=False, + showHidden=False, + scale=1, + rotation=0, + linewidth=1, + lineColor=(0.0, 0.0, 0.0), + fontsize=1, + linespacing=None, + showFill=False, + fillColor=(1.0, 1.0, 1.0), + techdraw=False, + fillSpaces=False, + cutlinewidth=0, + joinArch=False, +): """ Return an SVG fragment from an Arch SectionPlane or BuildingPart. @@ -307,14 +320,23 @@ def getSVG(source, # separate spaces and Draft objects spaces = [] nonspaces = [] - drafts = [] # Only used for annotations. + drafts = [] # Only used for annotations. windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) - elif Draft.getType(o) in ["Dimension","AngularDimension","LinearDimension","Annotation","Label","Text","DraftText","Axis"]: - if isOriented(o,cutplane): + elif Draft.getType(o) in [ + "Dimension", + "AngularDimension", + "LinearDimension", + "Annotation", + "Label", + "Text", + "DraftText", + "Axis", + ]: + if isOriented(o, cutplane): drafts.append(o) elif o.isDerivedFrom("App::DocumentObjectGroup"): # These will have been expanded by getSectionData already @@ -325,85 +347,94 @@ def getSVG(source, windows.append(o) objs = nonspaces - scaledLineWidth = linewidth/scale - if renderMode in ["Coin",2,"Coin mono",3]: + scaledLineWidth = linewidth / scale + if renderMode in ["Coin", 2, "Coin mono", 3]: # don't scale linewidths in coin mode - svgLineWidth = str(linewidth) + 'px' + svgLineWidth = str(linewidth) + "px" else: - svgLineWidth = str(scaledLineWidth) + 'px' + svgLineWidth = str(scaledLineWidth) + "px" if cutlinewidth: - scaledCutLineWidth = cutlinewidth/scale - svgCutLineWidth = str(scaledCutLineWidth) + 'px' + scaledCutLineWidth = cutlinewidth / scale + svgCutLineWidth = str(scaledCutLineWidth) + "px" else: st = params.get_param_arch("CutLineThickness") - svgCutLineWidth = str(scaledLineWidth * st) + 'px' + svgCutLineWidth = str(scaledLineWidth * st) + "px" yt = params.get_param_arch("SymbolLineThickness") svgSymbolLineWidth = str(linewidth * yt) hiddenPattern = params.get_param_arch("archHiddenPattern") - svgHiddenPattern = hiddenPattern.replace(" ","") - #fillpattern = 'bool: +def BoundBoxValid(boundBox) -> bool: """Return true if boundBox has a non-zero volume""" return boundBox.XLength > 0 and boundBox.YLength > 0 and boundBox.ZLength > 0 @@ -569,36 +650,37 @@ def getDXF(obj): result = [] import TechDraw import Part + if not obj.Source: return result source = obj.Source - objs,cutplane,onlySolids,clip,direction = getSectionData(source) + objs, cutplane, onlySolids, clip, direction = getSectionData(source) if not objs: return result if not allOn: - objs = Draft.removeHidden(objs) + objs = Draft.removeHidden(objs) objs = [ obj for obj in objs if ( not obj.isDerivedFrom("Part::Part2DObject") - and Draft.getType(obj) not in [ - "BezCurve", "BSpline", "Wire", "Annotation", "Dimension", "Space" - ] + and Draft.getType(obj) + not in ["BezCurve", "BSpline", "Wire", "Annotation", "Dimension", "Space"] ) ] - vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,cutplane,onlySolids,clip,False,showHidden) + vshapes, hshapes, sshapes, cutface, cutvolume, invcutvolume = getCutShapes( + objs, cutplane, onlySolids, clip, False, showHidden + ) if vshapes: - result.append(TechDraw.projectToDXF(Part.makeCompound(vshapes),direction)) + result.append(TechDraw.projectToDXF(Part.makeCompound(vshapes), direction)) if sshapes: - result.append(TechDraw.projectToDXF(Part.makeCompound(sshapes),direction)) + result.append(TechDraw.projectToDXF(Part.makeCompound(sshapes), direction)) if hshapes: - result.append(TechDraw.projectToDXF(Part.makeCompound(hshapes),direction)) + result.append(TechDraw.projectToDXF(Part.makeCompound(hshapes), direction)) return result def getCameraData(floatlist): - """reconstructs a valid camera data string from stored values""" c = "" @@ -613,7 +695,17 @@ def getCameraData(floatlist): else: c = "#Inventor V2.1 ascii\n\n\nPerspectiveCamera {\n viewportMapping ADJUST_CAMERA\n " c += "position " + str(d[0]) + " " + str(d[1]) + " " + str(d[2]) + "\n " - c += "orientation " + str(d[3]) + " " + str(d[4]) + " " + str(d[5]) + " " + str(d[6]) + "\n " + c += ( + "orientation " + + str(d[3]) + + " " + + str(d[4]) + + " " + + str(d[5]) + + " " + + str(d[6]) + + "\n " + ) c += "aspectRatio " + str(d[9]) + "\n " c += "focalDistance " + str(d[10]) + "\n " if camtype == "orthographic": @@ -623,8 +715,7 @@ def getCameraData(floatlist): return c -def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,facecolor=None): - +def getCoinSVG(cutplane, objs, cameradata=None, linewidth=0.2, singleface=False, facecolor=None): """Returns an SVG fragment generated from a coin view""" if not FreeCAD.GuiUp: @@ -646,7 +737,7 @@ def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,face ldict = {} if singleface: for obj in objs: - if hasattr(obj,"ViewObject") and hasattr(obj.ViewObject,"Lighting"): + if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "Lighting"): ldict[obj.Name] = obj.ViewObject.Lighting obj.ViewObject.Lighting = "One side" @@ -654,18 +745,18 @@ def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,face root_node = coin.SoSeparator() boundbox = FreeCAD.BoundBox() for obj in objs: - if hasattr(obj.ViewObject,"RootNode") and obj.ViewObject.RootNode: + if hasattr(obj.ViewObject, "RootNode") and obj.ViewObject.RootNode: old_visibility = obj.ViewObject.isVisible() # ignore visibility as only visible objects are passed here obj.ViewObject.show() node_copy = obj.ViewObject.RootNode.copy() root_node.addChild(node_copy) - if(old_visibility): - obj.ViewObject.show() + if old_visibility: + obj.ViewObject.show() else: obj.ViewObject.hide() - if hasattr(obj,"Shape") and hasattr(obj.Shape,"BoundBox"): + if hasattr(obj, "Shape") and hasattr(obj.Shape, "BoundBox"): boundbox.add(obj.Shape.BoundBox) # reset lighting of objects @@ -680,39 +771,43 @@ def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,face view_window.setName(view_window_name) inventor_view = view_window.getViewer() - inventor_view.setBackgroundColor(1,1,1) + inventor_view.setBackgroundColor(1, 1, 1) view_window.redraw() # set clip plane clip = coin.SoClipPlane() - norm = cutplane.normalAt(0,0).negative() - proj = DraftVecUtils.project(cutplane.CenterOfMass,norm) + norm = cutplane.normalAt(0, 0).negative() + proj = DraftVecUtils.project(cutplane.CenterOfMass, norm) dist = proj.Length if proj.getAngle(norm) > 1: dist = -dist clip.on = True - plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) #dir, position on dir + plane = coin.SbPlane(coin.SbVec3f(norm.x, norm.y, norm.z), dist) # dir, position on dir clip.plane.setValue(plane) - root_node.insertChild(clip,0) + root_node.insertChild(clip, 0) # add white marker at scene bound box corner - markervec = FreeCAD.Vector(10,10,10) - a = cutplane.normalAt(0,0).getAngle(markervec) - if (a < 0.01) or (abs(a-math.pi) < 0.01): - markervec = FreeCAD.Vector(10,-10,10) - boundbox.enlarge(10) # so the marker don't overlap the objects + markervec = FreeCAD.Vector(10, 10, 10) + a = cutplane.normalAt(0, 0).getAngle(markervec) + if (a < 0.01) or (abs(a - math.pi) < 0.01): + markervec = FreeCAD.Vector(10, -10, 10) + boundbox.enlarge(10) # so the marker don't overlap the objects sep = coin.SoSeparator() mat = coin.SoMaterial() - mat.diffuseColor.setValue([1,1,1]) + mat.diffuseColor.setValue([1, 1, 1]) sep.addChild(mat) coords = coin.SoCoordinate3() - coords.point.setValues([[boundbox.XMin,boundbox.YMin,boundbox.ZMin], - [boundbox.XMin+markervec.x,boundbox.YMin+markervec.y,boundbox.ZMin+markervec.z]]) + coords.point.setValues( + [ + [boundbox.XMin, boundbox.YMin, boundbox.ZMin], + [boundbox.XMin + markervec.x, boundbox.YMin + markervec.y, boundbox.ZMin + markervec.z], + ] + ) sep.addChild(coords) lset = coin.SoIndexedLineSet() - lset.coordIndex.setValues(0,2,[0,1]) + lset.coordIndex.setValues(0, 2, [0, 1]) sep.addChild(lset) - root_node.insertChild(sep,0) + root_node.insertChild(sep, 0) # set scenegraph inventor_view.setSceneGraph(root_node) @@ -722,82 +817,101 @@ def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,face view_window.setCamera(cameradata) else: view_window.setCameraType("Orthographic") - #rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),cutplane.normalAt(0,0)) - vx = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) - vy = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) - vz = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) - rot = FreeCAD.Rotation(vx,vy,vz,"ZXY") + # rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),cutplane.normalAt(0,0)) + vx = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) + vy = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) + vz = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) + rot = FreeCAD.Rotation(vx, vy, vz, "ZXY") view_window.setCameraOrientation(rot.Q) # this is needed to set correct focal depth, otherwise saving doesn't work properly view_window.fitAll() # save view - #print("saving to",svgfile) - view_window.saveVectorGraphic(svgfile,1) # number is pixel size + # print("saving to",svgfile) + view_window.saveVectorGraphic(svgfile, 1) # number is pixel size # set linewidth placeholder - f = open(svgfile,"r") + f = open(svgfile, "r") svg = f.read() f.close() - svg = svg.replace("stroke-width:1.0;","stroke-width:"+str(linewidth)+";") - svg = svg.replace("stroke-width=\"1px","stroke-width=\""+str(linewidth)) + svg = svg.replace("stroke-width:1.0;", "stroke-width:" + str(linewidth) + ";") + svg = svg.replace('stroke-width="1px', 'stroke-width="' + str(linewidth)) # find marker and calculate scale factor and translation # factor = None trans = None import WorkingPlane + wp = WorkingPlane.PlaneBase() - wp.align_to_point_and_axis_svg(Vector(0,0,0),cutplane.normalAt(0,0),0) + wp.align_to_point_and_axis_svg(Vector(0, 0, 0), cutplane.normalAt(0, 0), 0) p = wp.get_local_coords(markervec) - orlength = FreeCAD.Vector(p.x,p.y,0).Length - marker = re.findall(r"",svg) + orlength = FreeCAD.Vector(p.x, p.y, 0).Length + marker = re.findall(r"", svg) if marker: - marker = marker[0].split("\"") + marker = marker[0].split('"') x1 = float(marker[1]) y1 = float(marker[3]) x2 = float(marker[5]) y2 = float(marker[7]) - p1 = FreeCAD.Vector(x1,y1,0) - p2 = FreeCAD.Vector(x2,y2,0) - factor = orlength/p2.sub(p1).Length + p1 = FreeCAD.Vector(x1, y1, 0) + p2 = FreeCAD.Vector(x2, y2, 0) + factor = orlength / p2.sub(p1).Length if factor: - orig = wp.get_local_coords(FreeCAD.Vector(boundbox.XMin,boundbox.YMin,boundbox.ZMin)) - orig = FreeCAD.Vector(orig.x,-orig.y,0) - scaledp1 = FreeCAD.Vector(p1.x*factor,p1.y*factor,0) + orig = wp.get_local_coords(FreeCAD.Vector(boundbox.XMin, boundbox.YMin, boundbox.ZMin)) + orig = FreeCAD.Vector(orig.x, -orig.y, 0) + scaledp1 = FreeCAD.Vector(p1.x * factor, p1.y * factor, 0) trans = orig.sub(scaledp1) # remove marker - svg = re.sub(r"","",svg,count=1) + svg = re.sub(r"", "", svg, count=1) # remove background rectangle - svg = re.sub(r"","",svg,count=1,flags=re.MULTILINE|re.DOTALL) + svg = re.sub(r"", "", svg, count=1, flags=re.MULTILINE | re.DOTALL) # set face color to white if facecolor: - res = re.findall(r"fill:(.*?); stroke:(.*?);",svg) + res = re.findall(r"fill:(.*?); stroke:(.*?);", svg) pairs = [] for pair in res: - if (pair not in pairs) and (pair[0] == pair[1]) and(pair[0] not in ["#0a0a0a"]): + if (pair not in pairs) and (pair[0] == pair[1]) and (pair[0] not in ["#0a0a0a"]): # coin seems to be rendering a lot of lines as thin triangles with the #0a0a0a color... pairs.append(pair) for pair in pairs: - svg = re.sub(r"fill:"+pair[0]+"; stroke:"+pair[1]+";","fill:"+facecolor+"; stroke:"+facecolor+";",svg) + svg = re.sub( + r"fill:" + pair[0] + "; stroke:" + pair[1] + ";", + "fill:" + facecolor + "; stroke:" + facecolor + ";", + svg, + ) # embed everything in a scale group and scale the viewport if factor: if trans: - svg = svg.replace("","\n",1) + svg = svg.replace( + "", + '\n', + 1, + ) else: - svg = svg.replace("","\n",1) - svg = svg.replace("","\n") + svg = svg.replace( + "", '\n', 1 + ) + svg = svg.replace("", "\n") # trigger viewer close - QtCore.QTimer.singleShot(1,lambda: closeViewer(view_window_name)) + QtCore.QTimer.singleShot(1, lambda: closeViewer(view_window_name)) # strip svg tags (needed for TD Arch view) - svg = re.sub(r"<\?xml.*?>","",svg,flags=re.MULTILINE|re.DOTALL) - svg = re.sub(r"","",svg,flags=re.MULTILINE|re.DOTALL) - svg = re.sub(r"<\/svg>","",svg,flags=re.MULTILINE|re.DOTALL) + svg = re.sub(r"<\?xml.*?>", "", svg, flags=re.MULTILINE | re.DOTALL) + svg = re.sub(r"", "", svg, flags=re.MULTILINE | re.DOTALL) + svg = re.sub(r"<\/svg>", "", svg, flags=re.MULTILINE | re.DOTALL) ISRENDERING = False @@ -805,7 +919,6 @@ def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,face def closeViewer(name): - """Close temporary viewers""" mw = FreeCADGui.getMainWindow() @@ -815,48 +928,105 @@ def closeViewer(name): class _SectionPlane: - "A section plane object" - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "SectionPlane" self.setProperties(obj) - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Placement" in pl: - obj.addProperty("App::PropertyPlacement","Placement","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The placement of this object"), locked=True) + obj.addProperty( + "App::PropertyPlacement", + "Placement", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The placement of this object"), + locked=True, + ) if not "Shape" in pl: - obj.addProperty("Part::PropertyPartShape","Shape","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The shape of this object"), locked=True) + obj.addProperty( + "Part::PropertyPartShape", + "Shape", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The shape of this object"), + locked=True, + ) if not "Objects" in pl: - obj.addProperty("App::PropertyLinkList","Objects","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The objects that must be considered by this section plane. Empty means the whole document."), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Objects", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "The objects that must be considered by this section plane. Empty means the whole document.", + ), + locked=True, + ) if not "OnlySolids" in pl: - obj.addProperty("App::PropertyBool","OnlySolids","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If false, non-solids will be cut too, with possible wrong results."), locked=True) + obj.addProperty( + "App::PropertyBool", + "OnlySolids", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "If false, non-solids will be cut too, with possible wrong results.", + ), + locked=True, + ) obj.OnlySolids = True if not "Clip" in pl: - obj.addProperty("App::PropertyBool","Clip","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If True, resulting views will be clipped to the section plane area."), locked=True) + obj.addProperty( + "App::PropertyBool", + "Clip", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "If True, resulting views will be clipped to the section plane area.", + ), + locked=True, + ) if not "UseMaterialColorForFill" in pl: - obj.addProperty("App::PropertyBool","UseMaterialColorForFill","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If true, the color of the objects material will be used to fill cut areas."), locked=True) + obj.addProperty( + "App::PropertyBool", + "UseMaterialColorForFill", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "If true, the color of the objects material will be used to fill cut areas.", + ), + locked=True, + ) obj.UseMaterialColorForFill = False if not "Depth" in pl: - obj.addProperty("App::PropertyLength","Depth","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Geometry further than this value will be cut off. Keep zero for unlimited."), locked=True) + obj.addProperty( + "App::PropertyLength", + "Depth", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "Geometry further than this value will be cut off. Keep zero for unlimited.", + ), + locked=True, + ) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): self.setProperties(obj) - def execute(self,obj): + def execute(self, obj): import math import Part + l = 1 h = 1 if obj.ViewObject: - if hasattr(obj.ViewObject,"DisplayLength"): + if hasattr(obj.ViewObject, "DisplayLength"): l = obj.ViewObject.DisplayLength.Value h = obj.ViewObject.DisplayHeight.Value - elif hasattr(obj.ViewObject,"DisplaySize"): + elif hasattr(obj.ViewObject, "DisplaySize"): # old objects l = obj.ViewObject.DisplaySize.Value h = obj.ViewObject.DisplaySize.Value @@ -864,43 +1034,42 @@ class _SectionPlane: l = 1 if not h: h = 1 - p = Part.makePlane(l,h,Vector(l/2,-h/2,0),Vector(0,0,-1)) + p = Part.makePlane(l, h, Vector(l / 2, -h / 2, 0), Vector(0, 0, -1)) # make sure the normal direction is pointing outwards, you never know what OCC will decide... # Apply the object's placement to the new plane first. p.Placement = obj.Placement # Now, check if the resulting plane's normal matches the placement's intended direction. # This robustly handles all rotation angles and potential OCC inconsistencies. - target_normal = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) - if p.normalAt(0,0).getAngle(target_normal) > math.pi / 2: + target_normal = obj.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) + if p.normalAt(0, 0).getAngle(target_normal) > math.pi / 2: p.reverse() obj.Shape = p self.svgcache = None self.shapecache = None - def getNormal(self,obj): + def getNormal(self, obj): - return obj.Shape.Faces[0].normalAt(0,0) + return obj.Shape.Faces[0].normalAt(0, 0) def dumps(self): return None - def loads(self,state): + def loads(self, state): self.Type = "SectionPlane" class _ViewProviderSectionPlane: - "A View Provider for Section Planes" - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self self.setProperties(vobj) - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList d = 0 @@ -908,77 +1077,217 @@ class _ViewProviderSectionPlane: d = vobj.DisplaySize.Value vobj.removeProperty("DisplaySize") if not "DisplayLength" in pl: - vobj.addProperty("App::PropertyLength","DisplayLength","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display length of this section plane"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "DisplayLength", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The display length of this section plane"), + locked=True, + ) if d: vobj.DisplayLength = d else: vobj.DisplayLength = 1000 if not "DisplayHeight" in pl: - vobj.addProperty("App::PropertyLength","DisplayHeight","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display height of this section plane"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "DisplayHeight", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The display height of this section plane"), + locked=True, + ) if d: vobj.DisplayHeight = d else: vobj.DisplayHeight = 1000 if not "ArrowSize" in pl: - vobj.addProperty("App::PropertyLength","ArrowSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the arrows of this section plane"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "ArrowSize", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The size of the arrows of this section plane"), + locked=True, + ) vobj.ArrowSize = 50 if not "Transparency" in pl: - vobj.addProperty("App::PropertyPercent","Transparency","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The transparency of this object"), locked=True) + vobj.addProperty( + "App::PropertyPercent", + "Transparency", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The transparency of this object"), + locked=True, + ) vobj.Transparency = 85 if not "LineWidth" in pl: - vobj.addProperty("App::PropertyFloat","LineWidth","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The line width of this object"), locked=True) + vobj.addProperty( + "App::PropertyFloat", + "LineWidth", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The line width of this object"), + locked=True, + ) vobj.LineWidth = 1 if not "CutDistance" in pl: - vobj.addProperty("App::PropertyLength","CutDistance","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "CutDistance", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "Show the cut in the 3D view"), + locked=True, + ) if not "LineColor" in pl: - vobj.addProperty("App::PropertyColor","LineColor","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The color of this object"), locked=True) + vobj.addProperty( + "App::PropertyColor", + "LineColor", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The color of this object"), + locked=True, + ) vobj.LineColor = ArchCommands.getDefaultColor("Helpers") if not "CutView" in pl: - vobj.addProperty("App::PropertyBool","CutView","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "CutView", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "Show the cut in the 3D view"), + locked=True, + ) if not "CutMargin" in pl: - vobj.addProperty("App::PropertyLength","CutMargin","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The distance between the cut plane and the actual view cut (keep this a very small value but not zero)"), locked=True) + vobj.addProperty( + "App::PropertyLength", + "CutMargin", + "SectionPlane", + QT_TRANSLATE_NOOP( + "App::Property", + "The distance between the cut plane and the actual view cut (keep this a very small value but not zero)", + ), + locked=True, + ) vobj.CutMargin = 1 if not "ShowLabel" in pl: - vobj.addProperty("App::PropertyBool","ShowLabel","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the label in the 3D view"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowLabel", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "Show the label in the 3D view"), + locked=True, + ) if not "FontName" in pl: - vobj.addProperty("App::PropertyFont","FontName", "SectionPlane",QT_TRANSLATE_NOOP("App::Property","The name of the font"), locked=True) + vobj.addProperty( + "App::PropertyFont", + "FontName", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The name of the font"), + locked=True, + ) vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: - vobj.addProperty("App::PropertyLength","FontSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the text font"), locked=True) - vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") + vobj.addProperty( + "App::PropertyLength", + "FontSize", + "SectionPlane", + QT_TRANSLATE_NOOP("App::Property", "The size of the text font"), + locked=True, + ) + vobj.FontSize = params.get_param("textheight") * params.get_param( + "DefaultAnnoScaleMultiplier" + ) - - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_SectionPlane_Tree.svg" def claimChildren(self): # buggy at the moment so it's disabled - it will for ex. swallow a building object directly at the root of the document... - #if hasattr(self,"Object") and hasattr(self.Object,"Objects"): + # if hasattr(self,"Object") and hasattr(self.Object,"Objects"): # return self.Object.Objects return [] - def attach(self,vobj): + def attach(self, vobj): self.Object = vobj.Object self.clip = None self.mat1 = coin.SoMaterial() self.mat2 = coin.SoMaterial() self.fcoords = coin.SoCoordinate3() - #fs = coin.SoType.fromName("SoBrepFaceSet").createInstance() # this causes a FreeCAD freeze for me + # fs = coin.SoType.fromName("SoBrepFaceSet").createInstance() # this causes a FreeCAD freeze for me fs = coin.SoIndexedFaceSet() - fs.coordIndex.setValues(0,7,[0,1,2,-1,0,2,3]) + fs.coordIndex.setValues(0, 7, [0, 1, 2, -1, 0, 2, 3]) self.drawstyle = coin.SoDrawStyle() self.drawstyle.style = coin.SoDrawStyle.LINES self.lcoords = coin.SoCoordinate3() - import PartGui # Required for "SoBrepEdgeSet" (because a SectionPlane is not a Part::FeaturePython object). + import PartGui # Required for "SoBrepEdgeSet" (because a SectionPlane is not a Part::FeaturePython object). + ls = coin.SoType.fromName("SoBrepEdgeSet").createInstance() - ls.coordIndex.setValues(0,57,[0,1,-1,2,3,4,5,-1,6,7,8,9,-1,10,11,-1,12,13,14,15,-1,16,17,18,19,-1,20,21,-1,22,23,24,25,-1,26,27,28,29,-1,30,31,-1,32,33,34,35,-1,36,37,38,39,-1,40,41,42,43,44]) + ls.coordIndex.setValues( + 0, + 57, + [ + 0, + 1, + -1, + 2, + 3, + 4, + 5, + -1, + 6, + 7, + 8, + 9, + -1, + 10, + 11, + -1, + 12, + 13, + 14, + 15, + -1, + 16, + 17, + 18, + 19, + -1, + 20, + 21, + -1, + 22, + 23, + 24, + 25, + -1, + 26, + 27, + 28, + 29, + -1, + 30, + 31, + -1, + 32, + 33, + 34, + 35, + -1, + 36, + 37, + 38, + 39, + -1, + 40, + 41, + 42, + 43, + 44, + ], + ) self.txtcoords = coin.SoTransform() self.txtfont = coin.SoFont() self.txtfont.name = "" @@ -1003,13 +1312,13 @@ class _ViewProviderSectionPlane: sep.addChild(fsep) sep.addChild(psep) sep.addChild(tsep) - vobj.addDisplayMode(sep,"Default") - self.onChanged(vobj,"DisplayLength") - self.onChanged(vobj,"LineColor") - self.onChanged(vobj,"Transparency") - self.onChanged(vobj,"CutView") + vobj.addDisplayMode(sep, "Default") + self.onChanged(vobj, "DisplayLength") + self.onChanged(vobj, "LineColor") + self.onChanged(vobj, "Transparency") + self.onChanged(vobj, "CutView") - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): return ["Default"] @@ -1017,26 +1326,27 @@ class _ViewProviderSectionPlane: return "Default" - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): return mode - def updateData(self,obj,prop): + def updateData(self, obj, prop): vobj = obj.ViewObject if prop in ["Placement"]: # for some reason the text doesn't rotate with the host placement?? self.txtcoords.rotation.setValue(obj.Placement.Rotation.Q) - self.onChanged(vobj,"DisplayLength") + self.onChanged(vobj, "DisplayLength") # Defer the clipping plane update until after the current event # loop finishes. This ensures the scene graph has been updated with the # new placement before we try to recalculate the clip plane. if vobj and hasattr(vobj, "CutView") and vobj.CutView: from PySide import QtCore + # We use a lambda to pass the vobj argument to the delayed function. QtCore.QTimer.singleShot(0, lambda: self.refreshCutView(vobj)) elif prop == "Label": - if hasattr(obj.ViewObject,"ShowLabel") and obj.ViewObject.ShowLabel: + if hasattr(obj.ViewObject, "ShowLabel") and obj.ViewObject.ShowLabel: self.txt.string = obj.Label return @@ -1049,89 +1359,94 @@ class _ViewProviderSectionPlane: vobj.CutView = False vobj.CutView = True - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): if prop == "LineColor": - if hasattr(vobj,"LineColor"): + if hasattr(vobj, "LineColor"): l = vobj.LineColor - self.mat1.diffuseColor.setValue([l[0],l[1],l[2]]) - self.mat2.diffuseColor.setValue([l[0],l[1],l[2]]) + self.mat1.diffuseColor.setValue([l[0], l[1], l[2]]) + self.mat2.diffuseColor.setValue([l[0], l[1], l[2]]) elif prop == "Transparency": - if hasattr(vobj,"Transparency"): - self.mat2.transparency.setValue(vobj.Transparency/100.0) - elif prop in ["DisplayLength","DisplayHeight","ArrowSize"]: + if hasattr(vobj, "Transparency"): + self.mat2.transparency.setValue(vobj.Transparency / 100.0) + elif prop in ["DisplayLength", "DisplayHeight", "ArrowSize"]: # for IFC objects: propagate to the object - if prop in ["DisplayLength","DisplayHeight"]: + if prop in ["DisplayLength", "DisplayHeight"]: if hasattr(vobj.Object.Proxy, "onChanged"): vobj.Object.Proxy.onChanged(vobj.Object, prop) - if hasattr(vobj,"DisplayLength") and hasattr(vobj,"DisplayHeight"): - ld = vobj.DisplayLength.Value/2 - hd = vobj.DisplayHeight.Value/2 - elif hasattr(vobj,"DisplaySize"): + if hasattr(vobj, "DisplayLength") and hasattr(vobj, "DisplayHeight"): + ld = vobj.DisplayLength.Value / 2 + hd = vobj.DisplayHeight.Value / 2 + elif hasattr(vobj, "DisplaySize"): # old objects - ld = vobj.DisplaySize.Value/2 - hd = vobj.DisplaySize.Value/2 + ld = vobj.DisplaySize.Value / 2 + hd = vobj.DisplaySize.Value / 2 else: ld = 1 hd = 1 verts = [] fverts = [] pl = FreeCAD.Placement(vobj.Object.Placement) - if hasattr(vobj,"ArrowSize"): + if hasattr(vobj, "ArrowSize"): l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1 else: l1 = 0.1 - l2 = l1/3 - for v in [[-ld,-hd],[ld,-hd],[ld,hd],[-ld,hd]]: - p1 = pl.multVec(Vector(v[0],v[1],0)) - p2 = pl.multVec(Vector(v[0],v[1],-l1)) - p3 = pl.multVec(Vector(v[0]-l2,v[1],-l1+l2)) - p4 = pl.multVec(Vector(v[0]+l2,v[1],-l1+l2)) - p5 = pl.multVec(Vector(v[0],v[1]-l2,-l1+l2)) - p6 = pl.multVec(Vector(v[0],v[1]+l2,-l1+l2)) - verts.extend([[p1.x,p1.y,p1.z],[p2.x,p2.y,p2.z]]) - fverts.append([p1.x,p1.y,p1.z]) - verts.extend([[p2.x,p2.y,p2.z],[p3.x,p3.y,p3.z],[p4.x,p4.y,p4.z],[p2.x,p2.y,p2.z]]) - verts.extend([[p2.x,p2.y,p2.z],[p5.x,p5.y,p5.z],[p6.x,p6.y,p6.z],[p2.x,p2.y,p2.z]]) - p7 = pl.multVec(Vector(-ld+l2,-hd+l2,0)) # text pos - verts.extend(fverts+[fverts[0]]) + l2 = l1 / 3 + for v in [[-ld, -hd], [ld, -hd], [ld, hd], [-ld, hd]]: + p1 = pl.multVec(Vector(v[0], v[1], 0)) + p2 = pl.multVec(Vector(v[0], v[1], -l1)) + p3 = pl.multVec(Vector(v[0] - l2, v[1], -l1 + l2)) + p4 = pl.multVec(Vector(v[0] + l2, v[1], -l1 + l2)) + p5 = pl.multVec(Vector(v[0], v[1] - l2, -l1 + l2)) + p6 = pl.multVec(Vector(v[0], v[1] + l2, -l1 + l2)) + verts.extend([[p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z]]) + fverts.append([p1.x, p1.y, p1.z]) + verts.extend( + [[p2.x, p2.y, p2.z], [p3.x, p3.y, p3.z], [p4.x, p4.y, p4.z], [p2.x, p2.y, p2.z]] + ) + verts.extend( + [[p2.x, p2.y, p2.z], [p5.x, p5.y, p5.z], [p6.x, p6.y, p6.z], [p2.x, p2.y, p2.z]] + ) + p7 = pl.multVec(Vector(-ld + l2, -hd + l2, 0)) # text pos + verts.extend(fverts + [fverts[0]]) self.lcoords.point.setValues(verts) self.fcoords.point.setValues(fverts) - self.txtcoords.translation.setValue([p7.x,p7.y,p7.z]) - #self.txtfont.size = l1 + self.txtcoords.translation.setValue([p7.x, p7.y, p7.z]) + # self.txtfont.size = l1 elif prop == "LineWidth": self.drawstyle.lineWidth = vobj.LineWidth - elif prop in ["CutView","CutMargin"]: - if hasattr(vobj, "CutView") \ - and FreeCADGui.ActiveDocument.ActiveView \ - and hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph"): + elif prop in ["CutView", "CutMargin"]: + if ( + hasattr(vobj, "CutView") + and FreeCADGui.ActiveDocument.ActiveView + and hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph") + ): sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if vobj.CutView: if self.clip: sg.removeChild(self.clip) self.clip = None - for o in Draft.get_group_contents(vobj.Object.Objects, - walls=True): - if hasattr(o.ViewObject,"Lighting"): + for o in Draft.get_group_contents(vobj.Object.Objects, walls=True): + if hasattr(o.ViewObject, "Lighting"): o.ViewObject.Lighting = "One side" self.clip = coin.SoClipPlane() self.clip.on.setValue(True) norm = vobj.Object.Proxy.getNormal(vobj.Object) mp = vobj.Object.Shape.CenterOfMass - mp = DraftVecUtils.project(mp,norm) - dist = mp.Length #- 0.1 # to not clip exactly on the section object + mp = DraftVecUtils.project(mp, norm) + dist = mp.Length # - 0.1 # to not clip exactly on the section object norm = norm.negative() marg = 1 - if hasattr(vobj,"CutMargin"): + if hasattr(vobj, "CutMargin"): marg = vobj.CutMargin.Value if mp.getAngle(norm) > 1: dist += marg dist = -dist else: dist -= marg - plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) + plane = coin.SbPlane(coin.SbVec3f(norm.x, norm.y, norm.z), dist) self.clip.plane.setValue(plane) - sg.insertChild(self.clip,0) + sg.insertChild(self.clip, 0) else: if self.clip: sg.removeChild(self.clip) @@ -1142,13 +1457,13 @@ class _ViewProviderSectionPlane: else: self.txt.string = " " elif prop == "FontName": - if hasattr(self,"txtfont") and hasattr(vobj,"FontName"): + if hasattr(self, "txtfont") and hasattr(vobj, "FontName"): if vobj.FontName: self.txtfont.name = vobj.FontName else: self.txtfont.name = "" elif prop == "FontSize": - if hasattr(self,"txtfont") and hasattr(vobj,"FontSize"): + if hasattr(self, "txtfont") and hasattr(vobj, "FontSize"): self.txtfont.size = vobj.FontSize.Value return @@ -1156,7 +1471,7 @@ class _ViewProviderSectionPlane: return None - def loads(self,state): + def loads(self, state): return None @@ -1182,19 +1497,16 @@ class _ViewProviderSectionPlane: return True def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) - actionToggleCutview = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"), - translate("Arch", "Toggle Cutview"), - menu) + actionToggleCutview = QtGui.QAction( + QtGui.QIcon(":/icons/Draft_Edit.svg"), translate("Arch", "Toggle Cutview"), menu + ) actionToggleCutview.triggered.connect(lambda: self.toggleCutview(vobj)) menu.addAction(actionToggleCutview) @@ -1206,8 +1518,7 @@ class _ViewProviderSectionPlane: class SectionPlaneTaskPanel: - - '''A TaskPanel for all the section plane object''' + """A TaskPanel for all the section plane object""" def __init__(self): @@ -1249,7 +1560,9 @@ class SectionPlaneTaskPanel: self.cutViewButton.setIcon(QtGui.QIcon(":/icons/Arch_CutPlane.svg")) self.cutViewButton.setObjectName("cutViewButton") self.cutViewButton.setCheckable(True) - QtCore.QObject.connect(self.cutViewButton, QtCore.SIGNAL("toggled(bool)"), self.toggleCutView) + QtCore.QObject.connect( + self.cutViewButton, QtCore.SIGNAL("toggled(bool)"), self.toggleCutView + ) tools_layout.addWidget(self.cutViewButton) # rotate / resize buttons @@ -1293,8 +1606,8 @@ class SectionPlaneTaskPanel: def getStandardButtons(self): return QtGui.QDialogButtonBox.Close - def getIcon(self,obj): - if hasattr(obj.ViewObject,"Proxy"): + def getIcon(self, obj): + if hasattr(obj.ViewObject, "Proxy"): return QtGui.QIcon(obj.ViewObject.Proxy.getIcon()) elif obj.isDerivedFrom("Sketcher::SketchObject"): return QtGui.QIcon(":/icons/Sketcher_Sketch.svg") @@ -1305,14 +1618,14 @@ class SectionPlaneTaskPanel: return QtGui.QIcon(":/icons/Part_3D_object.svg") def update(self): - 'fills the treewidget' + "fills the treewidget" self.tree.clear() if self.obj: for o in self.obj.Objects: item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,o.Label) - item.setToolTip(0,o.Name) - item.setIcon(0,self.getIcon(o)) + item.setText(0, o.Label) + item.setToolTip(0, o.Name) + item.setIcon(0, self.getIcon(o)) if self.obj.ViewObject and hasattr(self.obj.ViewObject, "CutView"): self.cutViewButton.setChecked(self.obj.ViewObject.CutView) self.retranslateUi() @@ -1322,44 +1635,47 @@ class SectionPlaneTaskPanel: added = False for o in FreeCADGui.Selection.getSelection(): if o != self.obj: - ArchComponent.addToComponent(self.obj,o,"Objects") + ArchComponent.addToComponent(self.obj, o, "Objects") added = True if added: self.update() else: - FreeCAD.Console.PrintWarning("Select objects in the 3D view or in the model tree before pressing the button\n") + FreeCAD.Console.PrintWarning( + "Select objects in the 3D view or in the model tree before pressing the button\n" + ) def removeElement(self): if self.obj: it = self.tree.currentItem() if it: comp = FreeCAD.ActiveDocument.getObject(str(it.toolTip(0))) - ArchComponent.removeFromComponent(self.obj,comp) + ArchComponent.removeFromComponent(self.obj, comp) self.update() - def rotate(self,axis): + def rotate(self, axis): if self.obj and self.obj.Shape and self.obj.Shape.Faces: face = self.obj.Shape.copy() import Part + local_axis = self.obj.Placement.Rotation.multVec(axis) face.rotate(self.obj.Placement.Base, local_axis, 90) self.obj.Placement = face.Placement self.obj.Proxy.execute(self.obj) def rotateX(self): - self.rotate(FreeCAD.Vector(1,0,0)) + self.rotate(FreeCAD.Vector(1, 0, 0)) def rotateY(self): - self.rotate(FreeCAD.Vector(0,1,0)) + self.rotate(FreeCAD.Vector(0, 1, 0)) def rotateZ(self): - self.rotate(FreeCAD.Vector(0,0,1)) + self.rotate(FreeCAD.Vector(0, 0, 1)) def getBB(self): bb = FreeCAD.BoundBox() if self.obj: for o in Draft.get_group_contents(self.obj.Objects): - if hasattr(o,"Shape") and hasattr(o.Shape,"BoundBox"): + if hasattr(o, "Shape") and hasattr(o.Shape, "BoundBox"): bb.add(o.Shape.BoundBox) return bb @@ -1367,16 +1683,22 @@ class SectionPlaneTaskPanel: if self.obj and self.obj.ViewObject: bb = self.getBB() n = self.obj.Proxy.getNormal(self.obj) - margin = bb.XLength*0.1 - if (n.getAngle(FreeCAD.Vector(1,0,0)) < 0.1) or (n.getAngle(FreeCAD.Vector(-1,0,0)) < 0.1): - self.obj.ViewObject.DisplayLength = bb.YLength+margin - self.obj.ViewObject.DisplayHeight = bb.ZLength+margin - elif (n.getAngle(FreeCAD.Vector(0,1,0)) < 0.1) or (n.getAngle(FreeCAD.Vector(0,-1,0)) < 0.1): - self.obj.ViewObject.DisplayLength = bb.XLength+margin - self.obj.ViewObject.DisplayHeight = bb.ZLength+margin - elif (n.getAngle(FreeCAD.Vector(0,0,1)) < 0.1) or (n.getAngle(FreeCAD.Vector(0,0,-1)) < 0.1): - self.obj.ViewObject.DisplayLength = bb.XLength+margin - self.obj.ViewObject.DisplayHeight = bb.YLength+margin + margin = bb.XLength * 0.1 + if (n.getAngle(FreeCAD.Vector(1, 0, 0)) < 0.1) or ( + n.getAngle(FreeCAD.Vector(-1, 0, 0)) < 0.1 + ): + self.obj.ViewObject.DisplayLength = bb.YLength + margin + self.obj.ViewObject.DisplayHeight = bb.ZLength + margin + elif (n.getAngle(FreeCAD.Vector(0, 1, 0)) < 0.1) or ( + n.getAngle(FreeCAD.Vector(0, -1, 0)) < 0.1 + ): + self.obj.ViewObject.DisplayLength = bb.XLength + margin + self.obj.ViewObject.DisplayHeight = bb.ZLength + margin + elif (n.getAngle(FreeCAD.Vector(0, 0, 1)) < 0.1) or ( + n.getAngle(FreeCAD.Vector(0, 0, -1)) < 0.1 + ): + self.obj.ViewObject.DisplayLength = bb.XLength + margin + self.obj.ViewObject.DisplayHeight = bb.YLength + margin self.obj.Proxy.execute(self.obj) def recenter(self): @@ -1400,27 +1722,59 @@ class SectionPlaneTaskPanel: return True def toggleCutView(self, checked): - if self.obj and self.obj.ViewObject and hasattr(self.obj.ViewObject, "CutView"): + if self.obj and self.obj.ViewObject and hasattr(self.obj.ViewObject, "CutView"): self.obj.ViewObject.CutView = checked def retranslateUi(self): self.scope_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Scope", None)) - self.tools_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Placement and Visuals", None)) - self.title.setText(QtGui.QApplication.translate("Arch", "Objects seen by this section plane", None)) + self.tools_widget.setWindowTitle( + QtGui.QApplication.translate("Arch", "Placement and Visuals", None) + ) + self.title.setText( + QtGui.QApplication.translate("Arch", "Objects seen by this section plane", None) + ) self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None)) - self.delButton.setToolTip(QtGui.QApplication.translate("Arch", "Removes highlighted objects from the list above", None)) + self.delButton.setToolTip( + QtGui.QApplication.translate( + "Arch", "Removes highlighted objects from the list above", None + ) + ) self.addButton.setText(QtGui.QApplication.translate("Arch", "Add Selected", None)) - self.addButton.setToolTip(QtGui.QApplication.translate("Arch", "Adds selected objects to the scope of this section plane", None)) + self.addButton.setToolTip( + QtGui.QApplication.translate( + "Arch", "Adds selected objects to the scope of this section plane", None + ) + ) self.cutViewButton.setText(QtGui.QApplication.translate("Arch", "Cut View", None)) - self.cutViewButton.setToolTip(QtGui.QApplication.translate("Arch", "Creates a live cut in the 3D view, hiding geometry on one side of the plane to see inside your model", None)) + self.cutViewButton.setToolTip( + QtGui.QApplication.translate( + "Arch", + "Creates a live cut in the 3D view, hiding geometry on one side of the plane to see inside your model", + None, + ) + ) self.rotation_label.setText(QtGui.QApplication.translate("Arch", "Rotate by 90°", None)) self.rotateXButton.setText(QtGui.QApplication.translate("Arch", "Rotate X", None)) - self.rotateXButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane around its local X-axis", None)) + self.rotateXButton.setToolTip( + QtGui.QApplication.translate("Arch", "Rotates the plane around its local X-axis", None) + ) self.rotateYButton.setText(QtGui.QApplication.translate("Arch", "Rotate Y", None)) - self.rotateYButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane around its local Y-axis", None)) + self.rotateYButton.setToolTip( + QtGui.QApplication.translate("Arch", "Rotates the plane around its local Y-axis", None) + ) self.rotateZButton.setText(QtGui.QApplication.translate("Arch", "Rotate Z", None)) - self.rotateZButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane around its local Z-axis", None)) + self.rotateZButton.setToolTip( + QtGui.QApplication.translate("Arch", "Rotates the plane around its local Z-axis", None) + ) self.resizeButton.setText(QtGui.QApplication.translate("Arch", "Resize to Fit", None)) - self.resizeButton.setToolTip(QtGui.QApplication.translate("Arch", "Resizes the plane to fit the objects in the list above", None)) + self.resizeButton.setToolTip( + QtGui.QApplication.translate( + "Arch", "Resizes the plane to fit the objects in the list above", None + ) + ) self.recenterButton.setText(QtGui.QApplication.translate("Arch", "Recenter Plane", None)) - self.recenterButton.setToolTip(QtGui.QApplication.translate("Arch", "Centers the plane on the objects in the list above", None)) \ No newline at end of file + self.recenterButton.setToolTip( + QtGui.QApplication.translate( + "Arch", "Centers the plane on the objects in the list above", None + ) + ) diff --git a/src/Mod/BIM/ArchSite.py b/src/Mod/BIM/ArchSite.py index ae16bdb6a0..315df7e96e 100644 --- a/src/Mod/BIM/ArchSite.py +++ b/src/Mod/BIM/ArchSite.py @@ -22,7 +22,7 @@ # * * # *************************************************************************** -__title__= "FreeCAD Site" +__title__ = "FreeCAD Site" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" @@ -51,21 +51,24 @@ import Draft from draftutils import params if FreeCAD.GuiUp: - from PySide import QtGui,QtCore + from PySide import QtGui, QtCore from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCADGui from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond import logging from contextlib import contextmanager + @contextmanager def temp_logger_level(level): """A context manager to temporarily set the root logger's level.""" @@ -77,23 +80,24 @@ def temp_logger_level(level): finally: root_logger.setLevel(original_level) -def toNode(shape): +def toNode(shape): """builds a linear pivy node from a shape""" from pivy import coin - buf = shape.writeInventor(2,0.01).replace("\n","") - buf = re.findall(r"point \[(.*?)\]",buf) + + buf = shape.writeInventor(2, 0.01).replace("\n", "") + buf = re.findall(r"point \[(.*?)\]", buf) pts = [] for c in buf: - pts.extend(zip(*[iter( c.split() )]*3) ) + pts.extend(zip(*[iter(c.split())] * 3)) pc = [] for v in pts: - v = [float(v[0]),float(v[1]),float(v[2])] + v = [float(v[0]), float(v[1]), float(v[2])] if (not pc) or (pc[-1] != v): pc.append(v) coords = coin.SoCoordinate3() - coords.point.setValues(0,len(pc),pc) + coords.point.setValues(0, len(pc), pc) line = coin.SoLineSet() line.numVertices.setValue(-1) item = coin.SoSeparator() @@ -102,8 +106,7 @@ def toNode(shape): return item -def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): - +def makeSolarDiagram(longitude, latitude, scale=1, complete=False, tz=None): """makeSolarDiagram(longitude,latitude,[scale,complete,tz]): returns a solar diagram as a pivy node. If complete is True, the 12 months are drawn. Tz is the timezone related to @@ -114,7 +117,8 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): with temp_logger_level(logging.WARNING): try: import ladybug - logging.getLogger('ladybug').propagate = False + + logging.getLogger("ladybug").propagate = False from ladybug import location from ladybug import sunpath except ImportError: @@ -127,7 +131,9 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): try: import Pysolar as pysolar except ImportError: - FreeCAD.Console.PrintError("The pysolar module was not found. Unable to generate solar diagrams\n") + FreeCAD.Console.PrintError( + "The pysolar module was not found. Unable to generate solar diagrams\n" + ) return None else: oldversion = True @@ -136,7 +142,7 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): else: tz = datetime.timezone.utc else: - loc = ladybug.location.Location(latitude=latitude,longitude=longitude,time_zone=tz) + loc = ladybug.location.Location(latitude=latitude, longitude=longitude, time_zone=tz) sunpath = ladybug.sunpath.Sunpath.from_location(loc) from pivy import coin @@ -152,23 +158,24 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): # build the base circle + number positions import Part - for i in range(1,9): - circles.append(Part.makeCircle(scale*(i/8.0))) - for ad in range(0,360,15): + + for i in range(1, 9): + circles.append(Part.makeCircle(scale * (i / 8.0))) + for ad in range(0, 360, 15): a = math.radians(ad) - p1 = FreeCAD.Vector(math.cos(a)*scale,math.sin(a)*scale,0) - p2 = FreeCAD.Vector(math.cos(a)*scale*0.125,math.sin(a)*scale*0.125,0) - p3 = FreeCAD.Vector(math.cos(a)*scale*1.08,math.sin(a)*scale*1.08,0) - circles.append(Part.LineSegment(p1,p2).toShape()) - circlepos.append((ad,p3)) + p1 = FreeCAD.Vector(math.cos(a) * scale, math.sin(a) * scale, 0) + p2 = FreeCAD.Vector(math.cos(a) * scale * 0.125, math.sin(a) * scale * 0.125, 0) + p3 = FreeCAD.Vector(math.cos(a) * scale * 1.08, math.sin(a) * scale * 1.08, 0) + circles.append(Part.LineSegment(p1, p2).toShape()) + circlepos.append((ad, p3)) # build the sun curves at solstices and equinoxe year = datetime.datetime.now().year - hpts = [ [] for i in range(24) ] - m = [(6,21),(7,21),(8,21),(9,21),(10,21),(11,21),(12,21)] + hpts = [[] for i in range(24)] + m = [(6, 21), (7, 21), (8, 21), (9, 21), (10, 21), (11, 21), (12, 21)] if complete: - m.extend([(1,21),(2,21),(3,21),(4,21),(5,21)]) - for i,d in enumerate(m): + m.extend([(1, 21), (2, 21), (3, 21), (4, 21), (5, 21)]) + for i, d in enumerate(m): pts = [] for h in range(24): if ladybug: @@ -179,28 +186,28 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): dt = datetime.datetime(year, d[0], d[1], h) alt = math.radians(pysolar.solar.GetAltitudeFast(latitude, longitude, dt)) az = pysolar.solar.GetAzimuth(latitude, longitude, dt) - az = -90 + az # pysolar's zero is south, ours is X direction + az = -90 + az # pysolar's zero is south, ours is X direction else: dt = datetime.datetime(year, d[0], d[1], h, tzinfo=tz) alt = math.radians(pysolar.solar.get_altitude_fast(latitude, longitude, dt)) az = pysolar.solar.get_azimuth(latitude, longitude, dt) - az = 90 + az # pysolar's zero is north, ours is X direction + az = 90 + az # pysolar's zero is north, ours is X direction if az < 0: az = 360 + az az = math.radians(az) - zc = math.sin(alt)*scale - ic = math.cos(alt)*scale - xc = math.cos(az)*ic - yc = math.sin(az)*ic - p = FreeCAD.Vector(xc,yc,zc) + zc = math.sin(alt) * scale + ic = math.cos(alt) * scale + xc = math.cos(az) * ic + yc = math.sin(az) * ic + p = FreeCAD.Vector(xc, yc, zc) pts.append(p) hpts[h].append(p) - if i in [0,6]: + if i in [0, 6]: ep = FreeCAD.Vector(p) ep.multiply(1.08) if ep.z >= 0: if not oldversion: - h = 24-h # not sure why this is needed now... But it is. + h = 24 - h # not sure why this is needed now... But it is. if h == 12: if i == 0: h = "SUMMER" @@ -211,7 +218,7 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): h = "WINTER" else: h = "SUMMER" - hourpos.append((h,ep)) + hourpos.append((h, ep)) if i < 7: if len(pts) > 1: b_spline = Part.BSplineCurve() @@ -227,20 +234,20 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): hourpaths.append(b_spline.toShape()) # cut underground lines - sz = 2.1*scale - cube = Part.makeBox(sz,sz,sz) - cube.translate(FreeCAD.Vector(-sz/2,-sz/2,-sz)) + sz = 2.1 * scale + cube = Part.makeBox(sz, sz, sz) + cube.translate(FreeCAD.Vector(-sz / 2, -sz / 2, -sz)) sunpaths = [sp.cut(cube) for sp in sunpaths] hourpaths = [hp.cut(cube) for hp in hourpaths] # build nodes - ts = 0.005*scale # text scale + ts = 0.005 * scale # text scale mastersep = coin.SoSeparator() circlesep = coin.SoSeparator() numsep = coin.SoSeparator() pathsep = coin.SoSeparator() hoursep = coin.SoSeparator() - #hournumsep = coin.SoSeparator() + # hournumsep = coin.SoSeparator() mastersep.addChild(circlesep) mastersep.addChild(numsep) mastersep.addChild(pathsep) @@ -255,7 +262,7 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): hoursep.addChild(toNode(w)) for p in circlepos: text = coin.SoText2() - s = p[0]-90 + s = p[0] - 90 s = -s if s > 360: s = s - 360 @@ -274,8 +281,8 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): text.string = s text.justification = coin.SoText2.CENTER coords = coin.SoTransform() - coords.translation.setValue([p[1].x,p[1].y,p[1].z]) - coords.scaleFactor.setValue([ts,ts,ts]) + coords.translation.setValue([p[1].x, p[1].y, p[1].z]) + coords.scaleFactor.setValue([ts, ts, ts]) item = coin.SoSeparator() item.addChild(coords) item.addChild(text) @@ -286,8 +293,8 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): text.string = s text.justification = coin.SoText2.CENTER coords = coin.SoTransform() - coords.translation.setValue([p[1].x,p[1].y,p[1].z]) - coords.scaleFactor.setValue([ts,ts,ts]) + coords.translation.setValue([p[1].x, p[1].y, p[1].z]) + coords.scaleFactor.setValue([ts, ts, ts]) item = coin.SoSeparator() item.addChild(coords) item.addChild(text) @@ -295,59 +302,62 @@ def makeSolarDiagram(longitude,latitude,scale=1,complete=False,tz=None): return mastersep -def makeWindRose(epwfile,scale=1,sectors=24): - +def makeWindRose(epwfile, scale=1, sectors=24): """makeWindRose(site,sectors): returns a wind rose diagram as a pivy node""" with temp_logger_level(logging.WARNING): try: import ladybug - logging.getLogger('ladybug').propagate = False + + logging.getLogger("ladybug").propagate = False from ladybug import epw except ImportError: - FreeCAD.Console.PrintError("The ladybug module was not found. Unable to generate solar diagrams\n") + FreeCAD.Console.PrintError( + "The ladybug module was not found. Unable to generate solar diagrams\n" + ) return None if not epwfile: FreeCAD.Console.PrintWarning("No EPW file, unable to generate wind rose.\n") return None epw_data = ladybug.epw.EPW(epwfile) - baseangle = 360/sectors - sectorangles = [i * baseangle for i in range(sectors)] # the divider angles between each sector - basebissect = baseangle/2 - angles = [basebissect] # build a list of central direction for each sector - for i in range(1,sectors): - angles.append(angles[-1]+baseangle) - windsbysector = [0 for i in range(sectors)] # prepare a holder for values for each sector + baseangle = 360 / sectors + sectorangles = [i * baseangle for i in range(sectors)] # the divider angles between each sector + basebissect = baseangle / 2 + angles = [basebissect] # build a list of central direction for each sector + for i in range(1, sectors): + angles.append(angles[-1] + baseangle) + windsbysector = [0 for i in range(sectors)] # prepare a holder for values for each sector for hour in epw_data.wind_direction: - sector = min(angles, key=lambda x:abs(x-hour)) # find the closest sector angle + sector = min(angles, key=lambda x: abs(x - hour)) # find the closest sector angle sectorindex = angles.index(sector) windsbysector[sectorindex] = windsbysector[sectorindex] + 1 maxwind = max(windsbysector) - windsbysector = [wind/maxwind for wind in windsbysector] # normalize - vectors = [] # create 3D vectors + windsbysector = [wind / maxwind for wind in windsbysector] # normalize + vectors = [] # create 3D vectors dividers = [] for i in range(sectors): angle = math.radians(90 + angles[i]) x = math.cos(angle) * windsbysector[i] * scale y = math.sin(angle) * windsbysector[i] * scale - vectors.append(FreeCAD.Vector(x,y,0)) + vectors.append(FreeCAD.Vector(x, y, 0)) secangle = math.radians(90 + sectorangles[i]) x = math.cos(secangle) * scale y = math.sin(secangle) * scale - dividers.append(FreeCAD.Vector(x,y,0)) + dividers.append(FreeCAD.Vector(x, y, 0)) vectors.append(vectors[0]) # build coin node import Part from pivy import coin + masternode = coin.SoSeparator() - for r in (0.25,0.5,0.75,1.0): + for r in (0.25, 0.5, 0.75, 1.0): c = Part.makeCircle(r * scale) masternode.addChild(toNode(c)) for divider in dividers: - l = Part.makeLine(FreeCAD.Vector(),divider) + l = Part.makeLine(FreeCAD.Vector(), divider) masternode.addChild(toNode(l)) ds = coin.SoDrawStyle() ds.lineWidth = 2.0 @@ -357,7 +367,6 @@ def makeWindRose(epwfile,scale=1,sectors=24): return masternode - # Values in mm COMPASS_POINTER_LENGTH = 1000 COMPASS_POINTER_WIDTH = 100 @@ -369,19 +378,22 @@ class Compass(object): def show(self): from pivy import coin + self.compassswitch.whichChild = coin.SO_SWITCH_ALL def hide(self): from pivy import coin + self.compassswitch.whichChild = coin.SO_SWITCH_NONE def rotate(self, angleInDegrees): from pivy import coin - self.transform.rotation.setValue( - coin.SbVec3f(0, 0, 1), math.radians(angleInDegrees)) - def locate(self, x,y,z): + self.transform.rotation.setValue(coin.SbVec3f(0, 0, 1), math.radians(angleInDegrees)) + + def locate(self, x, y, z): from pivy import coin + self.transform.translation.setValue(x, y, z) def scale(self, area): @@ -399,19 +411,16 @@ class Compass(object): self.transform = coin.SoTransform() darkNorthMaterial = coin.SoMaterial() - darkNorthMaterial.diffuseColor.set1Value( - 0, 0.5, 0, 0) # north dark color + darkNorthMaterial.diffuseColor.set1Value(0, 0.5, 0, 0) # north dark color lightNorthMaterial = coin.SoMaterial() - lightNorthMaterial.diffuseColor.set1Value( - 0, 0.9, 0, 0) # north light color + lightNorthMaterial.diffuseColor.set1Value(0, 0.9, 0, 0) # north light color darkGreyMaterial = coin.SoMaterial() darkGreyMaterial.diffuseColor.set1Value(0, 0.9, 0.9, 0.9) # dark color lightGreyMaterial = coin.SoMaterial() - lightGreyMaterial.diffuseColor.set1Value( - 0, 0.5, 0.5, 0.5) # light color + lightGreyMaterial.diffuseColor.set1Value(0, 0.5, 0.5, 0.5) # light color coords = self.buildCoordinates() @@ -420,22 +429,26 @@ class Compass(object): lightColorFaceset = coin.SoIndexedFaceSet() lightColorCoordinateIndex = [4, 5, 6, -1, 8, 9, 10, -1, 12, 13, 14, -1] lightColorFaceset.coordIndex.setValues( - 0, len(lightColorCoordinateIndex), lightColorCoordinateIndex) + 0, len(lightColorCoordinateIndex), lightColorCoordinateIndex + ) darkColorFaceset = coin.SoIndexedFaceSet() darkColorCoordinateIndex = [6, 7, 4, -1, 10, 11, 8, -1, 14, 15, 12, -1] darkColorFaceset.coordIndex.setValues( - 0, len(darkColorCoordinateIndex), darkColorCoordinateIndex) + 0, len(darkColorCoordinateIndex), darkColorCoordinateIndex + ) lightNorthFaceset = coin.SoIndexedFaceSet() lightNorthCoordinateIndex = [2, 3, 0, -1] lightNorthFaceset.coordIndex.setValues( - 0, len(lightNorthCoordinateIndex), lightNorthCoordinateIndex) + 0, len(lightNorthCoordinateIndex), lightNorthCoordinateIndex + ) darkNorthFaceset = coin.SoIndexedFaceSet() darkNorthCoordinateIndex = [0, 1, 2, -1] darkNorthFaceset.coordIndex.setValues( - 0, len(darkNorthCoordinateIndex), darkNorthCoordinateIndex) + 0, len(darkNorthCoordinateIndex), darkNorthCoordinateIndex + ) self.compassswitch = coin.SoSwitch() self.compassswitch.whichChild = coin.SO_SWITCH_NONE @@ -473,41 +486,31 @@ class Compass(object): # North Arrow coords.point.set1Value(0, 0, 0, 0) - coords.point.set1Value(1, COMPASS_POINTER_WIDTH, - COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(1, COMPASS_POINTER_WIDTH, COMPASS_POINTER_WIDTH, 0) coords.point.set1Value(2, 0, COMPASS_POINTER_LENGTH, 0) - coords.point.set1Value(3, -COMPASS_POINTER_WIDTH, - COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(3, -COMPASS_POINTER_WIDTH, COMPASS_POINTER_WIDTH, 0) # East Arrow coords.point.set1Value(4, 0, 0, 0) - coords.point.set1Value( - 5, COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(5, COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) coords.point.set1Value(6, COMPASS_POINTER_LENGTH, 0, 0) - coords.point.set1Value(7, COMPASS_POINTER_WIDTH, - COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(7, COMPASS_POINTER_WIDTH, COMPASS_POINTER_WIDTH, 0) # South Arrow coords.point.set1Value(8, 0, 0, 0) - coords.point.set1Value( - 9, -COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(9, -COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) coords.point.set1Value(10, 0, -COMPASS_POINTER_LENGTH, 0) - coords.point.set1Value( - 11, COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(11, COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) # West Arrow coords.point.set1Value(12, 0, 0, 0) - coords.point.set1Value(13, -COMPASS_POINTER_WIDTH, - COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(13, -COMPASS_POINTER_WIDTH, COMPASS_POINTER_WIDTH, 0) coords.point.set1Value(14, -COMPASS_POINTER_LENGTH, 0, 0) - coords.point.set1Value( - 15, -COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) + coords.point.set1Value(15, -COMPASS_POINTER_WIDTH, -COMPASS_POINTER_WIDTH, 0) return coords - - class _Site(ArchIFC.IfcProduct): """The Site object. @@ -526,15 +529,14 @@ class _Site(ArchIFC.IfcProduct): The object to turn into a site. """ - def __init__(self,obj): + def __init__(self, obj): obj.Proxy = self self.Type = "Site" self.setProperties(obj) obj.IfcType = "Site" obj.CompositionType = "ELEMENT" - - def setProperties(self,obj): + def setProperties(self, obj): """Gives the object properties unique to sites. Adds the IFC product properties, and sites' unique properties like @@ -548,64 +550,232 @@ class _Site(ArchIFC.IfcProduct): pl = obj.PropertiesList if not "Terrain" in pl: - obj.addProperty("App::PropertyLink","Terrain","Site",QT_TRANSLATE_NOOP("App::Property","The base terrain of this site"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Terrain", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The base terrain of this site"), + locked=True, + ) if not "Address" in pl: - obj.addProperty("App::PropertyString","Address","Site",QT_TRANSLATE_NOOP("App::Property","The street and house number of this site, with postal box or apartment number if needed"), locked=True) + obj.addProperty( + "App::PropertyString", + "Address", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "The street and house number of this site, with postal box or apartment number if needed", + ), + locked=True, + ) if not "PostalCode" in pl: - obj.addProperty("App::PropertyString","PostalCode","Site",QT_TRANSLATE_NOOP("App::Property","The postal or zip code of this site"), locked=True) + obj.addProperty( + "App::PropertyString", + "PostalCode", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The postal or zip code of this site"), + locked=True, + ) if not "City" in pl: - obj.addProperty("App::PropertyString","City","Site",QT_TRANSLATE_NOOP("App::Property","The city of this site"), locked=True) + obj.addProperty( + "App::PropertyString", + "City", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The city of this site"), + locked=True, + ) if not "Region" in pl: - obj.addProperty("App::PropertyString","Region","Site",QT_TRANSLATE_NOOP("App::Property","The region, province or county of this site"), locked=True) + obj.addProperty( + "App::PropertyString", + "Region", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The region, province or county of this site"), + locked=True, + ) if not "Country" in pl: - obj.addProperty("App::PropertyString","Country","Site",QT_TRANSLATE_NOOP("App::Property","The country of this site"), locked=True) + obj.addProperty( + "App::PropertyString", + "Country", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The country of this site"), + locked=True, + ) if not "Latitude" in pl: - obj.addProperty("App::PropertyFloat","Latitude","Site",QT_TRANSLATE_NOOP("App::Property","The latitude of this site"), locked=True) + obj.addProperty( + "App::PropertyFloat", + "Latitude", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The latitude of this site"), + locked=True, + ) if not "Longitude" in pl: - obj.addProperty("App::PropertyFloat","Longitude","Site",QT_TRANSLATE_NOOP("App::Property","The latitude of this site"), locked=True) + obj.addProperty( + "App::PropertyFloat", + "Longitude", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The latitude of this site"), + locked=True, + ) if not "Declination" in pl: - obj.addProperty("App::PropertyAngle","Declination","Site",QT_TRANSLATE_NOOP("App::Property","Angle between the true North and the North direction in this document"), locked=True) - if "NorthDeviation"in pl: + obj.addProperty( + "App::PropertyAngle", + "Declination", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "Angle between the true North and the North direction in this document", + ), + locked=True, + ) + if "NorthDeviation" in pl: obj.Declination = obj.NorthDeviation.Value obj.removeProperty("NorthDeviation") if not "Elevation" in pl: - obj.addProperty("App::PropertyLength","Elevation","Site",QT_TRANSLATE_NOOP("App::Property","The elevation of level 0 of this site"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Elevation", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The elevation of level 0 of this site"), + locked=True, + ) if not "Url" in pl: - obj.addProperty("App::PropertyString","Url","Site",QT_TRANSLATE_NOOP("App::Property","A URL that shows this site in a mapping website"), locked=True) + obj.addProperty( + "App::PropertyString", + "Url", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "A URL that shows this site in a mapping website" + ), + locked=True, + ) if not "Additions" in pl: - obj.addProperty("App::PropertyLinkList","Additions","Site",QT_TRANSLATE_NOOP("App::Property","Other shapes that are appended to this object"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Additions", + "Site", + QT_TRANSLATE_NOOP("App::Property", "Other shapes that are appended to this object"), + locked=True, + ) if not "Subtractions" in pl: - obj.addProperty("App::PropertyLinkList","Subtractions","Site",QT_TRANSLATE_NOOP("App::Property","Other shapes that are subtracted from this object"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Subtractions", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "Other shapes that are subtracted from this object" + ), + locked=True, + ) if not "ProjectedArea" in pl: - obj.addProperty("App::PropertyArea","ProjectedArea","Site",QT_TRANSLATE_NOOP("App::Property","The area of the projection of this object onto the XY plane"), locked=True) + obj.addProperty( + "App::PropertyArea", + "ProjectedArea", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "The area of the projection of this object onto the XY plane" + ), + locked=True, + ) if not "Perimeter" in pl: - obj.addProperty("App::PropertyLength","Perimeter","Site",QT_TRANSLATE_NOOP("App::Property","The perimeter length of the projected area"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Perimeter", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The perimeter length of the projected area"), + locked=True, + ) if not "AdditionVolume" in pl: - obj.addProperty("App::PropertyVolume","AdditionVolume","Site",QT_TRANSLATE_NOOP("App::Property","The volume of earth to be added to this terrain"), locked=True) + obj.addProperty( + "App::PropertyVolume", + "AdditionVolume", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "The volume of earth to be added to this terrain" + ), + locked=True, + ) if not "SubtractionVolume" in pl: - obj.addProperty("App::PropertyVolume","SubtractionVolume","Site",QT_TRANSLATE_NOOP("App::Property","The volume of earth to be removed from this terrain"), locked=True) + obj.addProperty( + "App::PropertyVolume", + "SubtractionVolume", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "The volume of earth to be removed from this terrain" + ), + locked=True, + ) if not "ExtrusionVector" in pl: - obj.addProperty("App::PropertyVector","ExtrusionVector","Site",QT_TRANSLATE_NOOP("App::Property","An extrusion vector to use when performing boolean operations"), locked=True) - obj.ExtrusionVector = FreeCAD.Vector(0,0,-100000) + obj.addProperty( + "App::PropertyVector", + "ExtrusionVector", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", "An extrusion vector to use when performing boolean operations" + ), + locked=True, + ) + obj.ExtrusionVector = FreeCAD.Vector(0, 0, -100000) if not "RemoveSplitter" in pl: - obj.addProperty("App::PropertyBool","RemoveSplitter","Site",QT_TRANSLATE_NOOP("App::Property","Remove splitters from the resulting shape"), locked=True) + obj.addProperty( + "App::PropertyBool", + "RemoveSplitter", + "Site", + QT_TRANSLATE_NOOP("App::Property", "Remove splitters from the resulting shape"), + locked=True, + ) if not "OriginOffset" in pl: - obj.addProperty("App::PropertyVector","OriginOffset","Site",QT_TRANSLATE_NOOP("App::Property","An optional offset between the model (0,0,0) origin and the point indicated by the geocoordinates"), locked=True) - if not hasattr(obj,"Group"): + obj.addProperty( + "App::PropertyVector", + "OriginOffset", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional offset between the model (0,0,0) origin and the point indicated by the geocoordinates", + ), + locked=True, + ) + if not hasattr(obj, "Group"): obj.addExtension("App::GroupExtensionPython") if not "IfcType" in pl: - obj.addProperty("App::PropertyEnumeration","IfcType","IFC",QT_TRANSLATE_NOOP("App::Property","The type of this object"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "IfcType", + "IFC", + QT_TRANSLATE_NOOP("App::Property", "The type of this object"), + locked=True, + ) obj.IfcType = ArchIFC.IfcTypes obj.IcfType = "Site" if not "TimeZone" in pl: - obj.addProperty("App::PropertyInteger","TimeZone","Site",QT_TRANSLATE_NOOP("App::Property","The time zone where this site is located"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "TimeZone", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The time zone where this site is located"), + locked=True, + ) if not "EPWFile" in pl: - obj.addProperty("App::PropertyFileIncluded","EPWFile","Site",QT_TRANSLATE_NOOP("App::Property","An optional EPW File for the location of this site. Refer to the Site documentation to know how to obtain one"), locked=True) + obj.addProperty( + "App::PropertyFileIncluded", + "EPWFile", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional EPW File for the location of this site. Refer to the Site documentation to know how to obtain one", + ), + locked=True, + ) if not "SunRay" in pl: - obj.addProperty("App::PropertyLink", "SunRay", "Sun", QT_TRANSLATE_NOOP("App::Property", "The generated sun ray object"), locked=True) + obj.addProperty( + "App::PropertyLink", + "SunRay", + "Sun", + QT_TRANSLATE_NOOP("App::Property", "The generated sun ray object"), + locked=True, + ) obj.setEditorMode("SunRay", ["ReadOnly", "Hidden"]) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): """FreeCAD hook run after the object is restored from a file. This method serves a dual purpose due to FreeCAD framework limitations: @@ -630,7 +800,6 @@ class _Site(ArchIFC.IfcProduct): # 1. Restore properties on the data object. self.setProperties(obj) - # 2. Trigger the restoration sequence for the associated view provider. # This block only runs in GUI mode. if FreeCAD.GuiUp and hasattr(obj, "ViewObject"): @@ -670,25 +839,31 @@ class _Site(ArchIFC.IfcProduct): # guarantees that the Property Editor has fully processed the property-add signals in # one event loop cycle *before* we apply the constraints in a subsequent cycle. from PySide import QtCore - QtCore.QTimer.singleShot(0, lambda: obj.ViewObject.Proxy.restoreConstraints(obj.ViewObject)) - def execute(self,obj): + QtCore.QTimer.singleShot( + 0, lambda: obj.ViewObject.Proxy.restoreConstraints(obj.ViewObject) + ) + + def execute(self, obj): """Method run when the object is recomputed. Perform additions and subtractions on terrain, and assign to the site's Shape. """ - if not hasattr(obj,'Shape'): # old-style Site + if not hasattr(obj, "Shape"): # old-style Site return import Part + pl = FreeCAD.Placement(obj.Placement) shape = None - if obj.Terrain is not None \ - and hasattr(obj.Terrain,'Shape') \ - and not obj.Terrain.Shape.isNull() \ - and obj.Terrain.Shape.isValid(): + if ( + obj.Terrain is not None + and hasattr(obj.Terrain, "Shape") + and not obj.Terrain.Shape.isNull() + and obj.Terrain.Shape.isValid() + ): shape = Part.Shape(obj.Terrain.Shape) # Fuse and cut operations return a shape with a default placement. # We need to transform our shape accordingly to get a consistent @@ -698,23 +873,23 @@ class _Site(ArchIFC.IfcProduct): if shape.Solids: for sub in obj.Additions: - if hasattr(sub,'Shape') and sub.Shape and sub.Shape.Solids: + if hasattr(sub, "Shape") and sub.Shape and sub.Shape.Solids: for sol in sub.Shape.Solids: shape = shape.fuse(sol) for sub in obj.Subtractions: - if hasattr(sub,'Shape') and sub.Shape and sub.Shape.Solids: + if hasattr(sub, "Shape") and sub.Shape and sub.Shape.Solids: for sol in sub.Shape.Solids: shape = shape.cut(sol) elif shape.Faces: shells = [] for sub in obj.Additions: - if hasattr(sub,'Shape') and sub.Shape and sub.Shape.Solids: + if hasattr(sub, "Shape") and sub.Shape and sub.Shape.Solids: for sol in sub.Shape.Solids: rest = shape.cut(sol) shells.append(sol.Shells[0].cut(shape.extrude(obj.ExtrusionVector))) shape = rest for sub in obj.Subtractions: - if hasattr(sub,'Shape') and sub.Shape and sub.Shape.Solids: + if hasattr(sub, "Shape") and sub.Shape and sub.Shape.Solids: for sol in sub.Shape.Solids: rest = shape.cut(sol) shells.append(sol.Shells[0].common(shape.extrude(obj.ExtrusionVector))) @@ -749,19 +924,19 @@ class _Site(ArchIFC.IfcProduct): def onChanged(self, obj, prop): ArchComponent.Component.onChanged(self, obj, prop) if prop == "Terrain" and obj.Terrain: - if obj.Terrain in getattr(obj,"Group",[]): + if obj.Terrain in getattr(obj, "Group", []): grp = obj.Group grp.remove(obj.Terrain) obj.Group = grp if FreeCAD.GuiUp: obj.Terrain.ViewObject.hide() - if prop == "Group" and getattr(obj,"Terrain",None) in obj.Group: + if prop == "Group" and getattr(obj, "Terrain", None) in obj.Group: obj.Terrain = None def getMovableChildren(self, obj): return obj.Additions + obj.Subtractions - def computeAreas(self,obj): + def computeAreas(self, obj): """Compute the area, perimeter length, and volume of the terrain shape. Compute the area of the terrain projected onto an XY plane, IE: @@ -775,7 +950,7 @@ class _Site(ArchIFC.IfcProduct): Assign these values to their respective site properties. """ - if not hasattr(obj,"Perimeter"): # check we have a latest version site + if not hasattr(obj, "Perimeter"): # check we have a latest version site return if not obj.Shape.Faces: @@ -791,6 +966,7 @@ class _Site(ArchIFC.IfcProduct): import TechDraw import Part + area = 0 perim = 0 addvol = 0 @@ -798,13 +974,13 @@ class _Site(ArchIFC.IfcProduct): edges = [] for face in obj.Shape.Faces: - if face.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1)) < 1.5707: - edges.extend(TechDraw.project(face,FreeCAD.Vector(0,0,1))[0].Edges) + if face.normalAt(0, 0).getAngle(FreeCAD.Vector(0, 0, 1)) < 1.5707: + edges.extend(TechDraw.project(face, FreeCAD.Vector(0, 0, 1))[0].Edges) outer = TechDraw.findOuterWire(edges) # compute area try: - area = Part.Face(outer).Area # outer.Area does not always work. + area = Part.Face(outer).Area # outer.Area does not always work. except Part.OCCError: print("Error computing areas for", obj.Label) area = 0 @@ -832,8 +1008,7 @@ class _Site(ArchIFC.IfcProduct): if obj.SubtractionVolume.Value != subvol: obj.SubtractionVolume = subvol - def addObject(self,obj,child): - + def addObject(self, obj, child): "Adds an object to the group of this BuildingPart" if not child in obj.Group: @@ -845,7 +1020,7 @@ class _Site(ArchIFC.IfcProduct): return None - def loads(self,state): + def loads(self, state): self.Type = "Site" @@ -859,15 +1034,16 @@ class _ViewProviderSite: The view provider to turn into a site view provider. """ - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self vobj.addExtension("Gui::ViewProviderGroupExtensionPython") self.setProperties(vobj) # Defer the constraint and default value setup until after the GUI is fully initialized. from PySide import QtCore + QtCore.QTimer.singleShot(0, lambda: self.restoreConstraints(vobj)) - def setProperties(self,vobj): + def setProperties(self, vobj): """Give the site view provider its site view provider specific properties. These include solar diagram and compass data, dealing the orientation @@ -878,41 +1054,148 @@ class _ViewProviderSite: pl = vobj.PropertiesList if not "WindRose" in pl: - vobj.addProperty("App::PropertyBool","WindRose","Site",QT_TRANSLATE_NOOP("App::Property","Show wind rose diagram or not. Uses solar diagram scale. Needs Ladybug module"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "WindRose", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "Show wind rose diagram or not. Uses solar diagram scale. Needs Ladybug module", + ), + locked=True, + ) if not "SolarDiagram" in pl: - vobj.addProperty("App::PropertyBool","SolarDiagram","Site",QT_TRANSLATE_NOOP("App::Property","Show solar diagram or not"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "SolarDiagram", + "Site", + QT_TRANSLATE_NOOP("App::Property", "Show solar diagram or not"), + locked=True, + ) if not "SolarDiagramScale" in pl: - vobj.addProperty("App::PropertyFloat","SolarDiagramScale","Site",QT_TRANSLATE_NOOP("App::Property","The scale of the solar diagram"), locked=True) - vobj.SolarDiagramScale = 20000.0 # Default diagram of 20 m radius + vobj.addProperty( + "App::PropertyFloat", + "SolarDiagramScale", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The scale of the solar diagram"), + locked=True, + ) + vobj.SolarDiagramScale = 20000.0 # Default diagram of 20 m radius if not "SolarDiagramPosition" in pl: - vobj.addProperty("App::PropertyVector","SolarDiagramPosition","Site",QT_TRANSLATE_NOOP("App::Property","The position of the solar diagram"), locked=True) + vobj.addProperty( + "App::PropertyVector", + "SolarDiagramPosition", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The position of the solar diagram"), + locked=True, + ) if not "SolarDiagramColor" in pl: - vobj.addProperty("App::PropertyColor","SolarDiagramColor","Site",QT_TRANSLATE_NOOP("App::Property","The color of the solar diagram"), locked=True) - vobj.SolarDiagramColor = (0.16,0.16,0.25) + vobj.addProperty( + "App::PropertyColor", + "SolarDiagramColor", + "Site", + QT_TRANSLATE_NOOP("App::Property", "The color of the solar diagram"), + locked=True, + ) + vobj.SolarDiagramColor = (0.16, 0.16, 0.25) if not "Orientation" in pl: - vobj.addProperty("App::PropertyEnumeration", "Orientation", "Site", QT_TRANSLATE_NOOP( - "App::Property", "When set to 'True North' the whole geometry will be rotated to match the true north of this site"), locked=True) + vobj.addProperty( + "App::PropertyEnumeration", + "Orientation", + "Site", + QT_TRANSLATE_NOOP( + "App::Property", + "When set to 'True North' the whole geometry will be rotated to match the true north of this site", + ), + locked=True, + ) vobj.Orientation = ["Project North", "True North"] vobj.Orientation = "Project North" if not "Compass" in pl: - vobj.addProperty("App::PropertyBool", "Compass", "Compass", QT_TRANSLATE_NOOP("App::Property", "Show compass or not"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "Compass", + "Compass", + QT_TRANSLATE_NOOP("App::Property", "Show compass or not"), + locked=True, + ) if not "CompassRotation" in pl: - vobj.addProperty("App::PropertyAngle", "CompassRotation", "Compass", QT_TRANSLATE_NOOP("App::Property", "The rotation of the Compass relative to the Site"), locked=True) + vobj.addProperty( + "App::PropertyAngle", + "CompassRotation", + "Compass", + QT_TRANSLATE_NOOP( + "App::Property", "The rotation of the Compass relative to the Site" + ), + locked=True, + ) if not "CompassPosition" in pl: - vobj.addProperty("App::PropertyVector", "CompassPosition", "Compass", QT_TRANSLATE_NOOP("App::Property", "The position of the Compass relative to the Site placement"), locked=True) + vobj.addProperty( + "App::PropertyVector", + "CompassPosition", + "Compass", + QT_TRANSLATE_NOOP( + "App::Property", "The position of the Compass relative to the Site placement" + ), + locked=True, + ) if not "UpdateDeclination" in pl: - vobj.addProperty("App::PropertyBool", "UpdateDeclination", "Compass", QT_TRANSLATE_NOOP("App::Property", "Update the Declination value based on the compass rotation"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "UpdateDeclination", + "Compass", + QT_TRANSLATE_NOOP( + "App::Property", "Update the Declination value based on the compass rotation" + ), + locked=True, + ) if not "ShowSunPosition" in pl: - vobj.addProperty("App::PropertyBool", "ShowSunPosition", "Sun", QT_TRANSLATE_NOOP("App::Property", "Show the sun position for a specific date and time"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowSunPosition", + "Sun", + QT_TRANSLATE_NOOP( + "App::Property", "Show the sun position for a specific date and time" + ), + locked=True, + ) if not "SunDateMonth" in pl: - vobj.addProperty("App::PropertyIntegerConstraint", "SunDateMonth", "Sun", QT_TRANSLATE_NOOP("App::Property", "The month of the year to show the sun position"), locked=True) + vobj.addProperty( + "App::PropertyIntegerConstraint", + "SunDateMonth", + "Sun", + QT_TRANSLATE_NOOP( + "App::Property", "The month of the year to show the sun position" + ), + locked=True, + ) if not "SunDateDay" in pl: - vobj.addProperty("App::PropertyIntegerConstraint", "SunDateDay", "Sun", QT_TRANSLATE_NOOP("App::Property", "The day of the month to show the sun position"), locked=True) + vobj.addProperty( + "App::PropertyIntegerConstraint", + "SunDateDay", + "Sun", + QT_TRANSLATE_NOOP("App::Property", "The day of the month to show the sun position"), + locked=True, + ) if not "SunTimeHour" in pl: - vobj.addProperty("App::PropertyFloatConstraint", "SunTimeHour", "Sun", QT_TRANSLATE_NOOP("App::Property", "The hour of the day to show the sun position"), locked=True) + vobj.addProperty( + "App::PropertyFloatConstraint", + "SunTimeHour", + "Sun", + QT_TRANSLATE_NOOP("App::Property", "The hour of the day to show the sun position"), + locked=True, + ) if not "ShowHourLabels" in pl: - vobj.addProperty("App::PropertyBool", "ShowHourLabels", "Sun", QT_TRANSLATE_NOOP("App::Property", "Show text labels for key hours on the sun path"), locked=True) - vobj.ShowHourLabels = True # Show hour labels by default + vobj.addProperty( + "App::PropertyBool", + "ShowHourLabels", + "Sun", + QT_TRANSLATE_NOOP( + "App::Property", "Show text labels for key hours on the sun path" + ), + locked=True, + ) + vobj.ShowHourLabels = True # Show hour labels by default def restoreConstraints(self, vobj): """Re-apply non-persistent property constraints after a file load. @@ -924,21 +1207,21 @@ class _ViewProviderSite: saved_month = vobj.SunDateMonth if vobj.SunDateMonth != 0 else 6 vobj.SunDateMonth = (saved_month, 1, 12, 1) else: - vobj.SunDateMonth = (6, 1, 12, 1) # Default to June + vobj.SunDateMonth = (6, 1, 12, 1) # Default to June if "SunDateDay" in pl: saved_day = vobj.SunDateDay if vobj.SunDateDay != 0 else 21 vobj.SunDateDay = (saved_day, 1, 31, 1) else: # 31 is a safe maximum; the datetime object will handle invalid dates like Feb 31. - vobj.SunDateDay = (21, 1, 31, 1) # Default to the 21st (solstice) + vobj.SunDateDay = (21, 1, 31, 1) # Default to the 21st (solstice) if "SunTimeHour" in pl: saved_hour = vobj.SunTimeHour if abs(vobj.SunTimeHour) > 1e-9 else 12.0 vobj.SunTimeHour = (saved_hour, 0.0, 23.5, 0.5) else: # Use 23.5 to avoid issues with hour 24 - vobj.SunTimeHour = (12.0, 0.0, 23.5, 0.5) # Default to noon + vobj.SunTimeHour = (12.0, 0.0, 23.5, 0.5) # Default to noon def getIcon(self): """Return the path to the appropriate icon. @@ -950,6 +1233,7 @@ class _ViewProviderSite: """ import Arch_rc + return ":/icons/Arch_Site_Tree.svg" def claimChildren(self): @@ -967,11 +1251,13 @@ class _ViewProviderSite: """ objs = [] - if hasattr(self,"Object"): - objs = self.Object.Group+[self.Object.Terrain] - if hasattr(self.Object,"Additions") and params.get_param_arch("swallowAdditions"): + if hasattr(self, "Object"): + objs = self.Object.Group + [self.Object.Terrain] + if hasattr(self.Object, "Additions") and params.get_param_arch("swallowAdditions"): objs.extend(self.Object.Additions) - if hasattr(self.Object,"Subtractions") and params.get_param_arch("swallowSubtractions"): + if hasattr(self.Object, "Subtractions") and params.get_param_arch( + "swallowSubtractions" + ): objs.extend(self.Object.Subtractions) return objs @@ -980,6 +1266,7 @@ class _ViewProviderSite: return None import ArchComponent + taskd = ArchComponent.ComponentTaskPanel() taskd.obj = self.Object taskd.update() @@ -995,34 +1282,33 @@ class _ViewProviderSite: def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return - actionEdit = QtGui.QAction(translate("Arch", "Edit"), - menu) - QtCore.QObject.connect(actionEdit, - QtCore.SIGNAL("triggered()"), - self.edit) + actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) + QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) menu.addAction(actionEdit) - actionToggleSubcomponents = QtGui.QAction(QtGui.QIcon(":/icons/Arch_ToggleSubs.svg"), - translate("Arch", "Toggle Subcomponents"), - menu) - QtCore.QObject.connect(actionToggleSubcomponents, - QtCore.SIGNAL("triggered()"), - self.toggleSubcomponents) + actionToggleSubcomponents = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_ToggleSubs.svg"), + translate("Arch", "Toggle Subcomponents"), + menu, + ) + QtCore.QObject.connect( + actionToggleSubcomponents, QtCore.SIGNAL("triggered()"), self.toggleSubcomponents + ) menu.addAction(actionToggleSubcomponents) # The default Part::FeaturePython context menu contains a `Set colors` # option. This option does not work well for Site objects. We therefore # override this menu and have to add our own `Transform` item. # To override the default menu this function must return `True`. - actionTransform = QtGui.QAction(FreeCADGui.getIcon("Std_TransformManip.svg"), - translate("Command", "Transform"), # Context `Command` instead of `Arch`. - menu) - QtCore.QObject.connect(actionTransform, - QtCore.SIGNAL("triggered()"), - self.transform) + actionTransform = QtGui.QAction( + FreeCADGui.getIcon("Std_TransformManip.svg"), + translate("Command", "Transform"), # Context `Command` instead of `Arch`. + menu, + ) + QtCore.QObject.connect(actionTransform, QtCore.SIGNAL("triggered()"), self.transform) menu.addAction(actionTransform) return True @@ -1036,13 +1322,14 @@ class _ViewProviderSite: def transform(self): FreeCADGui.ActiveDocument.setEdit(self.Object, 1) - def attach(self,vobj): + def attach(self, vobj): """Adds the solar diagram and compass to the coin scenegraph, but does not add display modes. """ self.Object = vobj.Object from pivy import coin + basesep = coin.SoSeparator() vobj.Annotation.addChild(basesep) self.color = coin.SoBaseColor() @@ -1065,13 +1352,13 @@ class _ViewProviderSite: self.rotateCompass(vobj) vobj.Annotation.addChild(self.compass.rootNode) - self.sunSwitch = coin.SoSwitch() # Toggle the sun sphere on and off - self.sunSwitch.whichChild = -1 # -1 means hidden + self.sunSwitch = coin.SoSwitch() # Toggle the sun sphere on and off + self.sunSwitch.whichChild = -1 # -1 means hidden - self.sunSep = coin.SoSeparator() # A separator to group all sun elements - self.sunTransform = coin.SoTransform() # Position the sphere + self.sunSep = coin.SoSeparator() # A separator to group all sun elements + self.sunTransform = coin.SoTransform() # Position the sphere self.sunMaterial = coin.SoMaterial() - self.sunMaterial.diffuseColor.setValue(1, 1, 0) # Yellow color + self.sunMaterial.diffuseColor.setValue(1, 1, 0) # Yellow color self.sunSphere = coin.SoSphere() # Assemble the scene graph for the sphere @@ -1087,7 +1374,7 @@ class _ViewProviderSite: separator = coin.SoSeparator() material = coin.SoMaterial() material.diffuseColor.setValue(color_tuple) - node = coin.SoSeparator() # This will hold the geometry + node = coin.SoSeparator() # This will hold the geometry separator.addChild(material) separator.addChild(node) self.sunSwitch.addChild(separator) @@ -1095,17 +1382,17 @@ class _ViewProviderSite: # Create nodes for different segments of the sun path representing # morning, midday, and afternoon with distinct colors. - self.sunPathMorningNode = setup_path_segment((0.2, 0.8, 1.0)) # Sky Blue + self.sunPathMorningNode = setup_path_segment((0.2, 0.8, 1.0)) # Sky Blue self.sunPathMiddayNode = setup_path_segment((1.0, 0.75, 0.0)) # Golden Yellow / Amber - self.sunPathAfternoonNode = setup_path_segment((1.0, 0.35, 0.0)) # Orange-Red + self.sunPathAfternoonNode = setup_path_segment((1.0, 0.35, 0.0)) # Orange-Red # Create nodes for the hour marker points. self.hourMarkerSep = coin.SoSeparator() self.hourMarkerMaterial = coin.SoMaterial() - self.hourMarkerMaterial.diffuseColor.setValue(0.8, 0.8, 0.8) # Grey + self.hourMarkerMaterial.diffuseColor.setValue(0.8, 0.8, 0.8) # Grey self.hourMarkerDrawStyle = coin.SoDrawStyle() - self.hourMarkerDrawStyle.pointSize.setValue(5) # Set a visible point size (e.g., 5 pixels) + self.hourMarkerDrawStyle.pointSize.setValue(5) # Set a visible point size (e.g., 5 pixels) self.hourMarkerDrawStyle.style.setValue(coin.SoDrawStyle.POINTS) self.hourMarkerCoords = coin.SoCoordinate3() @@ -1120,13 +1407,13 @@ class _ViewProviderSite: # Create nodes for the hour labels. self.hourLabelSep = coin.SoSeparator() self.hourLabelMaterial = coin.SoMaterial() - self.hourLabelMaterial.diffuseColor.setValue(0.8, 0.8, 0.8) # Same grey as markers + self.hourLabelMaterial.diffuseColor.setValue(0.8, 0.8, 0.8) # Same grey as markers self.hourLabelFont = coin.SoFont() self.hourLabelSep.addChild(self.hourLabelMaterial) self.hourLabelSep.addChild(self.hourLabelFont) self.sunSwitch.addChild(self.hourLabelSep) - def updateData(self,obj,prop): + def updateData(self, obj, prop): """Method called when the host object has a property changed. If the Longitude or Latitude has changed, set the SolarDiagram to @@ -1142,10 +1429,10 @@ class _ViewProviderSite: The name of the property that has changed. """ - if prop in ["Longitude","Latitude","TimeZone"]: - self.onChanged(obj.ViewObject,"SolarDiagram") + if prop in ["Longitude", "Latitude", "TimeZone"]: + self.onChanged(obj.ViewObject, "SolarDiagram") elif prop == "Declination": - self.onChanged(obj.ViewObject,"SolarDiagramPosition") + self.onChanged(obj.ViewObject, "SolarDiagramPosition") self.updateTrueNorthRotation() elif prop == "Terrain": self.updateCompassLocation(obj.ViewObject) @@ -1155,7 +1442,7 @@ class _ViewProviderSite: elif prop == "ProjectedArea": self.updateCompassScale(obj.ViewObject) - def addDisplaymodeTerrainSwitches(self,vobj): + def addDisplaymodeTerrainSwitches(self, vobj): """Adds 'terrain' switches to the 4 default display modes. If the Terrain property of the site is None, the 'normal' display can @@ -1172,8 +1459,12 @@ class _ViewProviderSite: if not hasattr(self, "terrain_switches"): if vobj.RootNode.getNumChildren(): - main_switch = gui_utils.find_coin_node(vobj.RootNode, coin.SoSwitch) # The display mode switch. - if main_switch is not None and main_switch.getNumChildren() == 4: # Check if all display modes are available. + main_switch = gui_utils.find_coin_node( + vobj.RootNode, coin.SoSwitch + ) # The display mode switch. + if ( + main_switch is not None and main_switch.getNumChildren() == 4 + ): # Check if all display modes are available. self.terrain_switches = [] for node in tuple(main_switch.getChildren()): new_switch = coin.SoSwitch() @@ -1189,10 +1480,10 @@ class _ViewProviderSite: new_switch.whichChild = 0 node.addChild(new_switch) for i in range(len(child_list)): - node.removeChild(0) # Remove the original children. + node.removeChild(0) # Remove the original children. self.terrain_switches.append(new_switch) - def updateDisplaymodeTerrainSwitches(self,vobj): + def updateDisplaymodeTerrainSwitches(self, vobj): """Updates the 'terrain' switches.""" if not hasattr(self, "terrain_switches"): @@ -1202,13 +1493,13 @@ class _ViewProviderSite: for switch in self.terrain_switches: switch.whichChild = idx - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): from pivy import coin - if prop == 'Visibility': + if prop == "Visibility": if vobj.Visibility: # When the site becomes visible, check if the sun elements should also be shown. - if hasattr(vobj, 'ShowSunPosition') and vobj.ShowSunPosition: + if hasattr(vobj, "ShowSunPosition") and vobj.ShowSunPosition: self.sunSwitch.whichChild = coin.SO_SWITCH_ALL else: # When the site is hidden, always hide the sun elements. @@ -1221,26 +1512,31 @@ class _ViewProviderSite: self.updateDisplaymodeTerrainSwitches(vobj) if prop == "SolarDiagramPosition": - if hasattr(vobj,"SolarDiagramPosition"): + if hasattr(vobj, "SolarDiagramPosition"): p = vobj.SolarDiagramPosition - self.coords.translation.setValue([p.x,p.y,p.z]) - if hasattr(vobj.Object,"Declination"): + self.coords.translation.setValue([p.x, p.y, p.z]) + if hasattr(vobj.Object, "Declination"): from pivy import coin - self.coords.rotation.setValue(coin.SbVec3f((0,0,1)),math.radians(vobj.Object.Declination.Value)) + + self.coords.rotation.setValue( + coin.SbVec3f((0, 0, 1)), math.radians(vobj.Object.Declination.Value) + ) elif prop == "SolarDiagramColor": - if hasattr(vobj,"SolarDiagramColor"): + if hasattr(vobj, "SolarDiagramColor"): l = vobj.SolarDiagramColor - self.color.rgb.setValue([l[0],l[1],l[2]]) + self.color.rgb.setValue([l[0], l[1], l[2]]) elif "SolarDiagram" in prop: - if hasattr(self,"diagramnode"): + if hasattr(self, "diagramnode"): self.diagramsep.removeChild(self.diagramnode) del self.diagramnode - if hasattr(vobj,"SolarDiagram") and hasattr(vobj,"SolarDiagramScale"): + if hasattr(vobj, "SolarDiagram") and hasattr(vobj, "SolarDiagramScale"): if vobj.SolarDiagram: tz = 0 - if hasattr(vobj.Object,"TimeZone"): + if hasattr(vobj.Object, "TimeZone"): tz = vobj.Object.TimeZone - self.diagramnode = makeSolarDiagram(vobj.Object.Longitude,vobj.Object.Latitude,vobj.SolarDiagramScale,tz=tz) + self.diagramnode = makeSolarDiagram( + vobj.Object.Longitude, vobj.Object.Latitude, vobj.SolarDiagramScale, tz=tz + ) if self.diagramnode: self.diagramsep.addChild(self.diagramnode) self.diagramswitch.whichChild = 0 @@ -1249,27 +1545,39 @@ class _ViewProviderSite: else: self.diagramswitch.whichChild = -1 elif prop in [ - "ShowSunPosition", "SunDateMonth", "SunDateDay", "SunTimeHour", - "SolarDiagramScale", "SolarDiagramPosition", "ShowHourLabels"]: + "ShowSunPosition", + "SunDateMonth", + "SunDateDay", + "SunTimeHour", + "SolarDiagramScale", + "SolarDiagramPosition", + "ShowHourLabels", + ]: # During file load or property creation, this method can be triggered # before all necessary properties are available. This guard ensures # that the sun position is only updated when the object is in a consistent state. - if all(hasattr(vobj, p) for p in ["ShowSunPosition", "SunDateMonth", "SunDateDay", "SunTimeHour"]): - self.updateSunPosition(vobj) + if all( + hasattr(vobj, p) + for p in ["ShowSunPosition", "SunDateMonth", "SunDateDay", "SunTimeHour"] + ): + self.updateSunPosition(vobj) elif prop == "WindRose": - if hasattr(self,"windrosenode"): + if hasattr(self, "windrosenode"): del self.windrosenode - if hasattr(vobj,"WindRose"): + if hasattr(vobj, "WindRose"): if vobj.WindRose: - if hasattr(vobj.Object,"EPWFile") and vobj.Object.EPWFile: + if hasattr(vobj.Object, "EPWFile") and vobj.Object.EPWFile: with temp_logger_level(logging.WARNING): try: import ladybug - logging.getLogger('ladybug').propagate = False + + logging.getLogger("ladybug").propagate = False except ImportError: pass else: - self.windrosenode = makeWindRose(vobj.Object.EPWFile,vobj.SolarDiagramScale) + self.windrosenode = makeWindRose( + vobj.Object.EPWFile, vobj.SolarDiagramScale + ) if self.windrosenode: self.windrosesep.addChild(self.windrosenode) self.windroseswitch.whichChild = 0 @@ -1277,13 +1585,13 @@ class _ViewProviderSite: del self.windrosenode else: self.windroseswitch.whichChild = -1 - elif prop == 'Visibility': + elif prop == "Visibility": if vobj.Visibility: self.updateCompassVisibility(vobj) else: self.compass.hide() - elif prop == 'Orientation': - if vobj.Orientation == 'True North': + elif prop == "Orientation": + if vobj.Orientation == "True North": self.addTrueNorthRotation() else: self.removeTrueNorthRotation() @@ -1297,29 +1605,32 @@ class _ViewProviderSite: elif prop == "CompassPosition": self.updateCompassLocation(vobj) - def updateDeclination(self,vobj): + def updateDeclination(self, vobj): """Update the declination of the compass Update the declination by adding together how the site has been rotated within the document, and the rotation of the site compass. """ - if not hasattr(vobj, 'UpdateDeclination') or not vobj.UpdateDeclination: + if not hasattr(vobj, "UpdateDeclination") or not vobj.UpdateDeclination: return compassRotation = vobj.CompassRotation.Value - siteRotation = math.degrees(vobj.Object.Placement.Rotation.Angle) # This assumes Rotation.axis = (0,0,1) + siteRotation = math.degrees( + vobj.Object.Placement.Rotation.Angle + ) # This assumes Rotation.axis = (0,0,1) vobj.Object.Declination = compassRotation + siteRotation def addTrueNorthRotation(self): - if hasattr(self, 'trueNorthRotation') and self.trueNorthRotation is not None: + if hasattr(self, "trueNorthRotation") and self.trueNorthRotation is not None: return if not FreeCADGui.ActiveDocument.ActiveView: return - if not hasattr(FreeCADGui.ActiveDocument.ActiveView, 'getSceneGraph'): + if not hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph"): return from pivy import coin + self.trueNorthRotation = coin.SoTransform() sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() sg.insertChild(self.trueNorthRotation, 0) @@ -1327,13 +1638,13 @@ class _ViewProviderSite: def removeTrueNorthRotation(self): - if not hasattr(self, 'trueNorthRotation'): + if not hasattr(self, "trueNorthRotation"): return if self.trueNorthRotation is None: return if not FreeCADGui.ActiveDocument.ActiveView: return - if not hasattr(FreeCADGui.ActiveDocument.ActiveView, 'getSceneGraph'): + if not hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph"): return sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() @@ -1342,16 +1653,17 @@ class _ViewProviderSite: def updateTrueNorthRotation(self): - if hasattr(self, 'trueNorthRotation') and self.trueNorthRotation is not None: + if hasattr(self, "trueNorthRotation") and self.trueNorthRotation is not None: from pivy import coin + angle = self.Object.Declination.Value self.trueNorthRotation.rotation.setValue(coin.SbVec3f(0, 0, 1), math.radians(-angle)) def updateCompassVisibility(self, vobj): - if not hasattr(self, 'compass'): + if not hasattr(self, "compass"): return - show = hasattr(vobj, 'Compass') and vobj.Compass + show = hasattr(vobj, "Compass") and vobj.Compass if show: self.compass.show() else: @@ -1359,14 +1671,14 @@ class _ViewProviderSite: def rotateCompass(self, vobj): - if not hasattr(self, 'compass'): + if not hasattr(self, "compass"): return - if hasattr(vobj, 'CompassRotation'): + if hasattr(vobj, "CompassRotation"): self.compass.rotate(vobj.CompassRotation.Value) def updateCompassLocation(self, vobj): - if not hasattr(self, 'compass'): + if not hasattr(self, "compass"): return if not vobj.Object.Shape: return @@ -1378,11 +1690,11 @@ class _ViewProviderSite: x = vobj.CompassPosition.x y = vobj.CompassPosition.y z = boundBox.ZMax = pos.z - self.compass.locate(x,y,z+1000) + self.compass.locate(x, y, z + 1000) def updateCompassScale(self, vobj): - if not hasattr(self, 'compass'): + if not hasattr(self, "compass"): return self.compass.scale(vobj.Object.ProjectedArea) @@ -1390,7 +1702,7 @@ class _ViewProviderSite: return None - def loads(self,state): + def loads(self, state): """Restore hook for view provider instances created by the loader. During document deserialization the Python instance may be @@ -1500,13 +1812,19 @@ class _ViewProviderSite: # AttributeError and is harmless because updateSunPosition will be called again when the # object becomes consistent, or the scheduled retry will run after attach() finishes. required_attrs = [ - 'sunPathMorningNode', 'sunPathMiddayNode', 'sunPathAfternoonNode', - 'hourMarkerCoords', 'hourLabelSep', 'sunTransform', 'sunSphere' + "sunPathMorningNode", + "sunPathMiddayNode", + "sunPathAfternoonNode", + "hourMarkerCoords", + "hourLabelSep", + "sunTransform", + "sunSphere", ] for a in required_attrs: if not hasattr(self, a): try: from PySide import QtCore + QtCore.QTimer.singleShot(0, lambda: self.updateSunPosition(vobj)) except Exception: # If Qt is unavailable or scheduling fails, just return silently. @@ -1521,43 +1839,58 @@ class _ViewProviderSite: self.hourMarkerCoords.point.deleteValues(0) if not vobj.ShowSunPosition: - self.sunSwitch.whichChild = -1 # Hide the Pivy sphere and path + self.sunSwitch.whichChild = -1 # Hide the Pivy sphere and path ray_object = getattr(obj, "SunRay", None) if ray_object and hasattr(ray_object, "ViewObject"): obj.SunRay.ViewObject.Visibility = False return - self.sunSwitch.whichChild = coin.SO_SWITCH_ALL # Show sphere and path + self.sunSwitch.whichChild = coin.SO_SWITCH_ALL # Show sphere and path dt_object_for_label = None with temp_logger_level(logging.WARNING): try: from ladybug import location, sunpath - logging.getLogger('ladybug').propagate = False - loc = location.Location(latitude=obj.Latitude, longitude=obj.Longitude, time_zone=obj.TimeZone) + + logging.getLogger("ladybug").propagate = False + loc = location.Location( + latitude=obj.Latitude, longitude=obj.Longitude, time_zone=obj.TimeZone + ) sp = sunpath.Sunpath.from_location(loc) is_ladybug = True except ImportError: try: import pysolar.solar as solar + is_ladybug = False except ImportError: - FreeCAD.Console.PrintError("Ladybug or Pysolar module not found. Cannot calculate sun position.\n") + FreeCAD.Console.PrintError( + "Ladybug or Pysolar module not found. Cannot calculate sun position.\n" + ) return morning_points, midday_points, afternoon_points = [], [], [] - self.hourMarkerCoords.point.deleteValues(0) # Clear previous marker coordinates + self.hourMarkerCoords.point.deleteValues(0) # Clear previous marker coordinates marker_coords = [] - for hour_float in [h / 2.0 for h in range(48)]: # Loop from 0.0 to 23.5 + for hour_float in [h / 2.0 for h in range(48)]: # Loop from 0.0 to 23.5 if is_ladybug: - sun = sp.calculate_sun(month=vobj.SunDateMonth, day=vobj.SunDateDay, hour=hour_float) + sun = sp.calculate_sun( + month=vobj.SunDateMonth, day=vobj.SunDateDay, hour=hour_float + ) alt = sun.altitude az = sun.azimuth else: tz = datetime.timezone(datetime.timedelta(hours=obj.TimeZone)) - dt = datetime.datetime(2023, vobj.SunDateMonth, vobj.SunDateDay, int(hour_float), int((hour_float % 1)*60), tzinfo=tz) + dt = datetime.datetime( + 2023, + vobj.SunDateMonth, + vobj.SunDateDay, + int(hour_float), + int((hour_float % 1) * 60), + tzinfo=tz, + ) alt = solar.get_altitude(obj.Latitude, obj.Longitude, dt) az = solar.get_azimuth(obj.Latitude, obj.Longitude, dt) @@ -1587,7 +1920,9 @@ class _ViewProviderSite: # Create a transform to position the text slightly offset from the marker text_transform = coin.SoTransform() - offset_vec = FreeCAD.Vector(x, y, z).normalize() * (vobj.SolarDiagramScale * 0.03) + offset_vec = FreeCAD.Vector(x, y, z).normalize() * ( + vobj.SolarDiagramScale * 0.03 + ) text_pos = FreeCAD.Vector(x, y, z).add(offset_vec) text_transform.translation.setValue(text_pos.x, text_pos.y, text_pos.z) @@ -1625,12 +1960,27 @@ class _ViewProviderSite: # Sun sphere and sun ray logic if is_ladybug: - sun = sp.calculate_sun(month=vobj.SunDateMonth, day=vobj.SunDateDay, hour=vobj.SunTimeHour) + sun = sp.calculate_sun( + month=vobj.SunDateMonth, day=vobj.SunDateDay, hour=vobj.SunTimeHour + ) altitude_deg, azimuth_deg = sun.altitude, sun.azimuth - dt_object_for_label = datetime.datetime(2023, vobj.SunDateMonth, vobj.SunDateDay, int(vobj.SunTimeHour), int((vobj.SunTimeHour % 1)*60)) + dt_object_for_label = datetime.datetime( + 2023, + vobj.SunDateMonth, + vobj.SunDateDay, + int(vobj.SunTimeHour), + int((vobj.SunTimeHour % 1) * 60), + ) else: tz = datetime.timezone(datetime.timedelta(hours=obj.TimeZone)) - dt_object_for_label = datetime.datetime(2023, vobj.SunDateMonth, vobj.SunDateDay, int(vobj.SunTimeHour), int((vobj.SunTimeHour % 1)*60), tzinfo=tz) + dt_object_for_label = datetime.datetime( + 2023, + vobj.SunDateMonth, + vobj.SunDateDay, + int(vobj.SunTimeHour), + int((vobj.SunTimeHour % 1) * 60), + tzinfo=tz, + ) altitude_deg = solar.get_altitude(obj.Latitude, obj.Longitude, dt_object_for_label) azimuth_deg = solar.get_azimuth(obj.Latitude, obj.Longitude, dt_object_for_label) @@ -1640,7 +1990,9 @@ class _ViewProviderSite: x = math.cos(azimuth_rad) * xy_proj y = math.sin(azimuth_rad) * xy_proj z = math.sin(altitude_rad) * vobj.SolarDiagramScale - sun_pos_3d = vobj.SolarDiagramPosition.add(FreeCAD.Vector(x, y, z)) # Final absolute position + sun_pos_3d = vobj.SolarDiagramPosition.add( + FreeCAD.Vector(x, y, z) + ) # Final absolute position self.sunTransform.translation.setValue(sun_pos_3d.x, sun_pos_3d.y, sun_pos_3d.z) self.sunSphere.radius = vobj.SolarDiagramScale * 0.02 @@ -1673,17 +2025,37 @@ class _ViewProviderSite: # Ignore failures to set property on legacy objects, but log them FreeCAD.Console.PrintWarning( f"ArchSite: could not assign SunRay on object {obj.Label}: {e}\n" - ) + ) ray_object.recompute() # Add and update custom data properties if not hasattr(ray_object, "Altitude"): - ray_object.addProperty("App::PropertyAngle", "Altitude", "Sun Data", QT_TRANSLATE_NOOP("App::Property", "The altitude of the sun above the horizon"), locked=True) + ray_object.addProperty( + "App::PropertyAngle", + "Altitude", + "Sun Data", + QT_TRANSLATE_NOOP("App::Property", "The altitude of the sun above the horizon"), + locked=True, + ) ray_object.setEditorMode("Altitude", ["ReadOnly", "Hidden"]) - ray_object.addProperty("App::PropertyAngle", "Azimuth", "Sun Data", QT_TRANSLATE_NOOP("App::Property", "The compass direction of the sun (0° is North)"), locked=True) + ray_object.addProperty( + "App::PropertyAngle", + "Azimuth", + "Sun Data", + QT_TRANSLATE_NOOP( + "App::Property", "The compass direction of the sun (0° is North)" + ), + locked=True, + ) ray_object.setEditorMode("Azimuth", ["ReadOnly", "Hidden"]) - ray_object.addProperty("App::PropertyString", "Time", "Sun Data", QT_TRANSLATE_NOOP("App::Property", "The date and time for this sun position"), locked=True) + ray_object.addProperty( + "App::PropertyString", + "Time", + "Sun Data", + QT_TRANSLATE_NOOP("App::Property", "The date and time for this sun position"), + locked=True, + ) ray_object.setEditorMode("Time", ["ReadOnly", "Hidden"]) ray_object.Altitude = math.radians(altitude_deg) diff --git a/src/Mod/BIM/ArchSketchObject.py b/src/Mod/BIM/ArchSketchObject.py index 71ace8352b..20700c949c 100644 --- a/src/Mod/BIM/ArchSketchObject.py +++ b/src/Mod/BIM/ArchSketchObject.py @@ -25,10 +25,12 @@ import ArchWindow from PySide.QtCore import QT_TRANSLATE_NOOP + class ArchSketchObject: def __init__(self, obj): pass + class ArchSketch(ArchSketchObject): def __init__(self, obj): pass @@ -43,9 +45,14 @@ class ArchSketch(ArchSketchObject): pass else: if "Hosts" not in prop: - fp.addProperty("App::PropertyLinkList","Hosts","Window", - QT_TRANSLATE_NOOP("App::Property", - "The objects that host this window"), locked=True) - # Arch Window's code + fp.addProperty( + "App::PropertyLinkList", + "Hosts", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The objects that host this window"), + locked=True, + ) + # Arch Window's code -#from ArchSketchObjectExt import ArchSketch # Doesn't work + +# from ArchSketchObjectExt import ArchSketch # Doesn't work diff --git a/src/Mod/BIM/ArchSpace.py b/src/Mod/BIM/ArchSpace.py index f5f47e7e6a..216e1884b0 100644 --- a/src/Mod/BIM/ArchSpace.py +++ b/src/Mod/BIM/ArchSpace.py @@ -22,7 +22,7 @@ # * * # *************************************************************************** -__title__= "FreeCAD Arch Space" +__title__ = "FreeCAD Arch Space" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" @@ -50,248 +50,355 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond SpaceTypes = [ -"Undefined", -"Exterior", -"Exterior - Terrace", -"Office", -"Office - Enclosed", -"Office - Open Plan", -"Conference / Meeting / Multipurpose", -"Classroom / Lecture / Training For Penitentiary", -"Lobby", -"Lobby - For Hotel", -"Lobby - For Performing Arts Theater", -"Lobby - For Motion Picture Theater", -"Audience/Seating Area", -"Audience/Seating Area - For Gymnasium", -"Audience/Seating Area - For Exercise Center", -"Audience/Seating Area - For Convention Center", -"Audience/Seating Area - For Penitentiary", -"Audience/Seating Area - For Religious Buildings", -"Audience/Seating Area - For Sports Arena", -"Audience/Seating Area - For Performing Arts Theater", -"Audience/Seating Area - For Motion Picture Theater", -"Audience/Seating Area - For Transportation", -"Atrium", -"Atrium - First Three Floors", -"Atrium - Each Additional Floor", -"Lounge / Recreation", -"Lounge / Recreation - For Hospital", -"Dining Area", -"Dining Area - For Penitentiary", -"Dining Area - For Hotel", -"Dining Area - For Motel", -"Dining Area - For Bar Lounge/Leisure Dining", -"Dining Area - For Family Dining", -"Food Preparation", -"Laboratory", -"Restrooms", -"Dressing / Locker / Fitting", -"Room", -"Corridor / Transition", -"Corridor / Transition - For Hospital", -"Corridor / Transition - For Manufacturing Facility", -"Stairs", -"Active Storage", -"Active Storage - For Hospital", -"Inactive Storage", -"Inactive Storage - For Museum", -"Electrical / Mechanical", -"Gymnasium / Exercise Center", -"Gymnasium / Exercise Center - Playing Area", -"Gymnasium / Exercise Center - Exercise Area", -"Courthouse / Police Station / Penitentiary", -"Courthouse / Police Station / Penitentiary - Courtroom", -"Courthouse / Police Station / Penitentiary - Confinement Cells", -"Courthouse / Police Station / Penitentiary - Judges' Chambers", -"Fire Stations", -"Fire Stations - Engine Room", -"Fire Stations - Sleeping Quarters", -"Post Office - Sorting Area", -"Convention Center - Exhibit Space", -"Library", -"Library - Card File and Cataloging", -"Library - Stacks", -"Library - Reading Area", -"Hospital", -"Hospital - Emergency", -"Hospital - Recovery", -"Hospital - Nurses' Station", -"Hospital - Exam / Treatment", -"Hospital - Pharmacy", -"Hospital - Patient Room", -"Hospital - Operating Room", -"Hospital - Nursery", -"Hospital - Medical Supply", -"Hospital - Physical Therapy", -"Hospital - Radiology", -"Hospital - Laundry-Washing", -"Automotive - Service / Repair", -"Manufacturing", -"Manufacturing - Low Bay (< 7.5m Floor to Ceiling Height)", -"Manufacturing - High Bay (> 7.5m Floor to Ceiling Height)", -"Manufacturing - Detailed Manufacturing", -"Manufacturing - Equipment Room", -"Manufacturing - Control Room", -"Hotel / Motel Guest Rooms", -"Dormitory - Living Quarters", -"Museum", -"Museum - General Exhibition", -"Museum - Restoration", -"Bank / Office - Banking Activity Area", -"Workshop", -"Sales Area", -"Religious Buildings", -"Religious Buildings - Worship Pulpit, Choir", -"Religious Buildings - Fellowship Hall", -"Retail", -"Retail - Sales Area", -"Retail - Mall Concourse", -"Sports Arena", -"Sports Arena - Ring Sports Area", -"Sports Arena - Court Sports Area", -"Sports Arena - Indoor Playing Field Area", -"Warehouse", -"Warehouse - Fine Material Storage", -"Warehouse - Medium / Bulky Material Storage", -"Parking Garage - Garage Area", -"Transportation", -"Transportation - Airport / Concourse", -"Transportation - Air / Train / Bus - Baggage Area", -"Transportation - Terminal - Ticket Counter" + "Undefined", + "Exterior", + "Exterior - Terrace", + "Office", + "Office - Enclosed", + "Office - Open Plan", + "Conference / Meeting / Multipurpose", + "Classroom / Lecture / Training For Penitentiary", + "Lobby", + "Lobby - For Hotel", + "Lobby - For Performing Arts Theater", + "Lobby - For Motion Picture Theater", + "Audience/Seating Area", + "Audience/Seating Area - For Gymnasium", + "Audience/Seating Area - For Exercise Center", + "Audience/Seating Area - For Convention Center", + "Audience/Seating Area - For Penitentiary", + "Audience/Seating Area - For Religious Buildings", + "Audience/Seating Area - For Sports Arena", + "Audience/Seating Area - For Performing Arts Theater", + "Audience/Seating Area - For Motion Picture Theater", + "Audience/Seating Area - For Transportation", + "Atrium", + "Atrium - First Three Floors", + "Atrium - Each Additional Floor", + "Lounge / Recreation", + "Lounge / Recreation - For Hospital", + "Dining Area", + "Dining Area - For Penitentiary", + "Dining Area - For Hotel", + "Dining Area - For Motel", + "Dining Area - For Bar Lounge/Leisure Dining", + "Dining Area - For Family Dining", + "Food Preparation", + "Laboratory", + "Restrooms", + "Dressing / Locker / Fitting", + "Room", + "Corridor / Transition", + "Corridor / Transition - For Hospital", + "Corridor / Transition - For Manufacturing Facility", + "Stairs", + "Active Storage", + "Active Storage - For Hospital", + "Inactive Storage", + "Inactive Storage - For Museum", + "Electrical / Mechanical", + "Gymnasium / Exercise Center", + "Gymnasium / Exercise Center - Playing Area", + "Gymnasium / Exercise Center - Exercise Area", + "Courthouse / Police Station / Penitentiary", + "Courthouse / Police Station / Penitentiary - Courtroom", + "Courthouse / Police Station / Penitentiary - Confinement Cells", + "Courthouse / Police Station / Penitentiary - Judges' Chambers", + "Fire Stations", + "Fire Stations - Engine Room", + "Fire Stations - Sleeping Quarters", + "Post Office - Sorting Area", + "Convention Center - Exhibit Space", + "Library", + "Library - Card File and Cataloging", + "Library - Stacks", + "Library - Reading Area", + "Hospital", + "Hospital - Emergency", + "Hospital - Recovery", + "Hospital - Nurses' Station", + "Hospital - Exam / Treatment", + "Hospital - Pharmacy", + "Hospital - Patient Room", + "Hospital - Operating Room", + "Hospital - Nursery", + "Hospital - Medical Supply", + "Hospital - Physical Therapy", + "Hospital - Radiology", + "Hospital - Laundry-Washing", + "Automotive - Service / Repair", + "Manufacturing", + "Manufacturing - Low Bay (< 7.5m Floor to Ceiling Height)", + "Manufacturing - High Bay (> 7.5m Floor to Ceiling Height)", + "Manufacturing - Detailed Manufacturing", + "Manufacturing - Equipment Room", + "Manufacturing - Control Room", + "Hotel / Motel Guest Rooms", + "Dormitory - Living Quarters", + "Museum", + "Museum - General Exhibition", + "Museum - Restoration", + "Bank / Office - Banking Activity Area", + "Workshop", + "Sales Area", + "Religious Buildings", + "Religious Buildings - Worship Pulpit, Choir", + "Religious Buildings - Fellowship Hall", + "Retail", + "Retail - Sales Area", + "Retail - Mall Concourse", + "Sports Arena", + "Sports Arena - Ring Sports Area", + "Sports Arena - Court Sports Area", + "Sports Arena - Indoor Playing Field Area", + "Warehouse", + "Warehouse - Fine Material Storage", + "Warehouse - Medium / Bulky Material Storage", + "Parking Garage - Garage Area", + "Transportation", + "Transportation - Airport / Concourse", + "Transportation - Air / Train / Bus - Baggage Area", + "Transportation - Terminal - Ticket Counter", ] ConditioningTypes = [ -"Unconditioned", -"Heated", -"Cooled", -"HeatedAndCooled", -"Vented", -"NaturallyVentedOnly" + "Unconditioned", + "Heated", + "Cooled", + "HeatedAndCooled", + "Vented", + "NaturallyVentedOnly", ] -AreaCalculationType = [ - "XY-plane projection", - "At Center of Mass" -] +AreaCalculationType = ["XY-plane projection", "At Center of Mass"] class _Space(ArchComponent.Component): - "A space object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Space" self.setProperties(obj) obj.IfcType = "Space" obj.CompositionType = "ELEMENT" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "Boundaries" in pl: - obj.addProperty("App::PropertyLinkSubList","Boundaries", "Space",QT_TRANSLATE_NOOP("App::Property","The objects that make the boundaries of this space object"), locked=True) + obj.addProperty( + "App::PropertyLinkSubList", + "Boundaries", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", "The objects that make the boundaries of this space object" + ), + locked=True, + ) if not "Area" in pl: - obj.addProperty("App::PropertyArea", "Area", "Space",QT_TRANSLATE_NOOP("App::Property","Identical to Horizontal Area"), locked=True) + obj.addProperty( + "App::PropertyArea", + "Area", + "Space", + QT_TRANSLATE_NOOP("App::Property", "Identical to Horizontal Area"), + locked=True, + ) if not "FinishFloor" in pl: - obj.addProperty("App::PropertyString", "FinishFloor", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the floor of this space"), locked=True) + obj.addProperty( + "App::PropertyString", + "FinishFloor", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The finishing of the floor of this space"), + locked=True, + ) if not "FinishWalls" in pl: - obj.addProperty("App::PropertyString", "FinishWalls", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the walls of this space"), locked=True) + obj.addProperty( + "App::PropertyString", + "FinishWalls", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The finishing of the walls of this space"), + locked=True, + ) if not "FinishCeiling" in pl: - obj.addProperty("App::PropertyString", "FinishCeiling", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the ceiling of this space"), locked=True) + obj.addProperty( + "App::PropertyString", + "FinishCeiling", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The finishing of the ceiling of this space"), + locked=True, + ) if not "Group" in pl: - obj.addProperty("App::PropertyLinkList", "Group", "Space",QT_TRANSLATE_NOOP("App::Property","Objects that are included inside this space, such as furniture"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Group", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "Objects that are included inside this space, such as furniture", + ), + locked=True, + ) if not "SpaceType" in pl: - obj.addProperty("App::PropertyEnumeration","SpaceType", "Space",QT_TRANSLATE_NOOP("App::Property","The type of this space"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "SpaceType", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The type of this space"), + locked=True, + ) obj.SpaceType = SpaceTypes if not "FloorThickness" in pl: - obj.addProperty("App::PropertyLength", "FloorThickness","Space",QT_TRANSLATE_NOOP("App::Property","The thickness of the floor finish"), locked=True) + obj.addProperty( + "App::PropertyLength", + "FloorThickness", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the floor finish"), + locked=True, + ) if not "NumberOfPeople" in pl: - obj.addProperty("App::PropertyInteger", "NumberOfPeople","Space",QT_TRANSLATE_NOOP("App::Property","The number of people who typically occupy this space"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "NumberOfPeople", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", "The number of people who typically occupy this space" + ), + locked=True, + ) if not "LightingPower" in pl: - obj.addProperty("App::PropertyFloat", "LightingPower", "Space",QT_TRANSLATE_NOOP("App::Property","The electric power needed to light this space in Watts"), locked=True) + obj.addProperty( + "App::PropertyFloat", + "LightingPower", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", "The electric power needed to light this space in Watts" + ), + locked=True, + ) if not "EquipmentPower" in pl: - obj.addProperty("App::PropertyFloat", "EquipmentPower","Space",QT_TRANSLATE_NOOP("App::Property","The electric power needed by the equipment of this space in Watts"), locked=True) + obj.addProperty( + "App::PropertyFloat", + "EquipmentPower", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "The electric power needed by the equipment of this space in Watts", + ), + locked=True, + ) if not "AutoPower" in pl: - obj.addProperty("App::PropertyBool", "AutoPower", "Space",QT_TRANSLATE_NOOP("App::Property","If True, Equipment Power will be automatically filled by the equipment included in this space"), locked=True) + obj.addProperty( + "App::PropertyBool", + "AutoPower", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "If True, Equipment Power will be automatically filled by the equipment included in this space", + ), + locked=True, + ) if not "Conditioning" in pl: - obj.addProperty("App::PropertyEnumeration","Conditioning", "Space",QT_TRANSLATE_NOOP("App::Property","The type of air conditioning of this space"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "Conditioning", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The type of air conditioning of this space"), + locked=True, + ) obj.Conditioning = ConditioningTypes if not "Internal" in pl: - obj.addProperty("App::PropertyBool", "Internal", "Space",QT_TRANSLATE_NOOP("App::Property","Specifies if this space is internal or external"), locked=True) + obj.addProperty( + "App::PropertyBool", + "Internal", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", "Specifies if this space is internal or external" + ), + locked=True, + ) obj.Internal = True if not "AreaCalculationType" in pl: - obj.addProperty("App::PropertyEnumeration", "AreaCalculationType", "Space",QT_TRANSLATE_NOOP("App::Property","Defines the calculation type for the horizontal area and its perimeter length"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "AreaCalculationType", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "Defines the calculation type for the horizontal area and its perimeter length", + ), + locked=True, + ) obj.AreaCalculationType = AreaCalculationType - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Space" - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return # Space can do without Base. Base validity is tested in getShape() code below. # Remarked out ensureBase() below - #if not self.ensureBase(obj): + # if not self.ensureBase(obj): # return self.getShape(obj) - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): if prop == "Group": - if hasattr(obj,"EquipmentPower"): + if hasattr(obj, "EquipmentPower"): if obj.AutoPower: p = 0 - for o in Draft.getObjectsOfType(Draft.get_group_contents(obj.Group, addgroups=True), - "Equipment"): - if hasattr(o,"EquipmentPower"): + for o in Draft.getObjectsOfType( + Draft.get_group_contents(obj.Group, addgroups=True), "Equipment" + ): + if hasattr(o, "EquipmentPower"): p += o.EquipmentPower if p != obj.EquipmentPower: obj.EquipmentPower = p elif prop == "Zone": if obj.Zone: if obj.Zone.ViewObject: - if hasattr(obj.Zone.ViewObject,"Proxy"): - if hasattr(obj.Zone.ViewObject.Proxy,"claimChildren"): + if hasattr(obj.Zone.ViewObject, "Proxy"): + if hasattr(obj.Zone.ViewObject.Proxy, "claimChildren"): obj.Zone.ViewObject.Proxy.claimChildren() - if hasattr(obj,"Area"): - obj.setEditorMode('Area',1) - ArchComponent.Component.onChanged(self,obj,prop) - - def addSubobjects(self,obj,subobjects): + if hasattr(obj, "Area"): + obj.setEditorMode("Area", 1) + ArchComponent.Component.onChanged(self, obj, prop) + def addSubobjects(self, obj, subobjects): "adds subobjects to this space" objs = obj.Boundaries for o in subobjects: - if isinstance(o,tuple) or isinstance(o,list): + if isinstance(o, tuple) or isinstance(o, list): if o[0].Name != obj.Name: objs.append(tuple(o)) else: for el in o.SubElementNames: if "Face" in el: if o.Object.Name != obj.Name: - objs.append((o.Object,el)) + objs.append((o.Object, el)) obj.Boundaries = objs - def removeSubobjects(self,obj,subobjects): - + def removeSubobjects(self, obj, subobjects): "removes subobjects to this space" bounds = obj.Boundaries for o in subobjects: @@ -301,8 +408,7 @@ class _Space(ArchComponent.Component): break obj.Boundaries = bounds - def addObject(self,obj,child): - + def addObject(self, obj, child): "Adds an object to this Space" if not child in obj.Group: @@ -310,16 +416,16 @@ class _Space(ArchComponent.Component): g.append(child) obj.Group = g - def getShape(self,obj): - + def getShape(self, obj): "computes a shape from a base shape and/or boundary faces" import Part + shape = None faces = [] pl = obj.Placement - #print("starting compute") + # print("starting compute") # 1: if we have a base shape, we use it # Check if there is obj.Base and its validity to proceed @@ -330,12 +436,12 @@ class _Space(ArchComponent.Component): # 2: if not, add all bounding boxes of considered objects and build a first shape if shape: - #print("got shape from base object") + # print("got shape from base object") bb = shape.BoundBox else: bb = None for b in obj.Boundaries: - if hasattr(b[0],'Shape'): + if hasattr(b[0], "Shape"): if not bb: bb = b[0].Shape.BoundBox else: @@ -343,89 +449,92 @@ class _Space(ArchComponent.Component): if not bb: # compute area even if we are not calculating the shape if obj.Shape and obj.Shape.Solids: - if hasattr(obj.Area,"Value"): + if hasattr(obj.Area, "Value"): a = self.getArea(obj) if obj.Area.Value != a: obj.Area = a return - shape = Part.makeBox(bb.XLength,bb.YLength,bb.ZLength,FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin)) - #print("created shape from boundbox") + shape = Part.makeBox( + bb.XLength, bb.YLength, bb.ZLength, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin) + ) + # print("created shape from boundbox") # 3: identifying boundary faces goodfaces = [] for b in obj.Boundaries: - if hasattr(b[0],'Shape'): + if hasattr(b[0], "Shape"): for sub in b[1]: if "Face" in sub: - fn = int(sub[4:])-1 + fn = int(sub[4:]) - 1 faces.append(b[0].Shape.Faces[fn]) - #print("adding face ",fn," of object ",b[0].Name) + # print("adding face ",fn," of object ",b[0].Name) - #print("total: ", len(faces), " faces") + # print("total: ", len(faces), " faces") # 4: get cutvolumes from faces cutvolumes = [] for f in faces: f = f.copy() f.reverse() - cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(f,shape) + cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume(f, shape) if cutvolume: - #print("generated 1 cutvolume") + # print("generated 1 cutvolume") cutvolumes.append(cutvolume.copy()) - #Part.show(cutvolume) + # Part.show(cutvolume) for v in cutvolumes: - #print("cutting") + # print("cutting") shape = shape.cut(v) # 5: get the final shape if shape: if shape.Solids: - #print("setting objects shape") + # print("setting objects shape") shape = shape.Solids[0] - self.applyShape(obj,shape,pl) - if hasattr(obj.HorizontalArea,"Value"): - if hasattr(obj,"AreaCalculationType"): + self.applyShape(obj, shape, pl) + if hasattr(obj.HorizontalArea, "Value"): + if hasattr(obj, "AreaCalculationType"): if obj.AreaCalculationType == "At Center of Mass": a = self.getArea(obj) obj.HorizontalArea = a - if hasattr(obj,"Area"): + if hasattr(obj, "Area"): obj.Area = obj.HorizontalArea return - print("Arch: error computing space boundary for",obj.Label) - - def getArea(self,obj,notouch=False): + print("Arch: error computing space boundary for", obj.Label) + def getArea(self, obj, notouch=False): "returns the horizontal area at the center of the space" self.face = self.getFootprint(obj) if self.face: if not notouch: - if hasattr(obj,"PerimeterLength"): + if hasattr(obj, "PerimeterLength"): if self.face.OuterWire.Length != obj.PerimeterLength.Value: obj.PerimeterLength = self.face.OuterWire.Length return self.face.Area else: return 0 - def getFootprint(self,obj): - + def getFootprint(self, obj): "returns a face that represents the footprint of this space at the center of mass" import Part import DraftGeomUtils - if not hasattr(obj.Shape,"CenterOfMass"): + + if not hasattr(obj.Shape, "CenterOfMass"): return None try: - pl = Part.makePlane(1,1) + pl = Part.makePlane(1, 1) pl.translate(obj.Shape.CenterOfMass) sh = obj.Shape.copy() - cutplane,v1,v2 = ArchCommands.getCutVolume(pl,sh) + cutplane, v1, v2 = ArchCommands.getCutVolume(pl, sh) e = sh.section(cutplane) e = Part.__sortEdges__(e.Edges) w = Part.Wire(e) - dv = FreeCAD.Vector(obj.Shape.CenterOfMass.x,obj.Shape.CenterOfMass.y,obj.Shape.BoundBox.ZMin) + dv = FreeCAD.Vector( + obj.Shape.CenterOfMass.x, obj.Shape.CenterOfMass.y, obj.Shape.BoundBox.ZMin + ) dv = dv.sub(obj.Shape.CenterOfMass) w.translate(dv) return Part.Face(w) @@ -434,68 +543,144 @@ class _Space(ArchComponent.Component): class _ViewProviderSpace(ArchComponent.ViewProviderComponent): - "A View Provider for Section Planes" - def __init__(self,vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + def __init__(self, vobj): + + ArchComponent.ViewProviderComponent.__init__(self, vobj) self.setProperties(vobj) vobj.Transparency = params.get_param_arch("defaultSpaceTransparency") vobj.LineWidth = params.get_param_view("DefaultShapeLineWidth") vobj.LineColor = ArchCommands.getDefaultColor("Space") - vobj.DrawStyle = ["Solid","Dashed","Dotted","Dashdot"][params.get_param_arch("defaultSpaceStyle")] + vobj.DrawStyle = ["Solid", "Dashed", "Dotted", "Dashdot"][ + params.get_param_arch("defaultSpaceStyle") + ] - def setProperties(self,vobj): + def setProperties(self, vobj): pl = vobj.PropertiesList if not "Text" in pl: - vobj.addProperty("App::PropertyStringList", "Text", "Space",QT_TRANSLATE_NOOP("App::Property","The text to show. Use $area, $label, $longname, $description or any other property name preceded with $ (case insensitive), or $floor, $walls, $ceiling for finishes, to insert the respective data"), locked=True) - vobj.Text = ["$label","$area"] + vobj.addProperty( + "App::PropertyStringList", + "Text", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "The text to show. Use $area, $label, $longname, $description or any other property name preceded with $ (case insensitive), or $floor, $walls, $ceiling for finishes, to insert the respective data", + ), + locked=True, + ) + vobj.Text = ["$label", "$area"] if not "FontName" in pl: - vobj.addProperty("App::PropertyFont", "FontName", "Space",QT_TRANSLATE_NOOP("App::Property","The name of the font"), locked=True) + vobj.addProperty( + "App::PropertyFont", + "FontName", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The name of the font"), + locked=True, + ) vobj.FontName = params.get_param("textfont") if not "TextColor" in pl: - vobj.addProperty("App::PropertyColor", "TextColor", "Space",QT_TRANSLATE_NOOP("App::Property","The color of the area text"), locked=True) - vobj.TextColor = (0.0,0.0,0.0,1.0) + vobj.addProperty( + "App::PropertyColor", + "TextColor", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The color of the area text"), + locked=True, + ) + vobj.TextColor = (0.0, 0.0, 0.0, 1.0) if not "FontSize" in pl: - vobj.addProperty("App::PropertyLength", "FontSize", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the text font"), locked=True) - vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") + vobj.addProperty( + "App::PropertyLength", + "FontSize", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The size of the text font"), + locked=True, + ) + vobj.FontSize = params.get_param("textheight") * params.get_param( + "DefaultAnnoScaleMultiplier" + ) if not "FirstLine" in pl: - vobj.addProperty("App::PropertyLength", "FirstLine", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the first line of text"), locked=True) - vobj.FirstLine = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") + vobj.addProperty( + "App::PropertyLength", + "FirstLine", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The size of the first line of text"), + locked=True, + ) + vobj.FirstLine = params.get_param("textheight") * params.get_param( + "DefaultAnnoScaleMultiplier" + ) if not "LineSpacing" in pl: - vobj.addProperty("App::PropertyFloat", "LineSpacing", "Space",QT_TRANSLATE_NOOP("App::Property","The space between the lines of text"), locked=True) + vobj.addProperty( + "App::PropertyFloat", + "LineSpacing", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The space between the lines of text"), + locked=True, + ) vobj.LineSpacing = 1.0 if not "TextPosition" in pl: - vobj.addProperty("App::PropertyVectorDistance","TextPosition","Space",QT_TRANSLATE_NOOP("App::Property","The position of the text. Leave (0,0,0) for automatic position"), locked=True) + vobj.addProperty( + "App::PropertyVectorDistance", + "TextPosition", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", + "The position of the text. Leave (0,0,0) for automatic position", + ), + locked=True, + ) if not "TextAlign" in pl: - vobj.addProperty("App::PropertyEnumeration", "TextAlign", "Space",QT_TRANSLATE_NOOP("App::Property","The justification of the text"), locked=True) - vobj.TextAlign = ["Left","Center","Right"] + vobj.addProperty( + "App::PropertyEnumeration", + "TextAlign", + "Space", + QT_TRANSLATE_NOOP("App::Property", "The justification of the text"), + locked=True, + ) + vobj.TextAlign = ["Left", "Center", "Right"] vobj.TextAlign = "Center" if not "Decimals" in pl: - vobj.addProperty("App::PropertyInteger", "Decimals", "Space",QT_TRANSLATE_NOOP("App::Property","The number of decimals to use for calculated texts"), locked=True) + vobj.addProperty( + "App::PropertyInteger", + "Decimals", + "Space", + QT_TRANSLATE_NOOP( + "App::Property", "The number of decimals to use for calculated texts" + ), + locked=True, + ) vobj.Decimals = params.get_param("dimPrecision") if not "ShowUnit" in pl: - vobj.addProperty("App::PropertyBool", "ShowUnit", "Space",QT_TRANSLATE_NOOP("App::Property","Show the unit suffix"), locked=True) + vobj.addProperty( + "App::PropertyBool", + "ShowUnit", + "Space", + QT_TRANSLATE_NOOP("App::Property", "Show the unit suffix"), + locked=True, + ) vobj.ShowUnit = params.get_param("showUnit") - def onDocumentRestored(self,vobj): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + + if hasattr(self, "Object"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Space_Clone.svg" return ":/icons/Arch_Space_Tree.svg" - def attach(self,vobj): + def attach(self, vobj): - ArchComponent.ViewProviderComponent.attach(self,vobj) + ArchComponent.ViewProviderComponent.attach(self, vobj) from pivy import coin + self.color = coin.SoBaseColor() self.font = coin.SoFont() self.text1 = coin.SoAsciiText() @@ -517,11 +702,11 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): sep.addChild(self.text1) self.label.addChild(sep) vobj.Annotation.addChild(self.label) - self.onChanged(vobj,"TextColor") - self.onChanged(vobj,"FontSize") - self.onChanged(vobj,"FirstLine") - self.onChanged(vobj,"LineSpacing") - self.onChanged(vobj,"FontName") + self.onChanged(vobj, "TextColor") + self.onChanged(vobj, "FontSize") + self.onChanged(vobj, "FirstLine") + self.onChanged(vobj, "LineSpacing") + self.onChanged(vobj, "FontName") self.Object = vobj.Object # footprint mode self.fmat = coin.SoMaterial() @@ -536,22 +721,23 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): sep.addChild(self.fset) vobj.RootNode.addChild(sep) - def updateData(self,obj,prop): + def updateData(self, obj, prop): - if prop in ["Shape","Label","Tag","Area"]: - self.onChanged(obj.ViewObject,"Text") - self.onChanged(obj.ViewObject,"TextPosition") + if prop in ["Shape", "Label", "Tag", "Area"]: + self.onChanged(obj.ViewObject, "Text") + self.onChanged(obj.ViewObject, "TextPosition") - def getTextPosition(self,vobj): + def getTextPosition(self, vobj): pos = FreeCAD.Vector() - if hasattr(vobj,"TextPosition"): + if hasattr(vobj, "TextPosition"): import DraftVecUtils + if DraftVecUtils.isNull(vobj.TextPosition): try: pos = vobj.Object.Shape.CenterOfMass z = vobj.Object.Shape.BoundBox.ZMin - pos = FreeCAD.Vector(pos.x,pos.y,z) + pos = FreeCAD.Vector(pos.x, pos.y, z) except (AttributeError, RuntimeError): pos = FreeCAD.Vector() else: @@ -560,10 +746,10 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): pos = vobj.Object.Placement.inverse().multVec(pos) return pos - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): - if prop in ["Text","Decimals","ShowUnit"]: - if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"Text"): + if prop in ["Text", "Decimals", "ShowUnit"]: + if hasattr(self, "text1") and hasattr(self, "text2") and hasattr(vobj, "Text"): self.text1.string.deleteValues(0) self.text2.string.deleteValues(0) text1 = [] @@ -571,29 +757,32 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): first = True for t in vobj.Text: if t: - t = t.replace("$label",vobj.Object.Label) - if hasattr(vobj.Object,"Area"): + t = t.replace("$label", vobj.Object.Label) + if hasattr(vobj.Object, "Area"): from FreeCAD import Units - q = Units.Quantity(vobj.Object.Area.Value,Units.Area).getUserPreferred() - qt = vobj.Object.Area.Value/q[1] - if hasattr(vobj,"Decimals"): + + q = Units.Quantity( + vobj.Object.Area.Value, Units.Area + ).getUserPreferred() + qt = vobj.Object.Area.Value / q[1] + if hasattr(vobj, "Decimals"): if vobj.Decimals == 0: qt = str(int(qt)) else: - f = "%."+str(abs(vobj.Decimals))+"f" + f = "%." + str(abs(vobj.Decimals)) + "f" qt = f % qt else: qt = str(qt) - if hasattr(vobj,"ShowUnit"): + if hasattr(vobj, "ShowUnit"): if vobj.ShowUnit: - qt = qt + q[2].replace("^2",u"\xb2") # square symbol - t = t.replace("$area",qt) - if hasattr(vobj.Object,"FinishFloor"): - t = t.replace("$floor",vobj.Object.FinishFloor) - if hasattr(vobj.Object,"FinishWalls"): - t = t.replace("$walls",vobj.Object.FinishWalls) - if hasattr(vobj.Object,"FinishCeiling"): - t = t.replace("$ceiling",vobj.Object.FinishCeiling) + qt = qt + q[2].replace("^2", "\xb2") # square symbol + t = t.replace("$area", qt) + if hasattr(vobj.Object, "FinishFloor"): + t = t.replace("$floor", vobj.Object.FinishFloor) + if hasattr(vobj.Object, "FinishWalls"): + t = t.replace("$walls", vobj.Object.FinishWalls) + if hasattr(vobj.Object, "FinishCeiling"): + t = t.replace("$ceiling", vobj.Object.FinishCeiling) # replace all other properties props = vobj.Object.PropertiesList lower_props = [p.lower() for p in props] @@ -620,44 +809,52 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): self.text2.string.setValues(text2) elif prop == "FontName": - if hasattr(self,"font") and hasattr(vobj,"FontName"): + if hasattr(self, "font") and hasattr(vobj, "FontName"): self.font.name = str(vobj.FontName) elif prop == "FontSize": - if hasattr(self,"font") and hasattr(vobj,"FontSize"): + if hasattr(self, "font") and hasattr(vobj, "FontSize"): self.font.size = vobj.FontSize.Value - if hasattr(vobj,"FirstLine"): - scale = vobj.FirstLine.Value/vobj.FontSize.Value - self.header.scaleFactor.setValue([scale,scale,scale]) + if hasattr(vobj, "FirstLine"): + scale = vobj.FirstLine.Value / vobj.FontSize.Value + self.header.scaleFactor.setValue([scale, scale, scale]) self.onChanged(vobj, "TextPosition") elif prop == "FirstLine": - if hasattr(self,"header") and hasattr(vobj,"FontSize") and hasattr(vobj,"FirstLine"): - scale = vobj.FirstLine.Value/vobj.FontSize.Value - self.header.scaleFactor.setValue([scale,scale,scale]) + if hasattr(self, "header") and hasattr(vobj, "FontSize") and hasattr(vobj, "FirstLine"): + scale = vobj.FirstLine.Value / vobj.FontSize.Value + self.header.scaleFactor.setValue([scale, scale, scale]) self.onChanged(vobj, "TextPosition") elif prop == "TextColor": - if hasattr(self,"color") and hasattr(vobj,"TextColor"): + if hasattr(self, "color") and hasattr(vobj, "TextColor"): c = vobj.TextColor - self.color.rgb.setValue(c[0],c[1],c[2]) + self.color.rgb.setValue(c[0], c[1], c[2]) elif prop == "TextPosition": - if hasattr(self,"coords") and hasattr(self,"header") and hasattr(vobj,"TextPosition") and hasattr(vobj,"FirstLine"): + if ( + hasattr(self, "coords") + and hasattr(self, "header") + and hasattr(vobj, "TextPosition") + and hasattr(vobj, "FirstLine") + ): pos = self.getTextPosition(vobj) - self.coords.translation.setValue([pos.x,pos.y,pos.z+0.01]) # adding small z offset to separate from bottom face + self.coords.translation.setValue( + [pos.x, pos.y, pos.z + 0.01] + ) # adding small z offset to separate from bottom face up = vobj.FirstLine.Value * vobj.LineSpacing - self.header.translation.setValue([0,up,0]) + self.header.translation.setValue([0, up, 0]) elif prop == "LineSpacing": - if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"LineSpacing"): + if hasattr(self, "text1") and hasattr(self, "text2") and hasattr(vobj, "LineSpacing"): self.text1.spacing = vobj.LineSpacing self.text2.spacing = vobj.LineSpacing - self.onChanged(vobj,"TextPosition") + self.onChanged(vobj, "TextPosition") elif prop == "TextAlign": - if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"TextAlign"): + if hasattr(self, "text1") and hasattr(self, "text2") and hasattr(vobj, "TextAlign"): from pivy import coin + if vobj.TextAlign == "Center": self.text1.justification = coin.SoAsciiText.CENTER self.text2.justification = coin.SoAsciiText.CENTER @@ -675,7 +872,7 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): self.label.whichChild = -1 elif prop == "Transparency": - if hasattr(vobj,"DisplayMode"): + if hasattr(vobj, "DisplayMode"): vobj.DisplayMode = "Wireframe" if vobj.Transparency == 100 else "Flat Lines" def setEdit(self, vobj, mode): @@ -689,17 +886,17 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): FreeCADGui.Control.showDialog(taskd) return True - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): - modes = ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj)+["Footprint"] + modes = ArchComponent.ViewProviderComponent.getDisplayModes(self, vobj) + ["Footprint"] return modes - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): self.fset.coordIndex.deleteValues(0) self.fcoords.point.deleteValues(0) if mode == "Footprint": - if hasattr(self,"Object"): + if hasattr(self, "Object"): face = self.Object.Proxy.getFootprint(self.Object) if face: verts = [] @@ -707,18 +904,17 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): idx = 0 tri = face.tessellate(1) for v in tri[0]: - verts.append([v.x,v.y,v.z]) + verts.append([v.x, v.y, v.z]) for f in tri[1]: - fdata.extend([f[0]+idx,f[1]+idx,f[2]+idx,-1]) + fdata.extend([f[0] + idx, f[1] + idx, f[2] + idx, -1]) idx += len(tri[0]) self.fcoords.point.setValues(verts) - self.fset.coordIndex.setValues(0,len(fdata),fdata) + self.fset.coordIndex.setValues(0, len(fdata), fdata) return "Points" - return ArchComponent.ViewProviderComponent.setDisplayMode(self,mode) + return ArchComponent.ViewProviderComponent.setDisplayMode(self, mode) class SpaceTaskPanel(ArchComponent.ComponentTaskPanel): - "A modified version of the Arch component task panel" def __init__(self): @@ -768,7 +964,7 @@ class SpaceTaskPanel(ArchComponent.ComponentTaskPanel): if self.obj: if FreeCADGui.Selection.getSelectionEx(): - self.obj.Proxy.addSubobjects(self.obj,FreeCADGui.Selection.getSelectionEx()) + self.obj.Proxy.addSubobjects(self.obj, FreeCADGui.Selection.getSelectionEx()) self.updateBoundaries() def delBoundary(self): diff --git a/src/Mod/BIM/ArchStairs.py b/src/Mod/BIM/ArchStairs.py index 9307f06472..d5d6f62be5 100644 --- a/src/Mod/BIM/ArchStairs.py +++ b/src/Mod/BIM/ArchStairs.py @@ -22,7 +22,7 @@ # * * # *************************************************************************** -__title__= "FreeCAD Arch Stairs" +__title__ = "FreeCAD Arch Stairs" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" @@ -50,25 +50,25 @@ if FreeCAD.GuiUp: import FreeCADGui else: # \cond - def QT_TRANSLATE_NOOP(ctxt,txt): + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -zeroMM = FreeCAD.Units.Quantity('0mm') +zeroMM = FreeCAD.Units.Quantity("0mm") class _Stairs(ArchComponent.Component): - "A stairs object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Stairs" self.setProperties(obj) obj.IfcType = "Stair" - def setProperties(self,obj): + def setProperties(self, obj): # http://en.wikipedia.org/wiki/Stairs @@ -76,209 +76,514 @@ class _Stairs(ArchComponent.Component): # base properties if not "Length" in pl: - obj.addProperty("App::PropertyLength","Length","Stairs",QT_TRANSLATE_NOOP("App::Property","The length of these stairs, if no baseline is defined"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Length", + "Stairs", + QT_TRANSLATE_NOOP( + "App::Property", "The length of these stairs, if no baseline is defined" + ), + locked=True, + ) if not "Width" in pl: - obj.addProperty("App::PropertyLength","Width","Stairs",QT_TRANSLATE_NOOP("App::Property","The width of these stairs"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The width of these stairs"), + locked=True, + ) if not "Height" in pl: - obj.addProperty("App::PropertyLength","Height","Stairs",QT_TRANSLATE_NOOP("App::Property","The total height of these stairs"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "Stairs", + QT_TRANSLATE_NOOP("App::Property", "The total height of these stairs"), + locked=True, + ) if not "Align" in pl: - obj.addProperty("App::PropertyEnumeration","Align","Stairs",QT_TRANSLATE_NOOP("App::Property","The alignment of these stairs on their baseline, if applicable"), locked=True) - obj.Align = ['Left','Right','Center'] + obj.addProperty( + "App::PropertyEnumeration", + "Align", + "Stairs", + QT_TRANSLATE_NOOP( + "App::Property", + "The alignment of these stairs on their baseline, if applicable", + ), + locked=True, + ) + obj.Align = ["Left", "Right", "Center"] # TODO - To be combined into Width when PropertyLengthList is available if not "WidthOfLanding" in pl: - obj.addProperty("App::PropertyFloatList","WidthOfLanding","Stairs",QT_TRANSLATE_NOOP("App::Property","The width of a Landing (Second edge and after - First edge follows Width property)"), locked=True) + obj.addProperty( + "App::PropertyFloatList", + "WidthOfLanding", + "Stairs", + QT_TRANSLATE_NOOP( + "App::Property", + "The width of a Landing (Second edge and after - First edge follows Width property)", + ), + locked=True, + ) # steps and risers properties if not "NumberOfSteps" in pl: - obj.addProperty("App::PropertyInteger","NumberOfSteps","Steps",QT_TRANSLATE_NOOP("App::Property","The number of risers in these stairs"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "NumberOfSteps", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The number of risers in these stairs"), + locked=True, + ) if not "TreadDepth" in pl: - obj.addProperty("App::PropertyLength","TreadDepth","Steps",QT_TRANSLATE_NOOP("App::Property","The depth of the treads of these stairs"), locked=True) - obj.setEditorMode("TreadDepth",1) + obj.addProperty( + "App::PropertyLength", + "TreadDepth", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The depth of the treads of these stairs"), + locked=True, + ) + obj.setEditorMode("TreadDepth", 1) if not "RiserHeight" in pl: - obj.addProperty("App::PropertyLength","RiserHeight","Steps",QT_TRANSLATE_NOOP("App::Property","The height of the risers of these stairs"), locked=True) - obj.setEditorMode("RiserHeight",1) + obj.addProperty( + "App::PropertyLength", + "RiserHeight", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The height of the risers of these stairs"), + locked=True, + ) + obj.setEditorMode("RiserHeight", 1) if not "Nosing" in pl: - obj.addProperty("App::PropertyLength","Nosing","Steps",QT_TRANSLATE_NOOP("App::Property","The size of the nosing"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Nosing", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The size of the nosing"), + locked=True, + ) if not "TreadThickness" in pl: - obj.addProperty("App::PropertyLength","TreadThickness","Steps",QT_TRANSLATE_NOOP("App::Property","The thickness of the treads"), locked=True) - if not ("BlondelRatio" in pl and obj.getTypeIdOfProperty('BlondelRatio') == 'App::PropertyLength'): + obj.addProperty( + "App::PropertyLength", + "TreadThickness", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the treads"), + locked=True, + ) + if not ( + "BlondelRatio" in pl + and obj.getTypeIdOfProperty("BlondelRatio") == "App::PropertyLength" + ): if "BlondelRatio" in pl: obj.setPropertyStatus("BlondelRatio", "-LockDynamic") obj.removeProperty("BlondelRatio") # Change from unitless float to Length - #obj.addProperty("App::PropertyFloat","BlondelRatio","Steps",QT_TRANSLATE_NOOP("App::Property","The Blondel ratio indicates comfortable stairs and should be between 62 and 64cm or 24.5 and 25.5in"), locked=True) - obj.addProperty("App::PropertyLength","BlondelRatio","Steps",QT_TRANSLATE_NOOP("App::Property","The Blondel ratio indicates comfortable stairs and should be between 62 and 64cm or 24.5 and 25.5in"), locked=True) - obj.setEditorMode("BlondelRatio",1) + # obj.addProperty("App::PropertyFloat","BlondelRatio","Steps",QT_TRANSLATE_NOOP("App::Property","The Blondel ratio indicates comfortable stairs and should be between 62 and 64cm or 24.5 and 25.5in"), locked=True) + obj.addProperty( + "App::PropertyLength", + "BlondelRatio", + "Steps", + QT_TRANSLATE_NOOP( + "App::Property", + "The Blondel ratio indicates comfortable stairs and should be between 62 and 64cm or 24.5 and 25.5in", + ), + locked=True, + ) + obj.setEditorMode("BlondelRatio", 1) if not "RiserThickness" in pl: - obj.addProperty("App::PropertyLength","RiserThickness","Steps",QT_TRANSLATE_NOOP("App::Property","The thickness of the risers"), locked=True) + obj.addProperty( + "App::PropertyLength", + "RiserThickness", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the risers"), + locked=True, + ) - if not hasattr(obj,"LandingDepth"): - obj.addProperty("App::PropertyLength","LandingDepth","Steps",QT_TRANSLATE_NOOP("App::Property","The depth of the landing of these stairs"), locked=True) + if not hasattr(obj, "LandingDepth"): + obj.addProperty( + "App::PropertyLength", + "LandingDepth", + "Steps", + QT_TRANSLATE_NOOP("App::Property", "The depth of the landing of these stairs"), + locked=True, + ) - if not hasattr(obj,"TreadDepthEnforce"): - obj.addProperty("App::PropertyLength","TreadDepthEnforce","Steps",QT_TRANSLATE_NOOP("App::Property","The depth of the treads of these stairs - Enforced regardless of Length or edge's Length"), locked=True) - if not hasattr(obj,"RiserHeightEnforce"): - obj.addProperty("App::PropertyLength","RiserHeightEnforce","Steps",QT_TRANSLATE_NOOP("App::Property","The height of the risers of these stairs - Enforced regardless of Height or edge's Height"), locked=True) + if not hasattr(obj, "TreadDepthEnforce"): + obj.addProperty( + "App::PropertyLength", + "TreadDepthEnforce", + "Steps", + QT_TRANSLATE_NOOP( + "App::Property", + "The depth of the treads of these stairs - Enforced regardless of Length or edge's Length", + ), + locked=True, + ) + if not hasattr(obj, "RiserHeightEnforce"): + obj.addProperty( + "App::PropertyLength", + "RiserHeightEnforce", + "Steps", + QT_TRANSLATE_NOOP( + "App::Property", + "The height of the risers of these stairs - Enforced regardless of Height or edge's Height", + ), + locked=True, + ) - if not hasattr(obj,"Flight"): - obj.addProperty("App::PropertyEnumeration","Flight","Structure",QT_TRANSLATE_NOOP("App::Property","The direction of flight after landing"), locked=True) - obj.Flight = ["Straight","HalfTurnLeft","HalfTurnRight"] + if not hasattr(obj, "Flight"): + obj.addProperty( + "App::PropertyEnumeration", + "Flight", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The direction of flight after landing"), + locked=True, + ) + obj.Flight = ["Straight", "HalfTurnLeft", "HalfTurnRight"] # Segment and Parts properties - if not hasattr(obj,"LastSegment"): - obj.addProperty("App::PropertyLink","LastSegment","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Last Segment (Flight or Landing) of Arch Stairs connecting to This Segment"), locked=True) - if not hasattr(obj,"AbsTop"): - obj.addProperty("App::PropertyVector","AbsTop","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'absolute' top level of a flight of stairs leads to"), locked=True) - obj.setEditorMode("AbsTop",1) - if not hasattr(obj,"OutlineLeft"): - obj.addProperty("App::PropertyVectorList","OutlineLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' of stairs"), locked=True) # Used for Outline of Railing - obj.setEditorMode("OutlineLeft",1) - if not hasattr(obj,"OutlineRight"): - obj.addProperty("App::PropertyVectorList","OutlineRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' of stairs"), locked=True) - obj.setEditorMode("OutlineRight",1) + if not hasattr(obj, "LastSegment"): + obj.addProperty( + "App::PropertyLink", + "LastSegment", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", + "Last Segment (Flight or Landing) of Arch Stairs connecting to This Segment", + ), + locked=True, + ) + if not hasattr(obj, "AbsTop"): + obj.addProperty( + "App::PropertyVector", + "AbsTop", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", "The 'absolute' top level of a flight of stairs leads to" + ), + locked=True, + ) + obj.setEditorMode("AbsTop", 1) + if not hasattr(obj, "OutlineLeft"): + obj.addProperty( + "App::PropertyVectorList", + "OutlineLeft", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "The 'left outline' of stairs"), + locked=True, + ) # Used for Outline of Railing + obj.setEditorMode("OutlineLeft", 1) + if not hasattr(obj, "OutlineRight"): + obj.addProperty( + "App::PropertyVectorList", + "OutlineRight", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "The 'left outline' of stairs"), + locked=True, + ) + obj.setEditorMode("OutlineRight", 1) # Can't accept 'None' in list, need NaN - #if not hasattr(obj,"OutlineRailArcLeft"): - #obj.addProperty("App::PropertyVectorList","OutlineRailArcLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' 'arc points' of stairs railing"), locked=True) - #obj.setEditorMode("OutlineRailArcLeft",1) - #if not hasattr(obj,"OutlineRailArcRight"): - #obj.addProperty("App::PropertyVectorList","OutlineRailArcRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'right outline' 'arc points of stairs railing"), locked=True) - #obj.setEditorMode("OutlineRailArcRight",1) - if not hasattr(self,"OutlineRailArcLeft"): + # if not hasattr(obj,"OutlineRailArcLeft"): + # obj.addProperty("App::PropertyVectorList","OutlineRailArcLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' 'arc points' of stairs railing"), locked=True) + # obj.setEditorMode("OutlineRailArcLeft",1) + # if not hasattr(obj,"OutlineRailArcRight"): + # obj.addProperty("App::PropertyVectorList","OutlineRailArcRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'right outline' 'arc points of stairs railing"), locked=True) + # obj.setEditorMode("OutlineRailArcRight",1) + if not hasattr(self, "OutlineRailArcLeft"): self.OutlineRailArcLeft = [] - if not hasattr(self,"OutlineRailArcRight"): + if not hasattr(self, "OutlineRailArcRight"): self.OutlineRailArcRight = [] - if not hasattr(obj,"RailingLeft"): - obj.addProperty("App::PropertyLinkHidden","RailingLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Name of Railing object (left) created"), locked=True) - if not hasattr(obj,"RailingRight"): - obj.addProperty("App::PropertyLinkHidden","RailingRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Name of Railing object (right) created"), locked=True) + if not hasattr(obj, "RailingLeft"): + obj.addProperty( + "App::PropertyLinkHidden", + "RailingLeft", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "Name of Railing object (left) created"), + locked=True, + ) + if not hasattr(obj, "RailingRight"): + obj.addProperty( + "App::PropertyLinkHidden", + "RailingRight", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "Name of Railing object (right) created"), + locked=True, + ) - if not hasattr(obj,"OutlineLeftAll"): - obj.addProperty("App::PropertyVectorList","OutlineLeftAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' of all segments of stairs"), locked=True) - obj.setEditorMode("OutlineLeftAll",1) # Used for Outline of Railing - if not hasattr(obj,"OutlineRightAll"): - obj.addProperty("App::PropertyVectorList","OutlineRightAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'right outline' of all segments of stairs"), locked=True) - obj.setEditorMode("OutlineRightAll",1) + if not hasattr(obj, "OutlineLeftAll"): + obj.addProperty( + "App::PropertyVectorList", + "OutlineLeftAll", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "The 'left outline' of all segments of stairs"), + locked=True, + ) + obj.setEditorMode("OutlineLeftAll", 1) # Used for Outline of Railing + if not hasattr(obj, "OutlineRightAll"): + obj.addProperty( + "App::PropertyVectorList", + "OutlineRightAll", + "Segment and Parts", + QT_TRANSLATE_NOOP("App::Property", "The 'right outline' of all segments of stairs"), + locked=True, + ) + obj.setEditorMode("OutlineRightAll", 1) # Can't accept 'None' in list, need NaN - #if not hasattr(obj,"OutlineRailArcLeftAll"): - #obj.addProperty("App::PropertyVectorList","OutlineRailArcLeftAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' 'arc points' of all segments of stairs railing"), locked=True) - #obj.setEditorMode("OutlineRailArcLeftAll",1) # Used for Outline of Railing - #if not hasattr(obj,"OutlineRailArcRightAll"): - #obj.addProperty("App::PropertyVectorList","OutlineRailArcRightAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'right outline' 'arc points' of all segments of stairs railing"), locked=True) - #obj.setEditorMode("OutlineRailArcRightAll",1) - if not hasattr(self,"OutlineRailArcLeftAll"): + # if not hasattr(obj,"OutlineRailArcLeftAll"): + # obj.addProperty("App::PropertyVectorList","OutlineRailArcLeftAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'left outline' 'arc points' of all segments of stairs railing"), locked=True) + # obj.setEditorMode("OutlineRailArcLeftAll",1) # Used for Outline of Railing + # if not hasattr(obj,"OutlineRailArcRightAll"): + # obj.addProperty("App::PropertyVectorList","OutlineRailArcRightAll","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","The 'right outline' 'arc points' of all segments of stairs railing"), locked=True) + # obj.setEditorMode("OutlineRailArcRightAll",1) + if not hasattr(self, "OutlineRailArcLeftAll"): self.OutlineRailArcLeftAll = [] - if not hasattr(self,"OutlineRailArcRightAll"): + if not hasattr(self, "OutlineRailArcRightAll"): self.OutlineRailArcRightAll = [] - if not hasattr(obj,"RailingHeightLeft"): - obj.addProperty("App::PropertyLength","RailingHeightLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Height of Railing on Left hand side from Stairs or Landing"), locked=True) - if not hasattr(obj,"RailingHeightRight"): - obj.addProperty("App::PropertyLength","RailingHeightRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Height of Railing on Right hand side from Stairs or Landing"), locked=True) - if not hasattr(obj,"RailingOffsetLeft"): - obj.addProperty("App::PropertyLength","RailingOffsetLeft","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Offset of Railing on Left hand side from stairs or landing Edge"), locked=True) - if not hasattr(obj,"RailingOffsetRight"): - obj.addProperty("App::PropertyLength","RailingOffsetRight","Segment and Parts",QT_TRANSLATE_NOOP("App::Property","Offset of Railing on Right hand side from stairs or landing Edge"), locked=True) + if not hasattr(obj, "RailingHeightLeft"): + obj.addProperty( + "App::PropertyLength", + "RailingHeightLeft", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", "Height of Railing on Left hand side from Stairs or Landing" + ), + locked=True, + ) + if not hasattr(obj, "RailingHeightRight"): + obj.addProperty( + "App::PropertyLength", + "RailingHeightRight", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", "Height of Railing on Right hand side from Stairs or Landing" + ), + locked=True, + ) + if not hasattr(obj, "RailingOffsetLeft"): + obj.addProperty( + "App::PropertyLength", + "RailingOffsetLeft", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", + "Offset of Railing on Left hand side from stairs or landing Edge", + ), + locked=True, + ) + if not hasattr(obj, "RailingOffsetRight"): + obj.addProperty( + "App::PropertyLength", + "RailingOffsetRight", + "Segment and Parts", + QT_TRANSLATE_NOOP( + "App::Property", + "Offset of Railing on Right hand side from stairs or landing Edge", + ), + locked=True, + ) # structural properties if not "Landings" in pl: - obj.addProperty("App::PropertyEnumeration","Landings","Structure",QT_TRANSLATE_NOOP("App::Property","The type of landings of these stairs"), locked=True) - obj.Landings = ["None","At center","At each corner"] + obj.addProperty( + "App::PropertyEnumeration", + "Landings", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The type of landings of these stairs"), + locked=True, + ) + obj.Landings = ["None", "At center", "At each corner"] # Not implemented yet, remarked out at the moment - #if not "Winders" in pl: + # if not "Winders" in pl: # obj.addProperty("App::PropertyEnumeration","Winders","Structure",QT_TRANSLATE_NOOP("App::Property","The type of winders in these stairs"), locked=True) # obj.Winders = ["None","All","Corners strict","Corners relaxed"] if not "Structure" in pl: - obj.addProperty("App::PropertyEnumeration","Structure","Structure",QT_TRANSLATE_NOOP("App::Property","The type of structure of these stairs"), locked=True) - obj.Structure = ["None","Massive","One stringer","Two stringers"] + obj.addProperty( + "App::PropertyEnumeration", + "Structure", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The type of structure of these stairs"), + locked=True, + ) + obj.Structure = ["None", "Massive", "One stringer", "Two stringers"] if not "StructureThickness" in pl: - obj.addProperty("App::PropertyLength","StructureThickness","Structure",QT_TRANSLATE_NOOP("App::Property","The thickness of the massive structure or of the stringers"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StructureThickness", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", "The thickness of the massive structure or of the stringers" + ), + locked=True, + ) if not "StringerWidth" in pl: - obj.addProperty("App::PropertyLength","StringerWidth","Structure",QT_TRANSLATE_NOOP("App::Property","The width of the stringers"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StringerWidth", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The width of the stringers"), + locked=True, + ) if not "StructureOffset" in pl: - obj.addProperty("App::PropertyLength","StructureOffset","Structure",QT_TRANSLATE_NOOP("App::Property","The offset between the border of the stairs and the structure"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StructureOffset", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", "The offset between the border of the stairs and the structure" + ), + locked=True, + ) if not "StringerOverlap" in pl: - obj.addProperty("App::PropertyLength","StringerOverlap","Structure",QT_TRANSLATE_NOOP("App::Property","The overlap of the stringers above the bottom of the treads"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StringerOverlap", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", "The overlap of the stringers above the bottom of the treads" + ), + locked=True, + ) if not "DownSlabThickness" in pl: - obj.addProperty("App::PropertyLength","DownSlabThickness","Structure",QT_TRANSLATE_NOOP("App::Property","The thickness of the lower floor slab"), locked=True) + obj.addProperty( + "App::PropertyLength", + "DownSlabThickness", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the lower floor slab"), + locked=True, + ) if not "UpSlabThickness" in pl: - obj.addProperty("App::PropertyLength","UpSlabThickness","Structure",QT_TRANSLATE_NOOP("App::Property","The thickness of the upper floor slab"), locked=True) + obj.addProperty( + "App::PropertyLength", + "UpSlabThickness", + "Structure", + QT_TRANSLATE_NOOP("App::Property", "The thickness of the upper floor slab"), + locked=True, + ) if not "ConnectionDownStartStairs" in pl: - obj.addProperty("App::PropertyEnumeration","ConnectionDownStartStairs","Structure",QT_TRANSLATE_NOOP("App::Property","The type of connection between the lower floor slab and the start of the stairs"), locked=True) - obj.ConnectionDownStartStairs = ["HorizontalCut","VerticalCut","HorizontalVerticalCut"] + obj.addProperty( + "App::PropertyEnumeration", + "ConnectionDownStartStairs", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", + "The type of connection between the lower floor slab and the start of the stairs", + ), + locked=True, + ) + obj.ConnectionDownStartStairs = [ + "HorizontalCut", + "VerticalCut", + "HorizontalVerticalCut", + ] if not "ConnectionEndStairsUp" in pl: - obj.addProperty("App::PropertyEnumeration","ConnectionEndStairsUp","Structure",QT_TRANSLATE_NOOP("App::Property","The type of connection between the end of the stairs and the upper floor slab"), locked=True) - obj.ConnectionEndStairsUp = ["toFlightThickness","toSlabThickness"] + obj.addProperty( + "App::PropertyEnumeration", + "ConnectionEndStairsUp", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", + "The type of connection between the end of the stairs and the upper floor slab", + ), + locked=True, + ) + obj.ConnectionEndStairsUp = ["toFlightThickness", "toSlabThickness"] # additional stairs properties if not "ArchSketchData" in pl: - obj.addProperty("App::PropertyBool","ArchSketchData","Stairs",QT_TRANSLATE_NOOP("App::Property","Use Base ArchSketch (if used) data (e.g. selected edge, widths, aligns) instead of Stairs' properties"), locked=True) + obj.addProperty( + "App::PropertyBool", + "ArchSketchData", + "Stairs", + QT_TRANSLATE_NOOP( + "App::Property", + "Use Base ArchSketch (if used) data (e.g. selected edge, widths, aligns) instead of Stairs' properties", + ), + locked=True, + ) obj.ArchSketchData = True # TODO Consider other properties for flight, landing etc. if not "ArchSketchEdges" in pl: - obj.addProperty("App::PropertyStringList","ArchSketchEdges","Stairs",QT_TRANSLATE_NOOP("App::Property","Selected edges of the base Sketch/ArchSketch, to use in creating the shape (flight) of this Arch Stairs (instead of using all the Base ArchSketch's edges by default). Input are index numbers of edges. Disabled and ignored if Base object (ArchSketch) provides selected edges (as Flight Axis) information, with getStairsBaseShapeEdgesInfo() method. [ENHANCEMENT by ArchSketch] GUI 'Edit Stairs' Tool is provided in external SketchArch Add-on to let users to (de)select the edges interactively. 'Toponaming-Tolerant' if ArchSketch is used in Base (and SketchArch Add-on is installed). Warning : Not 'Toponaming-Tolerant' if just Sketch is used."), locked=True) - if not hasattr(obj,"ArchSketchPropertySet"): - obj.addProperty("App::PropertyEnumeration","ArchSketchPropertySet","Stairs",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"): + obj.addProperty( + "App::PropertyStringList", + "ArchSketchEdges", + "Stairs", + QT_TRANSLATE_NOOP( + "App::Property", + "Selected edges of the base Sketch/ArchSketch, to use in creating the shape (flight) of this Arch Stairs (instead of using all the Base ArchSketch's edges by default). Input are index numbers of edges. Disabled and ignored if Base object (ArchSketch) provides selected edges (as Flight Axis) information, with getStairsBaseShapeEdgesInfo() method. [ENHANCEMENT by ArchSketch] GUI 'Edit Stairs' Tool is provided in external SketchArch Add-on to let users to (de)select the edges interactively. 'Toponaming-Tolerant' if ArchSketch is used in Base (and SketchArch Add-on is installed). Warning : Not 'Toponaming-Tolerant' if just Sketch is used.", + ), + locked=True, + ) + if not hasattr(obj, "ArchSketchPropertySet"): + obj.addProperty( + "App::PropertyEnumeration", + "ArchSketchPropertySet", + "Stairs", + 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): # Supercede Arch.Component.dumps() dump = super().dumps() if not isinstance(dump, tuple): - dump = (dump,) #Python Tuple With One Item + dump = (dump,) # Python Tuple With One Item dump = dump + (self.ArchSkPropSetPickedUuid, self.ArchSkPropSetListPrev) return dump - - def loads(self,state): + def loads(self, state): self.Type = "Stairs" if state == None: return - elif state[0] == 'S': # state[1] == 't', behaviour before 2024.11.28 + elif state[0] == "S": # state[1] == 't', behaviour before 2024.11.28 return - elif state[0] == 'Stairs': + elif state[0] == "Stairs": self.ArchSkPropSetPickedUuid = state[1] self.ArchSkPropSetListPrev = state[2] - elif state[0] != 'Stairs': # model before merging super.dumps/loads() + elif state[0] != "Stairs": # model before merging super.dumps/loads() self.ArchSkPropSetPickedUuid = state[0] self.ArchSkPropSetListPrev = state[1] + def onDocumentRestored(self, obj): - def onDocumentRestored(self,obj): - - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - if hasattr(obj,"OutlineWireLeft"): + if hasattr(obj, "OutlineWireLeft"): self.update_properties_0v18_to_0v20(obj) if obj.getTypeIdOfProperty("RailingLeft") == "App::PropertyString": self.update_properties_0v19_to_0v20(obj) - if (hasattr(obj,"ArchSketchData") and obj.ArchSketchData and - Draft.getType(obj.Base) == "ArchSketch"): - if hasattr(obj,"ArchSketchEdges"): + 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"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", 0) else: - if hasattr(obj,"ArchSketchEdges"): + if hasattr(obj, "ArchSketchEdges"): obj.setEditorMode("ArchSketchEdges", 0) - if hasattr(obj,"ArchSketchPropertySet"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) - def update_properties_0v18_to_0v20(self, obj): doc = FreeCAD.ActiveDocument outlineWireLeftObject = doc.getObject(obj.OutlineWireLeft) @@ -295,6 +600,7 @@ class _Stairs(ArchComponent.Component): obj.removeProperty("OutlineWireRight") self.update_properties_to_0v20(obj) from draftutils.messages import _log + _log( "v0.20.3, " + obj.Name @@ -314,6 +620,7 @@ class _Stairs(ArchComponent.Component): obj.RailingRight = railingRightObject self.update_properties_to_0v20(obj) from draftutils.messages import _log + _log( "v0.20.3, " + obj.Name @@ -333,8 +640,7 @@ class _Stairs(ArchComponent.Component): if obj.RailingRight is not None: obj.RailingRight.Visibility = True - def execute(self,obj): - + def execute(self, obj): "constructs the shape of the stairs" if self.clone(obj): @@ -342,7 +648,7 @@ class _Stairs(ArchComponent.Component): # Stairs can do without Base. Base validity is tested in code below. # Remarked out ensureBase() below - #if not self.ensureBase(obj): + # if not self.ensureBase(obj): # return self.steps = [] @@ -365,19 +671,20 @@ class _Stairs(ArchComponent.Component): propSetListCur = None if Draft.getType(obj.Base) == "ArchSketch": baseProxy = obj.Base.Proxy - if hasattr(baseProxy,"getPropertySet"): + if hasattr(baseProxy, "getPropertySet"): # get full list of PropertySet propSetListCur = baseProxy.getPropertySet(obj.Base) # get updated name (if any) of the selected PropertySet - propSetSelectedNameCur = baseProxy.getPropertySet(obj.Base, - propSetUuid=propSetPickedUuidPrev) + propSetSelectedNameCur = baseProxy.getPropertySet( + obj.Base, propSetUuid=propSetPickedUuidPrev + ) if propSetSelectedNameCur: # True if selection is not deleted if propSetListPrev != propSetListCur: obj.ArchSketchPropertySet = propSetListCur obj.ArchSketchPropertySet = propSetSelectedNameCur self.ArchSkPropSetListPrev = propSetListCur - #elif propSetListPrev == propSetListCur: - #pass #nothing to do in this case + # elif propSetListPrev == propSetListCur: + # pass #nothing to do in this case # but if below, though (propSetListPrev == propSetListCur) elif propSetSelectedNamePrev != propSetSelectedNameCur: obj.ArchSketchPropertySet = propSetSelectedNameCur @@ -385,29 +692,27 @@ class _Stairs(ArchComponent.Component): if propSetListCur: if propSetListPrev != propSetListCur: obj.ArchSketchPropertySet = propSetListCur - obj.ArchSketchPropertySet = 'Default' - #else: # Seems no need ... - #obj.PropertySet = 'Default' + obj.ArchSketchPropertySet = "Default" + # else: # Seems no need ... + # obj.PropertySet = 'Default' # Get ArchSketchData if enabled # e.g. flightAxis, floorHeight, landingAxis flightAxis = landingAxis = strucInfo = [] # None # baseProxy if obj.base is ArchSketch above - if baseProxy and obj.ArchSketchData and \ - hasattr(baseProxy, 'getStairsBaseShapeEdgesInfo'): + if baseProxy and obj.ArchSketchData and hasattr(baseProxy, "getStairsBaseShapeEdgesInfo"): propSetUuid = self.ArchSkPropSetPickedUuid - info = baseProxy.getStairsBaseShapeEdgesInfo(obj.Base, - propSetUuid=propSetUuid) + info = baseProxy.getStairsBaseShapeEdgesInfo(obj.Base, propSetUuid=propSetUuid) if info: - flightAxis = info.get('flightAxis') - strucInfo = info.get('strucInfo, None') - #floorHeight = info.get('floorHeight') TODO To support floorHeight + flightAxis = info.get("flightAxis") + strucInfo = info.get("strucInfo, None") + # floorHeight = info.get('floorHeight') TODO To support floorHeight # Still fallback to check if Base has Solids e.g. is another Stairs, # do not clear shape. - #if not strucInfo and not flightAxis: - #obj.Shape = Part.Shape() - #return #if ArchSketchData but no baseShapeEdge, no fallback - #pass + # if not strucInfo and not flightAxis: + # obj.Shape = Part.Shape() + # return #if ArchSketchData but no baseShapeEdge, no fallback + # pass # Creation of base Stairs shape @@ -415,7 +720,7 @@ class _Stairs(ArchComponent.Component): # just part of which further down below. To add missing codes here? # If ArchSketch not provides info, but there is obj.Base e.g. a Stairs if (not flightAxis and not strucInfo) and obj.Base: - if hasattr(obj.Base,"Shape"): + if hasattr(obj.Base, "Shape"): if obj.Base.Shape: if obj.Base.Shape.Solids: base = Part.Shape(obj.Base.Shape) @@ -424,11 +729,10 @@ class _Stairs(ArchComponent.Component): # TODO to Support individual width, height etc. for each flight/ landing # TODO 2025.9.21: Fix - obj.Height.Value can be 0 if edge is inclined - if (not base) and obj.Width.Value and obj.Height.Value and \ - (obj.NumberOfSteps > 0): + if (not base) and obj.Width.Value and obj.Height.Value and (obj.NumberOfSteps > 0): # Check if there is obj.Base and its validity to proceed if self.ensureBase(obj): - if not hasattr(obj.Base,'Shape'): + if not hasattr(obj.Base, "Shape"): return # TODO See todo remarks above, below to be further reviewed. if obj.Base.Shape.Solids: @@ -442,33 +746,37 @@ class _Stairs(ArchComponent.Component): if obj.Base.Shape.Faces: return - if baseProxy and obj.ArchSketchData and not flightAxis : + if baseProxy and obj.ArchSketchData and not flightAxis: obj.Shape = Part.Shape() # TODO Clear Railings also? return elif flightAxis: - edgeL = flightAxis #[flightAxis] + edgeL = flightAxis # [flightAxis] # If Base is Sketch and has input in ArchSketchEdges elif obj.Base.isDerivedFrom("Sketcher::SketchObject") and obj.ArchSketchEdges: edgeL = [] baseGeom = obj.Base.Geometry - lg =len(baseGeom) + lg = len(baseGeom) for e in obj.ArchSketchEdges: ie = int(e) - if lg >= (ie+1) : + if lg >= (ie + 1): if isinstance(baseGeom[ie], Part.LineSegment): edgeL.append(baseGeom[ie].toShape()) - #if not ArchSketchData and obj.ArchSketchEdges (whether stock Sketch or ArchSketch) - elif len(obj.Base.Shape.Edges) == 1: #elif not obj.ArchSketchData and len(obj.Base.Shape.Edges) == 1: + # if not ArchSketchData and obj.ArchSketchEdges (whether stock Sketch or ArchSketch) + elif ( + len(obj.Base.Shape.Edges) == 1 + ): # elif not obj.ArchSketchData and len(obj.Base.Shape.Edges) == 1: edgeL = [obj.Base.Shape.Edges[0]] - elif len(obj.Base.Shape.Edges) > 1: #elif not obj.ArchSketchData and len(obj.Base.Shape.Edges) > 1: # >= 1 - #if obj.NumberOfSteps == 1: + elif ( + len(obj.Base.Shape.Edges) > 1 + ): # elif not obj.ArchSketchData and len(obj.Base.Shape.Edges) > 1: # >= 1 + # if obj.NumberOfSteps == 1: # Sort the edges so each vertex tested of its tangent direction in order # TODO - Found Part.sortEdges() occasionally return less edges then 'input' edgeL = [Part.sortEdges(obj.Base.Shape.Edges)[0]] else: # Should not happen? edgeL = [] - #lenAxis = len(flightAxis) + len(landingAxis) + # lenAxis = len(flightAxis) + len(landingAxis) # Build Stairs if there is no obj.Base or even obj.Base is not valid else: @@ -476,44 +784,50 @@ class _Stairs(ArchComponent.Component): obj.Shape = Part.Shape() # TODO clear Railings also return - edgeL = [Part.LineSegment(Vector(0,0,0),Vector(obj.Length.Value,0,0)).toShape()] + edgeL = [ + Part.LineSegment(Vector(0, 0, 0), Vector(obj.Length.Value, 0, 0)).toShape() + ] for edge in edgeL: if not isinstance(edge, list): # i.e. a single edges - if isinstance(edge.Curve,(Part.LineSegment,Part.Line)): + if isinstance(edge.Curve, (Part.LineSegment, Part.Line)): # preparing for multi-edges landing / segment staircase if obj.NumberOfSteps > 1: # if the edge has a delta Z the start point must have lowest Z: if edge.Vertexes[0].Point.z > edge.Vertexes[1].Point.z: - edge = Part.LineSegment(edge.Vertexes[1].Point,edge.Vertexes[0].Point).toShape() - self.makeStraightStairsWithLanding(obj,edge) # all cases use makeStraightStairsWithLanding() + edge = Part.LineSegment( + edge.Vertexes[1].Point, edge.Vertexes[0].Point + ).toShape() + self.makeStraightStairsWithLanding( + obj, edge + ) # all cases use makeStraightStairsWithLanding() elif obj.NumberOfSteps == 1: # TODO - All use self.makeMultiEdgesLanding(obj,edges) ? - self.makeStraightLanding(obj,edge) + self.makeStraightLanding(obj, edge) elif obj.NumberOfSteps == 0: pass # TODO Should delete the whole shape else: # TODO Not implemented yet if obj.Landings == "At center": landings = 1 - self.makeCurvedStairsWithLanding(obj,edge) + self.makeCurvedStairsWithLanding(obj, edge) else: - self.makeCurvedStairs(obj,edge) - #elif len(edge.Edges) >= 1: + self.makeCurvedStairs(obj, edge) + # elif len(edge.Edges) >= 1: else: # i.e. isinstance(edge, list), or elif len(edge) >= 1: - #edges = Part.sortEdges(obj.Base.Shape.Edges)[0] - #self.makeMultiEdgesLanding(obj,edges) - self.makeMultiEdgesLanding(obj,edge) + # edges = Part.sortEdges(obj.Base.Shape.Edges)[0] + # self.makeMultiEdgesLanding(obj,edges) + self.makeMultiEdgesLanding(obj, edge) steps = risers = None # TODO 2025.9.25: 'steps'/'risers' not used, remarked out at the moment - #if self.steps: + # if self.steps: # steps = self.steps - #elif self.pseudosteps: + # elif self.pseudosteps: # steps = self.pseudosteps - #if self.risers: + # if self.risers: # risers = self.risers - #elif self.pseudorisers: + # elif self.pseudorisers: # risers = self.pseudorisers if self.structures or self.steps or self.risers: @@ -532,13 +846,12 @@ class _Stairs(ArchComponent.Component): pass else: - #base = None + # base = None obj.Shape = Part.Shape() # TODO Clear Railings also # return ? - - base = self.processSubShapes(obj,base,pl) + base = self.processSubShapes(obj, base, pl) if base: if not base.isNull(): obj.Shape = base @@ -551,133 +864,186 @@ class _Stairs(ArchComponent.Component): if obj.RailingLeft: railingLeftObject = obj.RailingLeft if obj.OutlineLeftAll: - railWireL, NU = _Stairs.returnOutlineWireFace(obj.OutlineLeftAll, self.OutlineRailArcLeftAll, mode = "notFaceAlso") + railWireL, NU = _Stairs.returnOutlineWireFace( + obj.OutlineLeftAll, self.OutlineRailArcLeftAll, mode="notFaceAlso" + ) elif obj.OutlineLeft: - railWireL, NU = _Stairs.returnOutlineWireFace(obj.OutlineLeft, self.OutlineRailArcLeft, mode = "notFaceAlso") + railWireL, NU = _Stairs.returnOutlineWireFace( + obj.OutlineLeft, self.OutlineRailArcLeft, mode="notFaceAlso" + ) else: - print (" No obj.OutlineLeftAll or obj.OutlineLeft") + print(" No obj.OutlineLeftAll or obj.OutlineLeft") if railWireL: - if Draft.getType(railingLeftObject.Base) != "Part::Feature": # Base can have wrong type or be None. + if ( + Draft.getType(railingLeftObject.Base) != "Part::Feature" + ): # Base can have wrong type or be None. if railingLeftObject.Base: doc.removeObject(railingLeftObject.Base.Name) - railingLeftWireObject = doc.addObject("Part::Feature","RailingWire") + railingLeftWireObject = doc.addObject("Part::Feature", "RailingWire") if FreeCAD.GuiUp: railingLeftWireObject.ViewObject.hide() railingLeftObject.Base = railingLeftWireObject # update the Base object shape railingLeftObject.Base.Shape = railWireL else: - print (" No railWireL created ") + print(" No railWireL created ") if obj.RailingRight: railingRightObject = obj.RailingRight if obj.OutlineRightAll: - railWireR, NU = _Stairs.returnOutlineWireFace(obj.OutlineRightAll, self.OutlineRailArcRightAll, mode = "notFaceAlso") + railWireR, NU = _Stairs.returnOutlineWireFace( + obj.OutlineRightAll, self.OutlineRailArcRightAll, mode="notFaceAlso" + ) elif obj.OutlineLeft: - railWireR, NU = _Stairs.returnOutlineWireFace(obj.OutlineLeft, self.OutlineRailArcRight, mode = "notFaceAlso") + railWireR, NU = _Stairs.returnOutlineWireFace( + obj.OutlineLeft, self.OutlineRailArcRight, mode="notFaceAlso" + ) else: - print (" No obj.OutlineRightAll or obj.OutlineLeft") + print(" No obj.OutlineRightAll or obj.OutlineLeft") if railWireR: - if Draft.getType(railingRightObject.Base) != "Part::Feature": # Base can have wrong type or be None. + if ( + Draft.getType(railingRightObject.Base) != "Part::Feature" + ): # Base can have wrong type or be None. if railingRightObject.Base: doc.removeObject(railingRightObject.Base.Name) - railingRightWireObject = doc.addObject("Part::Feature","RailingWire") + railingRightWireObject = doc.addObject("Part::Feature", "RailingWire") if FreeCAD.GuiUp: railingRightWireObject.ViewObject.hide() railingRightObject.Base = railingRightWireObject # update the Base object shape railingRightObject.Base.Shape = railWireR else: - print (" No railWireL created ") + print(" No railWireL created ") # compute step data if obj.NumberOfSteps > 1: - obj.BlondelRatio = obj.RiserHeight.Value*2+obj.TreadDepth.Value + obj.BlondelRatio = obj.RiserHeight.Value * 2 + obj.TreadDepth.Value + def onChanged(self, obj, prop): - def onChanged(self,obj,prop): + self.hideSubobjects(obj, prop) + ArchComponent.Component.onChanged(self, obj, prop) - self.hideSubobjects(obj,prop) - ArchComponent.Component.onChanged(self,obj,prop) - - if (prop == "ArchSketchPropertySet" - and Draft.getType(obj.Base) == "ArchSketch"): + if prop == "ArchSketchPropertySet" and Draft.getType(obj.Base) == "ArchSketch": baseProxy = obj.Base.Proxy - if hasattr(baseProxy,"getPropertySet"): - uuid = baseProxy.getPropertySet(obj, - propSetName=obj.ArchSketchPropertySet) + 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"): + 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"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", 0) else: - if hasattr(obj,"ArchSketchEdges"): + if hasattr(obj, "ArchSketchEdges"): obj.setEditorMode("ArchSketchEdges", 0) - if hasattr(obj,"ArchSketchPropertySet"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) - @staticmethod - def align(basepoint,align,widthvec): - + def align(basepoint, align, widthvec): "moves a given basepoint according to the alignment" if align == "Center": - basepoint = basepoint.add(DraftVecUtils.scale(widthvec,-0.5)) + basepoint = basepoint.add(DraftVecUtils.scale(widthvec, -0.5)) elif align == "Right": - basepoint = basepoint.add(DraftVecUtils.scale(widthvec,-1)) + basepoint = basepoint.add(DraftVecUtils.scale(widthvec, -1)) return basepoint - # TODO - def makeMultiEdgesLanding(self,obj,edges): + def makeMultiEdgesLanding(self, obj, edges): + "builds a 'multi-edges' landing from edges" # 'copying' from makeStraightLanding() - "builds a 'multi-edges' landing from edges" # 'copying' from makeStraightLanding() - - outline, outlineL, outlineR, vBase1, outlineP1P2ClosedNU, outlineP3P4ClosedNU, NU, pArc, pArcL, pArcR = self.returnOutlines(obj, edges, obj.Align, None, obj.Width, obj.WidthOfLanding, - obj.TreadThickness, zeroMM, zeroMM, zeroMM, zeroMM, zeroMM, True) + ( + outline, + outlineL, + outlineR, + vBase1, + outlineP1P2ClosedNU, + outlineP3P4ClosedNU, + NU, + pArc, + pArcL, + pArcR, + ) = self.returnOutlines( + obj, + edges, + obj.Align, + None, + obj.Width, + obj.WidthOfLanding, + obj.TreadThickness, + zeroMM, + zeroMM, + zeroMM, + zeroMM, + zeroMM, + True, + ) obj.AbsTop = vBase1[0] # TODO - stepWire, stepFace = _Stairs.returnOutlineWireFace(outline, pArc, mode = "faceAlso") #(outlinePoints, pArc, mode="wire or faceAlso") + stepWire, stepFace = _Stairs.returnOutlineWireFace( + outline, pArc, mode="faceAlso" + ) # (outlinePoints, pArc, mode="wire or faceAlso") if obj.TreadThickness.Value: # TODO - step = stepFace.extrude(Vector(0,0,abs(obj.TreadThickness.Value))) + step = stepFace.extrude(Vector(0, 0, abs(obj.TreadThickness.Value))) self.steps.append(step) else: self.pseudosteps.append(stepFace) if obj.StructureThickness.Value: # TODO landingFace = stepFace - struct = landingFace.extrude(Vector(0,0,-abs(obj.StructureThickness.Value))) + struct = landingFace.extrude(Vector(0, 0, -abs(obj.StructureThickness.Value))) if struct: self.structures.append(struct) - self.makeRailingOutline(obj,edges) - + self.makeRailingOutline(obj, edges) # TODO - def makeRailingOutline(self,obj,edges): - - "builds railing outline " - - outlineNotUsed, outlineRailL, outlineRailR, vBase2, outlineP1P2ClosedNU, outlineP3P4ClosedNU, NU, NU, pArcRailL, pArcRailR = self.returnOutlines(obj, edges, obj.Align, None, obj.Width, - obj.WidthOfLanding, obj.TreadThickness, zeroMM, - obj.RailingOffsetLeft, obj.RailingOffsetRight, - obj.RailingHeightLeft, obj.RailingHeightRight, True) - self.connectRailingVector(obj,outlineRailL,outlineRailR, pArcRailL, pArcRailR) + def makeRailingOutline(self, obj, edges): + "builds railing outline" + ( + outlineNotUsed, + outlineRailL, + outlineRailR, + vBase2, + outlineP1P2ClosedNU, + outlineP3P4ClosedNU, + NU, + NU, + pArcRailL, + pArcRailR, + ) = self.returnOutlines( + obj, + edges, + obj.Align, + None, + obj.Width, + obj.WidthOfLanding, + obj.TreadThickness, + zeroMM, + obj.RailingOffsetLeft, + obj.RailingOffsetRight, + obj.RailingHeightLeft, + obj.RailingHeightRight, + True, + ) + self.connectRailingVector(obj, outlineRailL, outlineRailR, pArcRailL, pArcRailR) @staticmethod def returnOutlineWireFace(outlinePoints, pArc, mode="wire or faceAlso"): stepFace = None - if not any(pArc): # i.e. no arc ... though any([0, '', False]):- is False + if not any(pArc): # i.e. no arc ... though any([0, '', False]):- is False stepWire = Part.makePolygon(outlinePoints) if mode == "faceAlso": stepFace = Part.Face(stepWire) @@ -687,11 +1053,17 @@ class _Stairs(ArchComponent.Component): lenoutlinePoints = len(outlinePoints) for k, a in enum_outlinePoints: - if k < (lenoutlinePoints-1): # iterate to last but 1: [k], [k+1] ... len() is +1 over index + if k < ( + lenoutlinePoints - 1 + ): # iterate to last but 1: [k], [k+1] ... len() is +1 over index if pArc[k] is None: - edges.append(Part.LineSegment(outlinePoints[k],outlinePoints[k+1]).toShape()) + edges.append( + Part.LineSegment(outlinePoints[k], outlinePoints[k + 1]).toShape() + ) else: - edges.append(Part.Arc(outlinePoints[k],pArc[k],outlinePoints[k+1]).toShape()) + edges.append( + Part.Arc(outlinePoints[k], pArc[k], outlinePoints[k + 1]).toShape() + ) stepWire = Part.Wire(edges) @@ -700,21 +1072,39 @@ class _Stairs(ArchComponent.Component): return stepWire, stepFace + @staticmethod # obj become stairsObj + def returnOutlines( + stairsObj, + edges, + align="Left", + mode=None, + widthFirstSegment=zeroMM, + widthOtherSegments=[], + treadThickness=zeroMM, + railStartRiser=zeroMM, + offsetHLeft=zeroMM, + offsetHRight=zeroMM, + offsetVLeft=zeroMM, + offsetVRight=zeroMM, + widthFirstSegmentDefault=False, + ): + """Construct outline of stairs landing or the like from Edges - Side effect is vertexes are 'ordered' in series of findIntersection() functions""" - @staticmethod # obj become stairsObj - def returnOutlines(stairsObj, edges, align="Left", mode=None, widthFirstSegment=zeroMM, widthOtherSegments=[], treadThickness=zeroMM, - railStartRiser=zeroMM, offsetHLeft=zeroMM, offsetHRight=zeroMM, offsetVLeft=zeroMM, offsetVRight=zeroMM, widthFirstSegmentDefault=False): + """ outlineP1P2Ordered seem no use at the moment """ - ''' Construct outline of stairs landing or the like from Edges - Side effect is vertexes are 'ordered' in series of findIntersection() functions ''' - - ''' outlineP1P2Ordered seem no use at the moment ''' - - #import DraftGeomUtils + # import DraftGeomUtils v, vLength, vWidth, vBase = [], [], [], [] - p1, p2, p3, p4, pArc, pArc1, pArc2 = [], [], [], [], [], [], [] # p1o, p2o - Not used - outline, outlineP1P2, outlineP3P4, outlineP1P2Closed, outlineP3P4Closed, outlineP1P2Ordered = [], [], [], [], [], [] + p1, p2, p3, p4, pArc, pArc1, pArc2 = [], [], [], [], [], [], [] # p1o, p2o - Not used + ( + outline, + outlineP1P2, + outlineP3P4, + outlineP1P2Closed, + outlineP3P4Closed, + outlineP1P2Ordered, + ) = ([], [], [], [], [], []) if not isinstance(edges, list): edges = [edges] @@ -722,79 +1112,92 @@ class _Stairs(ArchComponent.Component): enum_edges = enumerate(edges) for i, edge in enum_edges: - isLine = isinstance(edge.Curve,(Part.Line, Part.LineSegment)) - isArc = isinstance(edge.Curve,Part.Circle) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? + isLine = isinstance(edge.Curve, (Part.Line, Part.LineSegment)) + isArc = isinstance( + edge.Curve, Part.Circle + ) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? - ''' (1) append v (vec) ''' - v.append(DraftGeomUtils.vec(edge)) # TODO check all function below ok with curve? + """ (1) append v (vec) """ + v.append(DraftGeomUtils.vec(edge)) # TODO check all function below ok with curve? - - ''' (2) get netWidthI ''' + """ (2) get netWidthI """ netWidthI = 0 if i > 0: try: - if widthOtherSegments[i-1] > 0 or (not widthFirstSegmentDefault): - netWidthI = widthOtherSegments[i-1] - offsetHLeft.Value - offsetHRight.Value #2*offsetH - else: # i.e. elif widthFirstSegmentDefault: - netWidthI = widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value #2*offsetH + if widthOtherSegments[i - 1] > 0 or (not widthFirstSegmentDefault): + netWidthI = ( + widthOtherSegments[i - 1] - offsetHLeft.Value - offsetHRight.Value + ) # 2*offsetH + else: # i.e. elif widthFirstSegmentDefault: + netWidthI = ( + widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value + ) # 2*offsetH except Exception: if widthFirstSegmentDefault: - netWidthI = widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value #2*offsetH + netWidthI = ( + widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value + ) # 2*offsetH else: - netWidthI = widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value #2*offsetH + netWidthI = ( + widthFirstSegment.Value - offsetHLeft.Value - offsetHRight.Value + ) # 2*offsetH - - ''' (3) append vBase ''' + """ (3) append vBase """ vBase.append(edges[i].Vertexes[0].Point) if isArc: vBase1 = edge.Vertexes[1].Point - vBase2 = (edge.valueAt((edge.LastParameter+edge.FirstParameter)/2)) - #vBase2vec = (vBase2-vBase[i]) # - would not be correct if Align is not Left + vBase2 = edge.valueAt((edge.LastParameter + edge.FirstParameter) / 2) + # vBase2vec = (vBase2-vBase[i]) # - would not be correct if Align is not Left - ''' (1a) calc & append vLength - Need v (vec) ''' - vLength.append(Vector(v[i].x,v[i].y,v[i].z)) # TODO check all function below ok with curve? # TODO vLength in this f() is 3d + """ (1a) calc & append vLength - Need v (vec) """ + vLength.append( + Vector(v[i].x, v[i].y, v[i].z) + ) # TODO check all function below ok with curve? # TODO vLength in this f() is 3d + """ (1b, 2a) calc & append vWidth - Need vLength, netWidthI """ - ''' (1b, 2a) calc & append vWidth - Need vLength, netWidthI ''' - - #vWidth.append(DraftVecUtils.scaleTo(vLength[i].cross(Vector(0,0,1)),netWidthI)) + # vWidth.append(DraftVecUtils.scaleTo(vLength[i].cross(Vector(0,0,1)),netWidthI)) if isLine: - dvec = vLength[i].cross(Vector(0,0,1)) + dvec = vLength[i].cross(Vector(0, 0, 1)) elif isArc: - #dvec = edge.Vertexes[0].Point.sub(edge.Curve.Center) # TODO - how to determine direction? - Reference from ArchWall; used tangentAt instead - #dvec1 = edge.Vertexes[1].Point.sub(edge.Curve.Center) - dvec = edge.tangentAt(edge.FirstParameter).cross(Vector(0,0,1)) - dvec1 = edge.tangentAt(edge.LastParameter).cross(Vector(0,0,1)) - dvec2 = edge.tangentAt((edge.LastParameter+edge.FirstParameter)/2).cross(Vector(0,0,1)) + # dvec = edge.Vertexes[0].Point.sub(edge.Curve.Center) # TODO - how to determine direction? - Reference from ArchWall; used tangentAt instead + # dvec1 = edge.Vertexes[1].Point.sub(edge.Curve.Center) + dvec = edge.tangentAt(edge.FirstParameter).cross(Vector(0, 0, 1)) + dvec1 = edge.tangentAt(edge.LastParameter).cross(Vector(0, 0, 1)) + dvec2 = edge.tangentAt((edge.LastParameter + edge.FirstParameter) / 2).cross( + Vector(0, 0, 1) + ) - vWidth.append(DraftVecUtils.scaleTo(dvec,netWidthI)) + vWidth.append(DraftVecUtils.scaleTo(dvec, netWidthI)) if isArc: - vWidth1=DraftVecUtils.scaleTo(dvec1,netWidthI) - vWidth2=DraftVecUtils.scaleTo(dvec2,netWidthI) + vWidth1 = DraftVecUtils.scaleTo(dvec1, netWidthI) + vWidth2 = DraftVecUtils.scaleTo(dvec2, netWidthI) - ''' (3a) alter vBase ''' + """ (3a) alter vBase """ if stairsObj: vBase[i] = stairsObj.Proxy.vbaseFollowLastSegment(stairsObj, vBase[i]) if isArc: vBase1 = stairsObj.Proxy.vbaseFollowLastSegment(stairsObj, vBase1) vBase2 = stairsObj.Proxy.vbaseFollowLastSegment(stairsObj, vBase2) - vBase[i] = vBase[i].add(Vector(0,0,offsetVLeft.Value)) - vBase[i] = vBase[i].add(Vector(0,0,railStartRiser.Value)) + vBase[i] = vBase[i].add(Vector(0, 0, offsetVLeft.Value)) + vBase[i] = vBase[i].add(Vector(0, 0, railStartRiser.Value)) if isArc: - vBase1 = vBase1.add(Vector(0,0,offsetVLeft.Value)) # TODO - if arc is flight (sloping then), arc would be ellipse, so the following become incorrect? - vBase1 = vBase1.add(Vector(0,0,railStartRiser.Value)) - vBase2 = vBase2.add(Vector(0,0,offsetVLeft.Value)) - vBase2 = vBase2.add(Vector(0,0,railStartRiser.Value)) + vBase1 = vBase1.add( + Vector(0, 0, offsetVLeft.Value) + ) # TODO - if arc is flight (sloping then), arc would be ellipse, so the following become incorrect? + vBase1 = vBase1.add(Vector(0, 0, railStartRiser.Value)) + vBase2 = vBase2.add(Vector(0, 0, offsetVLeft.Value)) + vBase2 = vBase2.add(Vector(0, 0, railStartRiser.Value)) - vOffsetH = DraftVecUtils.scaleTo(dvec,offsetHLeft.Value) + vOffsetH = DraftVecUtils.scaleTo(dvec, offsetHLeft.Value) if isArc: - vOffsetH1 = DraftVecUtils.scaleTo(dvec1,offsetHLeft.Value) - vOffsetH2 = DraftVecUtils.scaleTo(dvec2,offsetHLeft.Value) + vOffsetH1 = DraftVecUtils.scaleTo(dvec1, offsetHLeft.Value) + vOffsetH2 = DraftVecUtils.scaleTo(dvec2, offsetHLeft.Value) if align == "Left": vBase[i] = _Stairs.align(vBase[i], "Right", -vOffsetH) @@ -807,49 +1210,87 @@ class _Stairs(ArchComponent.Component): vBase1 = _Stairs.align(vBase1, "Right", vOffsetH1) vBase2 = _Stairs.align(vBase2, "Right", vOffsetH2) + """ (3b, 2b/1c) get + alter [p1, p2, p3, p4] - Need vBase """ - ''' (3b, 2b/1c) get + alter [p1, p2, p3, p4] - Need vBase ''' - - p1.append(_Stairs.align(vBase[i], align, vWidth[i]).add(Vector(0,0,-abs(treadThickness.Value)))) # vWidth already calculated above against arc geometry + p1.append( + _Stairs.align(vBase[i], align, vWidth[i]).add( + Vector(0, 0, -abs(treadThickness.Value)) + ) + ) # vWidth already calculated above against arc geometry if isLine: - p2.append(p1[i].add(vLength[i]).add(Vector(0,0,-railStartRiser.Value))) - p3.append(p2[i].add(vWidth[i]).add(Vector(0,0,(offsetVRight-offsetVLeft).Value))) - p4.append(p3[i].add(DraftVecUtils.neg(vLength[i])).add(Vector(0,0,railStartRiser.Value))) + p2.append(p1[i].add(vLength[i]).add(Vector(0, 0, -railStartRiser.Value))) + p3.append( + p2[i].add(vWidth[i]).add(Vector(0, 0, (offsetVRight - offsetVLeft).Value)) + ) + p4.append( + p3[i].add(DraftVecUtils.neg(vLength[i])).add(Vector(0, 0, railStartRiser.Value)) + ) pArc1.append(None) pArc2.append(None) elif isArc: - p2.append(_Stairs.align(vBase1, align, vWidth1).add(Vector(0,0,-abs(treadThickness.Value))).add(Vector(0,0,-railStartRiser.Value))) - p3.append(p2[i].add(vWidth1.add(Vector(0,0,(offsetVRight-offsetVLeft).Value)))) - p4.append(p1[i].add(vWidth[i].add(Vector(0,0,(offsetVRight-offsetVLeft).Value)))) - pArc1.append(_Stairs.align(vBase2, align, vWidth2).add(Vector(0,0,-abs(treadThickness.Value))).add(Vector(0,0,-railStartRiser.Value))) - pArc2.append(pArc1[i].add(vWidth2.add(Vector(0,0,(offsetVRight-offsetVLeft).Value)))) + p2.append( + _Stairs.align(vBase1, align, vWidth1) + .add(Vector(0, 0, -abs(treadThickness.Value))) + .add(Vector(0, 0, -railStartRiser.Value)) + ) + p3.append(p2[i].add(vWidth1.add(Vector(0, 0, (offsetVRight - offsetVLeft).Value)))) + p4.append( + p1[i].add(vWidth[i].add(Vector(0, 0, (offsetVRight - offsetVLeft).Value))) + ) + pArc1.append( + _Stairs.align(vBase2, align, vWidth2) + .add(Vector(0, 0, -abs(treadThickness.Value))) + .add(Vector(0, 0, -railStartRiser.Value)) + ) + pArc2.append( + pArc1[i].add(vWidth2.add(Vector(0, 0, (offsetVRight - offsetVLeft).Value))) + ) - ''' (3c, 2c/2d) from [p1, p2, p3, p4] - calc outlineP1P2, outlineP3P4 ''' + """ (3c, 2c/2d) from [p1, p2, p3, p4] - calc outlineP1P2, outlineP3P4 """ if i > 0: - lastEdge = edges[i-1] # thisEdge = edge - p1last = p1[i-1] - p2last = p2[i-1] - p3last = p3[i-1] - p4last = p4[i-1] - p1this = p1[i] - p2this = p2[i] - p3this = p3[i] - p4this = p4[i] - pArc1last = pArc1[i-1] - pArc2last = pArc2[i-1] + lastEdge = edges[i - 1] # thisEdge = edge + p1last = p1[i - 1] + p2last = p2[i - 1] + p3last = p3[i - 1] + p4last = p4[i - 1] + p1this = p1[i] + p2this = p2[i] + p3this = p3[i] + p4this = p4[i] + pArc1last = pArc1[i - 1] + pArc2last = pArc2[i - 1] pArc1this = pArc1[i] pArc2this = pArc2[i] + lastEdgeIsLineSegmentBool = isinstance( + lastEdge.Curve, (Part.Line, Part.LineSegment) + ) + thisEdgeIsLineSegmentBool = isinstance(edge.Curve, (Part.Line, Part.LineSegment)) - lastEdgeIsLineSegmentBool = isinstance(lastEdge.Curve,(Part.Line, Part.LineSegment)) - thisEdgeIsLineSegmentBool = isinstance(edge.Curve,(Part.Line, Part.LineSegment)) + lastEdgeIsCircleBool = isinstance( + lastEdge.Curve, (Part.Circle) + ) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? + thisEdgeIsCircleBool = isinstance(edge.Curve, (Part.Circle)) - lastEdgeIsCircleBool = isinstance(lastEdge.Curve,(Part.Circle)) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? - thisEdgeIsCircleBool = isinstance(edge.Curve,(Part.Circle)) - - intersectionP1P2, intersectionP3P4 = _Stairs.findLineArcIntersections(p1last, p2last, p3last, p4last, p1this, p2this, p3this, p4this, lastEdgeIsLineSegmentBool, thisEdgeIsLineSegmentBool, - lastEdgeIsCircleBool, thisEdgeIsCircleBool, pArc1last, pArc2last, pArc1this, pArc2this) + intersectionP1P2, intersectionP3P4 = _Stairs.findLineArcIntersections( + p1last, + p2last, + p3last, + p4last, + p1this, + p2this, + p3this, + p4this, + lastEdgeIsLineSegmentBool, + thisEdgeIsLineSegmentBool, + lastEdgeIsCircleBool, + thisEdgeIsCircleBool, + pArc1last, + pArc2last, + pArc1this, + pArc2this, + ) outlineP1P2.append(intersectionP1P2) outlineP3P4.insert(0, intersectionP3P4) @@ -865,102 +1306,172 @@ class _Stairs(ArchComponent.Component): outline.append(p1[0]) pArc1.append(None) - pArc2 = pArc2[::-1] # pArcReverse = pArc2[::-1] + pArc2 = pArc2[::-1] # pArcReverse = pArc2[::-1] pArc2.append(None) pArc.extend(pArc1) - pArc.extend(pArc2) # pArc.extend(pArcReverse) + pArc.extend(pArc2) # pArc.extend(pArcReverse) - firstEdgeIsLineSegmentBool = isinstance(edges[0].Curve,(Part.Line, Part.LineSegment)) - firstEdgeIsCircleBool = isinstance(edges[0].Curve,(Part.Circle)) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? + firstEdgeIsLineSegmentBool = isinstance(edges[0].Curve, (Part.Line, Part.LineSegment)) + firstEdgeIsCircleBool = isinstance( + edges[0].Curve, (Part.Circle) + ) # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? - if mode in ["OrderedClose", "OrderedCloseAndOrderedOpen"]: # seem only using 'OrderedClose' - intersectionP1P2, intersectionP3P4 = _Stairs.findLineArcIntersections(p1this, p2this, p3this, p4this, p1[0], p2[0], p3[0], p4[0], thisEdgeIsLineSegmentBool, firstEdgeIsLineSegmentBool, - thisEdgeIsCircleBool, firstEdgeIsCircleBool, pArc1this, pArc2this, pArc1[0], pArc2[0]) + if mode in ["OrderedClose", "OrderedCloseAndOrderedOpen"]: # seem only using 'OrderedClose' + intersectionP1P2, intersectionP3P4 = _Stairs.findLineArcIntersections( + p1this, + p2this, + p3this, + p4this, + p1[0], + p2[0], + p3[0], + p4[0], + thisEdgeIsLineSegmentBool, + firstEdgeIsLineSegmentBool, + thisEdgeIsCircleBool, + firstEdgeIsCircleBool, + pArc1this, + pArc2this, + pArc1[0], + pArc2[0], + ) outlineP1P2Closed = list(outlineP1P2) - outlineP1P2Closed[0] = intersectionP1P2 #intersection[0] - outlineP1P2Closed[i+1] = intersectionP1P2 #intersection[0] + outlineP1P2Closed[0] = intersectionP1P2 # intersection[0] + outlineP1P2Closed[i + 1] = intersectionP1P2 # intersection[0] outlineP3P4Closed = list(outlineP3P4) - outlineP3P4Closed[0] = intersectionP3P4 #intersection[0] - outlineP3P4Closed[i+1] = intersectionP3P4 #intersection[0] + outlineP3P4Closed[0] = intersectionP3P4 # intersection[0] + outlineP3P4Closed[i + 1] = intersectionP3P4 # intersection[0] if mode in ["OrderedOpen", "OrderedCloseAndOrderedOpen"]: - if i > 0: # Multi-edge, otherwise no use + if i > 0: # Multi-edge, otherwise no use outlineP1P2Ordered = list(outlineP1P2) - ''' Guessing the 1st Start Point based on Intersection ''' + """ Guessing the 1st Start Point based on Intersection """ vx1 = Vector(outlineP1P2[1].x, outlineP1P2[1].y, 0) l0 = Part.LineSegment(edges[0].Vertexes[0].Point, edges[0].Vertexes[1].Point) try: distFrom1stParameter = l0.parameter(vx1) - distFrom2ndParameter = l0.length()-distFrom1stParameter + distFrom2ndParameter = l0.length() - distFrom1stParameter - ''' Further point of this line from intersection ''' + """ Further point of this line from intersection """ if distFrom2ndParameter > distFrom1stParameter: foundStart = edges[0].Vertexes[1].Point - else: # if distFrom2ndParameter = / < distFrom1stParameter (i.e. if equal, Vertexes[0].Point is taken ?) + else: # if distFrom2ndParameter = / < distFrom1stParameter (i.e. if equal, Vertexes[0].Point is taken ?) foundStart = edges[0].Vertexes[0].Point except Exception: - print('Intersection point Not on this edge') + print("Intersection point Not on this edge") - ''' Guessing the last End Point based on Intersection ''' + """ Guessing the last End Point based on Intersection """ vx99 = Vector(outlineP1P2[i].x, outlineP1P2[i].y, 0) l99 = Part.LineSegment(edges[i].Vertexes[0].Point, edges[i].Vertexes[1].Point) try: distFrom1stParameter = l99.parameter(vx99) - distFrom2ndParameter = l99.length()-distFrom1stParameter + distFrom2ndParameter = l99.length() - distFrom1stParameter if distFrom2ndParameter > distFrom1stParameter: foundEnd = edges[i].Vertexes[1].Point else: foundEnd = edges[i].Vertexes[0].Point except Exception: - print('Intersection point Not on this edge') + print("Intersection point Not on this edge") outlineP1P2Ordered[0] = foundStart - outlineP1P2Ordered[i+1] = foundEnd - - return outline, outlineP1P2, outlineP3P4, vBase, outlineP1P2Closed, outlineP3P4Closed, outlineP1P2Ordered, pArc, pArc1, pArc2 + outlineP1P2Ordered[i + 1] = foundEnd + return ( + outline, + outlineP1P2, + outlineP3P4, + vBase, + outlineP1P2Closed, + outlineP3P4Closed, + outlineP1P2Ordered, + pArc, + pArc1, + pArc2, + ) @staticmethod - def findLineArcIntersections(p1last, p2last, p3last, p4last, p1this, p2this, p3this, p4this, lastEdgeIsLineSegmentBool, thisEdgeIsLineSegmentBool, lastEdgeIsCircleBool, thisEdgeIsCircleBool, - pArc1last, pArc2last, pArc1this, pArc2this): + def findLineArcIntersections( + p1last, + p2last, + p3last, + p4last, + p1this, + p2this, + p3this, + p4this, + lastEdgeIsLineSegmentBool, + thisEdgeIsLineSegmentBool, + lastEdgeIsCircleBool, + thisEdgeIsCircleBool, + pArc1last, + pArc2last, + pArc1this, + pArc2this, + ): if lastEdgeIsLineSegmentBool and thisEdgeIsLineSegmentBool: - intersectionsP1P2 = DraftGeomUtils.findIntersection(p1last,p2last,p1this,p2this,True,True) - intersectionsP3P4 = DraftGeomUtils.findIntersection(p3last,p4last,p3this,p4this,True,True) + intersectionsP1P2 = DraftGeomUtils.findIntersection( + p1last, p2last, p1this, p2this, True, True + ) + intersectionsP3P4 = DraftGeomUtils.findIntersection( + p3last, p4last, p3this, p4this, True, True + ) return intersectionsP1P2[0], intersectionsP3P4[0] else: if lastEdgeIsCircleBool: - edge1 = Part.Arc(p1last,pArc1last,p2last).toShape() # edge1 = Part.Arc(p1[i-1],pArc1[i-1],p2[i-1]).toShape() - edge1a = Part.Arc(p3last,pArc2last,p4last).toShape() # edge1a = Part.Arc(p3[i-1],pArc2[i-1],p4[i-1]).toShape() + edge1 = Part.Arc( + p1last, pArc1last, p2last + ).toShape() # edge1 = Part.Arc(p1[i-1],pArc1[i-1],p2[i-1]).toShape() + edge1a = Part.Arc( + p3last, pArc2last, p4last + ).toShape() # edge1a = Part.Arc(p3[i-1],pArc2[i-1],p4[i-1]).toShape() else: - edge1 = Part.LineSegment(p1last,p2last).toShape() # edge1 = Part.LineSegment(p1[i-1],p2[i-1]).toShape() - edge1a = Part.LineSegment(p3last,p4last).toShape() # edge1a = Part.LineSegment(p3[i-1],p4[i-1]).toShape() + edge1 = Part.LineSegment( + p1last, p2last + ).toShape() # edge1 = Part.LineSegment(p1[i-1],p2[i-1]).toShape() + edge1a = Part.LineSegment( + p3last, p4last + ).toShape() # edge1a = Part.LineSegment(p3[i-1],p4[i-1]).toShape() - if thisEdgeIsCircleBool: # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? - edge2 = Part.Arc(p1this,pArc1this,p2this).toShape() # edge2 = Part.Arc(p1[i],pArc1[i],p2[i]).toShape() - edge2a = Part.Arc(p3this,pArc2this,p4this).toShape() # edge2a = Part.Arc(p3[i],pArc2[i],p4[i]).toShape() + if ( + thisEdgeIsCircleBool + ): # why it is Part.Circle for an Arc Edge? - why Part.ArcOfCircle Not Working? + edge2 = Part.Arc( + p1this, pArc1this, p2this + ).toShape() # edge2 = Part.Arc(p1[i],pArc1[i],p2[i]).toShape() + edge2a = Part.Arc( + p3this, pArc2this, p4this + ).toShape() # edge2a = Part.Arc(p3[i],pArc2[i],p4[i]).toShape() else: - edge2 = Part.LineSegment(p1this,p2this).toShape() # edge2 = Part.LineSegment(p1[i],p2[i]).toShape() - edge2a = Part.LineSegment(p3this,p4this).toShape() # edge2a = Part.LineSegment(p3[i],p4[i]).toShape() - intersections = DraftGeomUtils.findIntersection(edge1, edge2, True,True) + edge2 = Part.LineSegment( + p1this, p2this + ).toShape() # edge2 = Part.LineSegment(p1[i],p2[i]).toShape() + edge2a = Part.LineSegment( + p3this, p4this + ).toShape() # edge2a = Part.LineSegment(p3[i],p4[i]).toShape() + intersections = DraftGeomUtils.findIntersection(edge1, edge2, True, True) enum_intersections = enumerate(intersections) distList = [] for n, intersectionI in enum_intersections: - distList.append((intersectionI-p1this).Length) # distList.append((intersectionI-p1[i]).Length)) # TODO just use p1[i] for test; may be p2[i-1]...? + distList.append( + (intersectionI - p1this).Length + ) # distList.append((intersectionI-p1[i]).Length)) # TODO just use p1[i] for test; may be p2[i-1]...? # TODO - To test and follow up if none intersection is found nearestIntersectionIndex = distList.index(min(distList)) nearestIntersectionP1P2 = intersections[nearestIntersectionIndex] - intersections = DraftGeomUtils.findIntersection(edge1a, edge2a, True,True) + intersections = DraftGeomUtils.findIntersection(edge1a, edge2a, True, True) enum_intersections = enumerate(intersections) distList = [] for n, intersectionI in enum_intersections: - distList.append((intersectionI-p4this).Length) # distList.append((intersectionI-p4[i]).Length)) # TODO just use p4[i] for test; may be p3[i-1]...? + distList.append( + (intersectionI - p4this).Length + ) # distList.append((intersectionI-p4[i]).Length)) # TODO just use p4[i] for test; may be p3[i-1]...? nearestIntersectionIndex = distList.index(min(distList)) nearestIntersectionP3P4 = intersections[nearestIntersectionIndex] return nearestIntersectionP1P2, nearestIntersectionP3P4 @@ -969,20 +1480,23 @@ class _Stairs(ArchComponent.Component): def vbaseFollowLastSegment(obj, vBase): if obj.LastSegment: lastSegmentAbsTop = obj.LastSegment.AbsTop - vBase = Vector(vBase.x, vBase.y,lastSegmentAbsTop.z) # use Last Segment top's z-coordinate + vBase = Vector( + vBase.x, vBase.y, lastSegmentAbsTop.z + ) # use Last Segment top's z-coordinate return vBase - # Add flag (temporarily?) for indicating which method call this to determine whether the landing has been 're-based' before or not - def makeStraightLanding(self,obj,edge,numberofsteps=None, callByMakeStraightStairsWithLanding=False): # what is use of numberofsteps ? + def makeStraightLanding( + self, obj, edge, numberofsteps=None, callByMakeStraightStairsWithLanding=False + ): # what is use of numberofsteps ? "builds a landing from a straight edge" # general data if not numberofsteps: numberofsteps = obj.NumberOfSteps v = DraftGeomUtils.vec(edge) - vLength = Vector(v.x,v.y,0) - vWidth = vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) + vLength = Vector(v.x, v.y, 0) + vWidth = vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0, 0, 1)), obj.Width.Value) vBase = edge.Vertexes[0].Point # if not call by makeStraightStairsWithLanding() - not 're-base' in function there, then 're-base' here @@ -990,57 +1504,59 @@ class _Stairs(ArchComponent.Component): vBase = self.vbaseFollowLastSegment(obj, vBase) obj.AbsTop = vBase - if not obj.Flight in ["HalfTurnLeft","HalfTurnRight"]: - vNose = DraftVecUtils.scaleTo(vLength,-abs(obj.Nosing.Value)) + if not obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: + vNose = DraftVecUtils.scaleTo(vLength, -abs(obj.Nosing.Value)) else: - vNose = Vector(0,0,0) + vNose = Vector(0, 0, 0) h = 0 l = 0 if obj.RiserHeightEnforce != 0: h = obj.RiserHeightEnforce * numberofsteps - elif obj.Base: # TODO - should this happen? - though in original code - if hasattr(obj.Base,'Shape'): - #l = obj.Base.Shape.Length - #if obj.Base.Shape.BoundBox.ZLength: - if round(obj.Base.Shape.BoundBox.ZLength,Draft.precision()) != 0: # ? - need precision - h = obj.Base.Shape.BoundBox.ZLength #.Value? + elif obj.Base: # TODO - should this happen? - though in original code + if hasattr(obj.Base, "Shape"): + # l = obj.Base.Shape.Length + # if obj.Base.Shape.BoundBox.ZLength: + if ( + round(obj.Base.Shape.BoundBox.ZLength, Draft.precision()) != 0 + ): # ? - need precision + h = obj.Base.Shape.BoundBox.ZLength # .Value? else: - print ("obj.Base has 0 z-value") - print (h) + print("obj.Base has 0 z-value") + print(h) if (h == 0) and obj.Height.Value != 0: h = obj.Height.Value else: - print (h) + print(h) if obj.TreadDepthEnforce != 0: - l = obj.TreadDepthEnforce.Value * (numberofsteps-2) + l = obj.TreadDepthEnforce.Value * (numberofsteps - 2) if obj.LandingDepth: l += obj.LandingDepth.Value else: l += obj.Width.Value elif obj.Base: - if hasattr(obj.Base,'Shape'): - l = obj.Base.Shape.Length #.Value? + if hasattr(obj.Base, "Shape"): + l = obj.Base.Shape.Length # .Value? elif obj.Length.Value != 0: l = obj.Length.Value if obj.LandingDepth: - fLength = float(l-obj.LandingDepth.Value)/(numberofsteps-2) + fLength = float(l - obj.LandingDepth.Value) / (numberofsteps - 2) else: - fLength = float(l-obj.Width.Value)/(numberofsteps-2) + fLength = float(l - obj.Width.Value) / (numberofsteps - 2) - fHeight = float(h)/numberofsteps - a = math.atan(fHeight/fLength) - print("landing data:",fLength,":",fHeight) + fHeight = float(h) / numberofsteps + a = math.atan(fHeight / fLength) + print("landing data:", fLength, ":", fHeight) # step - p1 = self.align(vBase,obj.Align,vWidth) - p1o = p1.add(Vector(0,0,-abs(obj.TreadThickness.Value))) + p1 = self.align(vBase, obj.Align, vWidth) + p1o = p1.add(Vector(0, 0, -abs(obj.TreadThickness.Value))) - p1 = p1.add(vNose).add(Vector(0,0,-abs(obj.TreadThickness.Value))) + p1 = p1.add(vNose).add(Vector(0, 0, -abs(obj.TreadThickness.Value))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) @@ -1058,9 +1574,9 @@ class _Stairs(ArchComponent.Component): p3 = p3.add(vWidth) p4 = p4.add(vWidth) - step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) + step = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1])) if obj.TreadThickness.Value: - step = step.extrude(Vector(0,0,abs(obj.TreadThickness.Value))) + step = step.extrude(Vector(0, 0, abs(obj.TreadThickness.Value))) self.steps.append(step) else: self.pseudosteps.append(step) @@ -1068,55 +1584,63 @@ class _Stairs(ArchComponent.Component): # structure struct = None p1 = p1.add(DraftVecUtils.neg(vNose)) - p2 = p1.add(Vector(0,0,-(abs(fHeight) - obj.TreadThickness.Value))) + p2 = p1.add(Vector(0, 0, -(abs(fHeight) - obj.TreadThickness.Value))) p3 = p2.add(vLength) p4 = p1.add(vLength) if obj.Structure == "Massive": if obj.StructureThickness.Value: - struct = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) + struct = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1])) evec = vWidth - mvec = FreeCAD.Vector(0,0,0) + mvec = FreeCAD.Vector(0, 0, 0) if obj.StructureOffset.Value: - mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) + mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset.Value) struct.translate(mvec) - if obj.Flight in ["HalfTurnLeft","HalfTurnRight"]: - evec = DraftVecUtils.scaleTo(evec,2*evec.Length-2*mvec.Length) + if obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: + evec = DraftVecUtils.scaleTo(evec, 2 * evec.Length - 2 * mvec.Length) else: - evec = DraftVecUtils.scaleTo(evec,evec.Length-(2*mvec.Length)) + evec = DraftVecUtils.scaleTo(evec, evec.Length - (2 * mvec.Length)) struct = struct.extrude(evec) - elif obj.Structure in ["One stringer","Two stringers"]: + elif obj.Structure in ["One stringer", "Two stringers"]: if obj.StringerWidth.Value and obj.StructureThickness.Value: - reslength = fHeight/math.tan(a) - p1b = p1.add(DraftVecUtils.scaleTo(vLength,reslength)) - p1c = p1.add(Vector(0,0,-fHeight)) - reslength = obj.StructureThickness.Value/math.cos(a) - p1d = p1c.add(Vector(0,0,-reslength)) - reslength = obj.StructureThickness.Value*math.tan(a/2) - p2 = p1b.add(DraftVecUtils.scaleTo(vLength,reslength)).add(Vector(0,0,-obj.StructureThickness.Value)) - p3 = p4.add(DraftVecUtils.scaleTo(vLength,reslength)).add(Vector(0,0,-obj.StructureThickness.Value)) + reslength = fHeight / math.tan(a) + p1b = p1.add(DraftVecUtils.scaleTo(vLength, reslength)) + p1c = p1.add(Vector(0, 0, -fHeight)) + reslength = obj.StructureThickness.Value / math.cos(a) + p1d = p1c.add(Vector(0, 0, -reslength)) + reslength = obj.StructureThickness.Value * math.tan(a / 2) + p2 = p1b.add(DraftVecUtils.scaleTo(vLength, reslength)).add( + Vector(0, 0, -obj.StructureThickness.Value) + ) + p3 = p4.add(DraftVecUtils.scaleTo(vLength, reslength)).add( + Vector(0, 0, -obj.StructureThickness.Value) + ) if obj.TreadThickness.Value: - reslength = obj.TreadThickness.Value/math.tan(a) - p3c = p4.add(DraftVecUtils.scaleTo(vLength,reslength)).add(Vector(0,0,obj.TreadThickness.Value)) - reslength = obj.StructureThickness.Value/math.sin(a) - p3b = p3c.add(DraftVecUtils.scaleTo(vLength,reslength)) - pol = Part.Face(Part.makePolygon([p1b,p1c,p1d,p2,p3,p3b,p3c,p4,p1b])) + reslength = obj.TreadThickness.Value / math.tan(a) + p3c = p4.add(DraftVecUtils.scaleTo(vLength, reslength)).add( + Vector(0, 0, obj.TreadThickness.Value) + ) + reslength = obj.StructureThickness.Value / math.sin(a) + p3b = p3c.add(DraftVecUtils.scaleTo(vLength, reslength)) + pol = Part.Face(Part.makePolygon([p1b, p1c, p1d, p2, p3, p3b, p3c, p4, p1b])) else: - reslength = obj.StructureThickness.Value/math.sin(a) - p3b = p4.add(DraftVecUtils.scaleTo(vLength,reslength)) - pol = Part.Face(Part.makePolygon([p1b,p1c,p1d,p2,p3,p3b,p1b])) - evec = DraftVecUtils.scaleTo(vWidth,obj.StringerWidth.Value) + reslength = obj.StructureThickness.Value / math.sin(a) + p3b = p4.add(DraftVecUtils.scaleTo(vLength, reslength)) + pol = Part.Face(Part.makePolygon([p1b, p1c, p1d, p2, p3, p3b, p1b])) + evec = DraftVecUtils.scaleTo(vWidth, obj.StringerWidth.Value) if obj.Structure == "One stringer": if obj.StructureOffset.Value: - mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) + mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset.Value) else: - mvec = DraftVecUtils.scaleTo(vWidth,(vWidth.Length/2)-obj.StringerWidth.Value/2) + mvec = DraftVecUtils.scaleTo( + vWidth, (vWidth.Length / 2) - obj.StringerWidth.Value / 2 + ) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset.Value: - mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) + mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset.Value) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) @@ -1124,31 +1648,46 @@ class _Stairs(ArchComponent.Component): pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) - struct = Part.makeCompound([s1,s2]) + struct = Part.makeCompound([s1, s2]) # Overwriting result of above functions if case fit - should better avoid running the above in first place (better rewrite later) if not callByMakeStraightStairsWithLanding: if obj.StructureThickness.Value: struct = None - landingFace = Part.Face(Part.makePolygon([p1o,p2o,p3o,p4o,p1o])) - struct = landingFace.extrude(Vector(0,0,-abs(obj.StructureThickness.Value))) + landingFace = Part.Face(Part.makePolygon([p1o, p2o, p3o, p4o, p1o])) + struct = landingFace.extrude(Vector(0, 0, -abs(obj.StructureThickness.Value))) if struct: self.structures.append(struct) - - def makeStraightStairs(self,obj,edge,s1,s2,numOfSteps=None, - downstartstairs=None,endstairsup=None,hgt=None, - vWidth=None,align=None,vLength=None,vHeight=None, - vNose=None,vRiserThickness=None, - vTreadThickness=None,structure=None, - structureThickness=None,downSlabThickness=None, - upSlabThickness=None,structureOffset=None, - stringerWidth=None,stringerOverlap=None): - + def makeStraightStairs( + self, + obj, + edge, + s1, + s2, + numOfSteps=None, + downstartstairs=None, + endstairsup=None, + hgt=None, + vWidth=None, + align=None, + vLength=None, + vHeight=None, + vNose=None, + vRiserThickness=None, + vTreadThickness=None, + structure=None, + structureThickness=None, + downSlabThickness=None, + upSlabThickness=None, + structureOffset=None, + stringerWidth=None, + stringerOverlap=None, + ): "builds a simple, straight staircase from a straight edge" - ''' + """ edge : Edge defining the flight/landing like stairs' direction, run, rise/height etc. (mandatory) Below parameters, if provided, would overrides information derived from the edge and/or Stairs' built-in properties - @@ -1175,11 +1714,19 @@ class _Stairs(ArchComponent.Component): (TODO : To support custom input of tread, riser, structure, stringer etc. and output of these parts individually) - ''' + """ # Upgrade obj if it is from an older version of FreeCAD if not hasattr(obj, "StringerOverlap"): - obj.addProperty("App::PropertyLength","StringerOverlap","Structure",QT_TRANSLATE_NOOP("App::Property","The overlap of the stringers above the bottom of the treads"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StringerOverlap", + "Structure", + QT_TRANSLATE_NOOP( + "App::Property", "The overlap of the stringers above the bottom of the treads" + ), + locked=True, + ) v = DraftGeomUtils.vec(edge) v_proj = Vector(v.x, v.y, 0) # Projected on XY plane. May not be @@ -1203,14 +1750,14 @@ class _Stairs(ArchComponent.Component): # setup hgt (flight height) if not hgt: - if round(v.z,Draft.precision()) != 0: + if round(v.z, Draft.precision()) != 0: hgt = v.z else: hgt = obj.Height.Value # setup vWidth (flight/tread width) if not vWidth: - vWidth = DraftVecUtils.scaleTo(v_proj.cross(Vector(0,0,1)),obj.Width.Value) + vWidth = DraftVecUtils.scaleTo(v_proj.cross(Vector(0, 0, 1)), obj.Width.Value) # setup align if not align: @@ -1218,11 +1765,13 @@ class _Stairs(ArchComponent.Component): # setup vLength (tread length(depth) ) if not vLength: - vLength = DraftVecUtils.scaleTo(v_proj,float(v_proj.Length)/(numOfSteps-1)) # TODO need this float? + vLength = DraftVecUtils.scaleTo( + v_proj, float(v_proj.Length) / (numOfSteps - 1) + ) # TODO need this float? # setup vHeight (tread height) if not vHeight: - vHeight = Vector(0,0,float(hgt)/numOfSteps) + vHeight = Vector(0, 0, float(hgt) / numOfSteps) # setup vBase: Based on edge provided vBase = edge.Vertexes[0].Point @@ -1230,34 +1779,40 @@ class _Stairs(ArchComponent.Component): if not callByMakeStraightStairsWithLanding: if obj.LastSegment: # TODO lastSegmentAbsTop = obj.LastSegment.AbsTop # TODO - vBase = Vector(vBase.x, vBase.y,lastSegmentAbsTop.z) # TODO # use Last Segment top's z-coordinate - obj.AbsTop = vBase.add(Vector(0,0,hgt)) # TODO + vBase = Vector( + vBase.x, vBase.y, lastSegmentAbsTop.z + ) # TODO # use Last Segment top's z-coordinate + obj.AbsTop = vBase.add(Vector(0, 0, hgt)) # TODO # setup vNose (nosing length) if not vNose: - vNose = DraftVecUtils.scaleTo(v_proj,-abs(obj.Nosing.Value)) + vNose = DraftVecUtils.scaleTo(v_proj, -abs(obj.Nosing.Value)) # setup ang - ang = math.atan(vHeight.Length/vLength.Length) + ang = math.atan(vHeight.Length / vLength.Length) # setup vBasedAligned - vBasedAligned = self.align(vBase,align,vWidth) + vBasedAligned = self.align(vBase, align, vWidth) if not vRiserThickness: - vRiserThickness = DraftVecUtils.scaleTo(v_proj,obj.RiserThickness.Value) + vRiserThickness = DraftVecUtils.scaleTo(v_proj, obj.RiserThickness.Value) if not vTreadThickness: - vTreadThickness = DraftVecUtils.scaleTo(vHeight,obj.TreadThickness.Value) + vTreadThickness = DraftVecUtils.scaleTo(vHeight, obj.TreadThickness.Value) # steps and risers - for i in range(numOfSteps-1): - p1 = vBasedAligned.add((Vector(vLength).multiply(i)).add(Vector(vHeight).multiply(i+1))) # TODO need Vector()? + for i in range(numOfSteps - 1): + p1 = vBasedAligned.add( + (Vector(vLength).multiply(i)).add(Vector(vHeight).multiply(i + 1)) + ) # TODO need Vector()? p1 = p1.add(-vTreadThickness) # TODO 2025.4.20 Tested: Seems DraftVecUtils.neg(x) == -x r1 = p1 p1 = p1.add(vNose) - p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) # TODO 2025.4.20 Tested: Seems DraftVecUtils.neg(x) == -x + p2 = p1.add(DraftVecUtils.neg(vNose)).add( + vLength + ) # TODO 2025.4.20 Tested: Seems DraftVecUtils.neg(x) == -x p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) # TODO 2025.4.20 - step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) + step = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1])) if vTreadThickness.Length: step = step.extrude(vTreadThickness) # TODO need abs()? @@ -1267,18 +1822,20 @@ class _Stairs(ArchComponent.Component): self.pseudosteps.append(step) # TODO 2025.4.20 To Review - ''' risers - add to steps or pseudosteps in the meantime before adding self.risers / self.pseudorisers ''' + """ risers - add to steps or pseudosteps in the meantime before adding self.risers / self.pseudorisers """ - r2 = r1.add(-vHeight) #vResHeight # TODO 2025.4.20 Tested: Seems DraftVecUtils.neg(x) == -x + r2 = r1.add( + -vHeight + ) # vResHeight # TODO 2025.4.20 Tested: Seems DraftVecUtils.neg(x) == -x if i == 0: r2 = r2.add(vTreadThickness) r3 = r2.add(vWidth) - r4 = r3.add(vHeight) #vResHeight + r4 = r3.add(vHeight) # vResHeight if i == 0: r4 = r4.add(-vTreadThickness) - riser = Part.Face(Part.makePolygon([r1,r2,r3,r4,r1])) + riser = Part.Face(Part.makePolygon([r1, r2, r3, r4, r1])) if vRiserThickness.Length: - riser = riser.extrude(vRiserThickness) #Vector(0,100,0)) + riser = riser.extrude(vRiserThickness) # Vector(0,100,0)) self.steps.append(riser) # Separate risers from steps, keep above in the meantime self.risers.append(riser) @@ -1305,7 +1862,7 @@ class _Stairs(ArchComponent.Component): # '# Massive Structure to respect 'align' attribute' vBase = vBasedAligned.add(vRiserThickness) - for i in range(numOfSteps-1): + for i in range(numOfSteps - 1): if not lProfile: lProfile.append(vBase) last = lProfile[-1] @@ -1316,35 +1873,41 @@ class _Stairs(ArchComponent.Component): lProfile.append(lProfile[-1].add(vLength)) lProfile[-1] = lProfile[-1].add(-vRiserThickness) - resHeight1 = structureThickness/math.cos(ang) - dh = s2 - float(hgt)/numOfSteps + resHeight1 = structureThickness / math.cos(ang) + dh = s2 - float(hgt) / numOfSteps - resHeight2 = ((numOfSteps-1)*vHeight.Length) - dh + resHeight2 = ((numOfSteps - 1) * vHeight.Length) - dh if endstairsup == "toFlightThickness": - lProfile.append(lProfile[-1].add(Vector(0,0,-resHeight1))) - resHeight2 = ((numOfSteps-1)*vHeight.Length)-(resHeight1+vTreadThickness.z) + lProfile.append(lProfile[-1].add(Vector(0, 0, -resHeight1))) + resHeight2 = ((numOfSteps - 1) * vHeight.Length) - ( + resHeight1 + vTreadThickness.z + ) - resLength = (vLength.Length/vHeight.Length)*resHeight2 - h = DraftVecUtils.scaleTo(vLength,-resLength) + resLength = (vLength.Length / vHeight.Length) * resHeight2 + h = DraftVecUtils.scaleTo(vLength, -resLength) elif endstairsup == "toSlabThickness": - resLength = (vLength.Length/vHeight.Length) * resHeight2 - h = DraftVecUtils.scaleTo(vLength,-resLength) + resLength = (vLength.Length / vHeight.Length) * resHeight2 + h = DraftVecUtils.scaleTo(vLength, -resLength) th = (resHeight1 + vTreadThickness.z) - dh resLength2 = th / math.tan(ang) - lProfile.append(lProfile[-1].add(Vector(0,0,vTreadThickness.z - dh))) - lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength,resLength2))) + lProfile.append(lProfile[-1].add(Vector(0, 0, vTreadThickness.z - dh))) + lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength, resLength2))) - if s1 > resHeight1: + if s1 > resHeight1: downstartstairs = "VerticalCut" if downstartstairs == "VerticalCut": if not downSlabThickness: downSlabThickness = obj.DownSlabThickness.Value dh = downSlabThickness - resHeight1 - vTreadThickness.z resHeight2 = resHeight2 + downSlabThickness - dh - resLength = (vLength.Length/vHeight.Length)*resHeight2 - lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength,-resLength)).add(Vector(0,0,-resHeight2))) + resLength = (vLength.Length / vHeight.Length) * resHeight2 + lProfile.append( + lProfile[-1] + .add(DraftVecUtils.scaleTo(vLength, -resLength)) + .add(Vector(0, 0, -resHeight2)) + ) elif downstartstairs == "HorizontalVerticalCut": temp_s1 = s1 @@ -1354,13 +1917,17 @@ class _Stairs(ArchComponent.Component): if upSlabThickness > resHeight1: s1 = temp_s1 resHeight2 = resHeight2 + s1 - resLength = (vLength.Length/vHeight.Length) * resHeight2 + resLength = (vLength.Length / vHeight.Length) * resHeight2 th = (resHeight1 - s1) + vTreadThickness.z resLength2 = th / math.tan(ang) - lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength,-resLength)).add(Vector(0,0,-resHeight2))) - lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength,-resLength2))) + lProfile.append( + lProfile[-1] + .add(DraftVecUtils.scaleTo(vLength, -resLength)) + .add(Vector(0, 0, -resHeight2)) + ) + lProfile.append(lProfile[-1].add(DraftVecUtils.scaleTo(vLength, -resLength2))) else: - lProfile.append(lProfile[-1].add(Vector(h.x,h.y,-resHeight2))) + lProfile.append(lProfile[-1].add(Vector(h.x, h.y, -resHeight2))) lProfile.append(vBase) @@ -1368,59 +1935,65 @@ class _Stairs(ArchComponent.Component): struct = Part.Face(pol) evec = vWidth if structureOffset: - mvec = DraftVecUtils.scaleTo(vWidth,structureOffset) + mvec = DraftVecUtils.scaleTo(vWidth, structureOffset) struct.translate(mvec) - evec = DraftVecUtils.scaleTo(evec,evec.Length-(2*mvec.Length)) + evec = DraftVecUtils.scaleTo(evec, evec.Length - (2 * mvec.Length)) struct = struct.extrude(evec) - elif structure in ["One stringer","Two stringers"]: + elif structure in ["One stringer", "Two stringers"]: # setup stringerWidth if not stringerWidth: stringerWidth = obj.StringerWidth.Value if stringerWidth and structureThickness: hyp = math.sqrt(vHeight.Length**2 + vLength.Length**2) - l1 = Vector(vLength).multiply(numOfSteps-1) + l1 = Vector(vLength).multiply(numOfSteps - 1) # setup stringerOverlap if not stringerOverlap: stringerOverlap = obj.StringerOverlap.Value - h1 = Vector(vHeight).multiply(numOfSteps-1).add(Vector(0,0,-vTreadThickness.Length+stringerOverlap)) + h1 = ( + Vector(vHeight) + .multiply(numOfSteps - 1) + .add(Vector(0, 0, -vTreadThickness.Length + stringerOverlap)) + ) p1 = vBase.add(l1).add(h1) - p1 = self.align(p1,align,vWidth) - if stringerOverlap <= float(hgt)/numOfSteps: + p1 = self.align(p1, align, vWidth) + if stringerOverlap <= float(hgt) / numOfSteps: lProfile.append(p1) else: - p1b = vBase.add(l1).add(Vector(0,0,float(h))) - p1a = p1b.add(Vector(vLength).multiply((p1b.z-p1.z)/vHeight.Length)) + p1b = vBase.add(l1).add(Vector(0, 0, float(h))) + p1a = p1b.add(Vector(vLength).multiply((p1b.z - p1.z) / vHeight.Length)) lProfile.append(p1a) lProfile.append(p1b) - h2 = (structureThickness/vLength.Length)*hyp - lProfile.append(p1.add(Vector(0,0,-abs(h2)))) - h3 = lProfile[-1].z-vBase.z - l3 = (h3/vHeight.Length)*vLength.Length - v3 = DraftVecUtils.scaleTo(vLength,-l3) - lProfile.append(lProfile[-1].add(Vector(0,0,-abs(h3))).add(v3)) - l4 = (structureThickness/vHeight.Length)*hyp - v4 = DraftVecUtils.scaleTo(vLength,-l4) + h2 = (structureThickness / vLength.Length) * hyp + lProfile.append(p1.add(Vector(0, 0, -abs(h2)))) + h3 = lProfile[-1].z - vBase.z + l3 = (h3 / vHeight.Length) * vLength.Length + v3 = DraftVecUtils.scaleTo(vLength, -l3) + lProfile.append(lProfile[-1].add(Vector(0, 0, -abs(h3))).add(v3)) + l4 = (structureThickness / vHeight.Length) * hyp + v4 = DraftVecUtils.scaleTo(vLength, -l4) lProfile.append(lProfile[-1].add(v4)) lProfile.append(lProfile[0]) - #print(lProfile) + # print(lProfile) pol = Part.makePolygon(lProfile) pol = Part.Face(pol) - evec = DraftVecUtils.scaleTo(vWidth,stringerWidth) + evec = DraftVecUtils.scaleTo(vWidth, stringerWidth) if obj.Structure == "One stringer": if structureOffset: - mvec = DraftVecUtils.scaleTo(vWidth,structureOffset) + mvec = DraftVecUtils.scaleTo(vWidth, structureOffset) else: - mvec = DraftVecUtils.scaleTo(vWidth,(vWidth.Length/2)-stringerWidth/2) + mvec = DraftVecUtils.scaleTo( + vWidth, (vWidth.Length / 2) - stringerWidth / 2 + ) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if structureOffset: - mvec = DraftVecUtils.scaleTo(vWidth,structureOffset) + mvec = DraftVecUtils.scaleTo(vWidth, structureOffset) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) @@ -1428,17 +2001,28 @@ class _Stairs(ArchComponent.Component): pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) - struct = Part.makeCompound([s1,s2]) + struct = Part.makeCompound([s1, s2]) if struct: self.structures.append(struct) - - def makeStraightStairsWithLanding(self,obj,edge, numOfSteps=None, landings=None, landingDepth=None, height=None, width=None, align=None, treadDepthEnforce=None, riserHeightEnforce=None, flight=None): - + def makeStraightStairsWithLanding( + self, + obj, + edge, + numOfSteps=None, + landings=None, + landingDepth=None, + height=None, + width=None, + align=None, + treadDepthEnforce=None, + riserHeightEnforce=None, + flight=None, + ): "builds a straight staircase with/without a landing in the middle" - ''' + """ edge : Edge defining the flight/landing like stairs' direction, run, rise/height etc. (mandatory) Below parameters are optional, and if provided, would overrides information derived from the edge and/or Stairs' built-in properties - @@ -1454,10 +2038,10 @@ class _Stairs(ArchComponent.Component): (TODO : To support custom input of tread, riser, structure, stringer etc. and output of these parts individually) - ''' + """ v = DraftGeomUtils.vec(edge) - v_proj = Vector(v.x, v.y, 0) # Projected on XY plane. + v_proj = Vector(v.x, v.y, 0) # Projected on XY plane. landingStep = 0 # setup numOfSteps @@ -1465,50 +2049,54 @@ class _Stairs(ArchComponent.Component): if numOfSteps < 2: print("Fewer than 2 steps, unable to create/update stairs") return - #else: print(" OK, numOfSteps equal or > 2 steps, proceed creation") - elif obj.NumberOfSteps < 2: #elif numOfSteps is None and obj.NumberOfSteps < 2: # Would not be called by execute() + # else: print(" OK, numOfSteps equal or > 2 steps, proceed creation") + elif ( + obj.NumberOfSteps < 2 + ): # elif numOfSteps is None and obj.NumberOfSteps < 2: # Would not be called by execute() print("Fewer than 2 steps, unable to create/update stairs") return - else: #elif obj.NumberOfSteps >= 2: #elif numOfSteps is None and obj.NumberOfSteps >= 2: + else: # elif obj.NumberOfSteps >= 2: #elif numOfSteps is None and obj.NumberOfSteps >= 2: numOfSteps = obj.NumberOfSteps print(" numOfSteps = obj.NumberOfSteps - ", numOfSteps) # setup landingStep - step number of landing, if present - if (landings == "At center" or obj.Landings == "At center"): + if landings == "At center" or obj.Landings == "At center": wantLanding = True else: wantLanding = False if wantLanding and numOfSteps > 3: - #landing = int(obj.NumberOfSteps/2) - landingStep = int(numOfSteps/2) # so, landing >= 1 + # landing = int(obj.NumberOfSteps/2) + landingStep = int(numOfSteps / 2) # so, landing >= 1 hasLanding = True else: - #landing = obj.NumberOfSteps + # landing = obj.NumberOfSteps landingStep = numOfSteps hasLanding = False if wantLanding: # but not numOfSteps > 3, so no hasLanding print("Fewer than 4 steps, unable to create landing") # setup height - if height is None: #if not height: + if height is None: # if not height: height = obj.Height.Value # check width - if width is None: #if not width: + if width is None: # if not width: width = obj.Width.Value # check align if align is None: align = obj.Align # setup vLength (tread length(depth) ) : check treadDepthEnforce - if treadDepthEnforce and treadDepthEnforce != 'Auto': - vLength = DraftVecUtils.scaleTo(v_proj,treadDepthEnforce) + if treadDepthEnforce and treadDepthEnforce != "Auto": + vLength = DraftVecUtils.scaleTo(v_proj, treadDepthEnforce) obj.TreadDepth = treadDepthEnforce - elif treadDepthEnforce == 'Auto' or obj.TreadDepthEnforce == 0: #elif treadDepth is None and ... + elif ( + treadDepthEnforce == "Auto" or obj.TreadDepthEnforce == 0 + ): # elif treadDepth is None and ... # check landings if hasLanding: # check landingDepth - if landingDepth and landingDepth != 'Auto': # i.e. landingDepth == float + if landingDepth and landingDepth != "Auto": # i.e. landingDepth == float reslength = v_proj.Length - landingDepth elif (landingDepth is None) and obj.LandingDepth: reslength = v_proj.Length - obj.LandingDepth.Value @@ -1516,135 +2104,209 @@ class _Stairs(ArchComponent.Component): else: # e.g. landingDepth == 'Auto', obj.LandingDepth == 0 reslength = v_proj.Length - width landingDepth = width - treadDepth = reslength/(numOfSteps-2) + treadDepth = reslength / (numOfSteps - 2) else: reslength = v_proj.Length - treadDepth = reslength/(numOfSteps-1) + treadDepth = reslength / (numOfSteps - 1) obj.TreadDepth = treadDepth - vLength = DraftVecUtils.scaleTo(v_proj,treadDepth) + vLength = DraftVecUtils.scaleTo(v_proj, treadDepth) else: # if obj.TreadDepthEnforce obj.TreadDepth = obj.TreadDepthEnforce - vLength = DraftVecUtils.scaleTo(v_proj,obj.TreadDepthEnforce.Value) + vLength = DraftVecUtils.scaleTo(v_proj, obj.TreadDepthEnforce.Value) # setup vWidth (tread width) - vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),width) + vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0, 0, 1)), width) # setup h, hstep (flight height, riser height) : check riserHeightEnforce - if riserHeightEnforce and riserHeightEnforce != 'Auto': + if riserHeightEnforce and riserHeightEnforce != "Auto": h = riserHeightEnforce * numOfSteps hstep = riserHeight obj.RiserHeight = hstep - #if obj.RiserHeightEnforce == 0: - elif riserHeightEnforce == 'Auto' or obj.RiserHeightEnforce == 0: # elif riserHeightEnforce is None and ... - if round(v.z,Draft.precision()) != 0: + # if obj.RiserHeightEnforce == 0: + elif ( + riserHeightEnforce == "Auto" or obj.RiserHeightEnforce == 0 + ): # elif riserHeightEnforce is None and ... + if round(v.z, Draft.precision()) != 0: h = v.z else: h = height - hstep = h/numOfSteps + hstep = h / numOfSteps obj.RiserHeight = hstep else: # if obj.RiserHeightEnforce - #h = obj.RiserHeightEnforce.Value * (obj.NumberOfSteps) + # h = obj.RiserHeightEnforce.Value * (obj.NumberOfSteps) h = obj.RiserHeightEnforce.Value * numOfSteps hstep = obj.RiserHeightEnforce.Value obj.RiserHeight = hstep - vHeight = Vector(0,0,hstep) + vHeight = Vector(0, 0, hstep) p1 = edge.Vertexes[0].Point # TODO To add support for multi-segments stairs if obj.LastSegment: lastSegmentAbsTop = obj.LastSegment.AbsTop - p1 = Vector(p1.x, p1.y,lastSegmentAbsTop.z) # use Last Segment top's z-coordinate - obj.AbsTop = p1.add(Vector(0,0,h)) - p2h = landingStep*hstep - p2 = p1.add(DraftVecUtils.scale(vLength,landingStep-1).add(Vector(0,0,p2h))) - edgeP1p2 = Part.LineSegment(p1,p2).toShape() + p1 = Vector(p1.x, p1.y, lastSegmentAbsTop.z) # use Last Segment top's z-coordinate + obj.AbsTop = p1.add(Vector(0, 0, h)) + p2h = landingStep * hstep + p2 = p1.add(DraftVecUtils.scale(vLength, landingStep - 1).add(Vector(0, 0, p2h))) + edgeP1p2 = Part.LineSegment(p1, p2).toShape() if hasLanding: - p3 = p2.add(DraftVecUtils.scaleTo(vLength,landingDepth)) - halfTurn = ['HalfTurnLeft', 'HalfTurnRight'] - #if obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: - if ( (flight in halfTurn) or - (flight is None and (obj.Flight in halfTurn)) ): - if (flight == 'HalfTurnLeft') or (obj.Flight in 'HalfTurnLeft') : - #(flight is None and (obj.Flight in 'HalfTurnLeft')) ): - halfTurnLR = 'HalfTurnLeft' + p3 = p2.add(DraftVecUtils.scaleTo(vLength, landingDepth)) + halfTurn = ["HalfTurnLeft", "HalfTurnRight"] + # if obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: + if (flight in halfTurn) or (flight is None and (obj.Flight in halfTurn)): + if (flight == "HalfTurnLeft") or (obj.Flight in "HalfTurnLeft"): + # (flight is None and (obj.Flight in 'HalfTurnLeft')) ): + halfTurnLR = "HalfTurnLeft" else: - halfTurnLR = 'HalfTurnRight' - if (align == "Left" and halfTurnLR == "HalfTurnLeft") or (align == "Right" and halfTurnLR == "HalfTurnRight"): + halfTurnLR = "HalfTurnRight" + if (align == "Left" and halfTurnLR == "HalfTurnLeft") or ( + align == "Right" and halfTurnLR == "HalfTurnRight" + ): p3r = p2 - elif (align == "Left" and halfTurnLR == "HalfTurnRight"): - p3r = self.align(p2,"Right",-2*vWidth) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() - elif (align == "Right" and halfTurnLR == "HalfTurnLeft"): - p3r = self.align(p2,"Right",2*vWidth) - elif (align == "Center" and halfTurnLR == "HalfTurnLeft"): - p3r = self.align(p2,"Right",vWidth) - elif (align == "Center" and halfTurnLR == "HalfTurnRight"): - p3r = self.align(p2,"Right",-vWidth) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() + elif align == "Left" and halfTurnLR == "HalfTurnRight": + p3r = self.align( + p2, "Right", -2 * vWidth + ) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() + elif align == "Right" and halfTurnLR == "HalfTurnLeft": + p3r = self.align(p2, "Right", 2 * vWidth) + elif align == "Center" and halfTurnLR == "HalfTurnLeft": + p3r = self.align(p2, "Right", vWidth) + elif align == "Center" and halfTurnLR == "HalfTurnRight": + p3r = self.align( + p2, "Right", -vWidth + ) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() else: print("Should have a bug here, if see this") if p3r: - p4r = p3r.add(DraftVecUtils.scale(-vLength,numOfSteps-(landingStep+1)).add(Vector(0,0,(numOfSteps-landingStep)*hstep))) + p4r = p3r.add( + DraftVecUtils.scale(-vLength, numOfSteps - (landingStep + 1)).add( + Vector(0, 0, (numOfSteps - landingStep) * hstep) + ) + ) # TODO To support DownSlabThickness, UpSlabThickness per flight - self.makeStraightStairs(obj,Part.LineSegment(p3r,p4r).toShape(), - hstep,obj.UpSlabThickness.Value, - numOfSteps-landingStep, - "HorizontalVerticalCut",None,hgt=h, - vWidth=-vWidth,align=align, - vLength=-vLength,vHeight=vHeight) + self.makeStraightStairs( + obj, + Part.LineSegment(p3r, p4r).toShape(), + hstep, + obj.UpSlabThickness.Value, + numOfSteps - landingStep, + "HorizontalVerticalCut", + None, + hgt=h, + vWidth=-vWidth, + align=align, + vLength=-vLength, + vHeight=vHeight, + ) else: - #p4 = p3.add(DraftVecUtils.scale(vLength,numOfSteps-(landingStep+1)).add(Vector(0,0,(numOfSteps-landingStep)*hstep))) - p4 = p3.add(DraftVecUtils.scale(vLength,numOfSteps-( - landingStep+1)).add(Vector(0,0,(numOfSteps- - landingStep)*hstep))) + # p4 = p3.add(DraftVecUtils.scale(vLength,numOfSteps-(landingStep+1)).add(Vector(0,0,(numOfSteps-landingStep)*hstep))) + p4 = p3.add( + DraftVecUtils.scale(vLength, numOfSteps - (landingStep + 1)).add( + Vector(0, 0, (numOfSteps - landingStep) * hstep) + ) + ) # TODO To support DownSlabThickness, UpSlabThickness per flight - self.makeStraightStairs(obj,Part.LineSegment(p3,p4).toShape(), - hstep,obj.UpSlabThickness.Value, - numOfSteps-landingStep, - "HorizontalVerticalCut",None,hgt=h, - vWidth=vWidth,align=align, - vLength=vLength,vHeight=vHeight) + self.makeStraightStairs( + obj, + Part.LineSegment(p3, p4).toShape(), + hstep, + obj.UpSlabThickness.Value, + numOfSteps - landingStep, + "HorizontalVerticalCut", + None, + hgt=h, + vWidth=vWidth, + align=align, + vLength=vLength, + vHeight=vHeight, + ) - self.makeStraightLanding(obj,Part.LineSegment(p2,p3).toShape(), - None,True) # TODO numOfSteps=None, callByMakeStraightStairsWithLanding=True + self.makeStraightLanding( + obj, Part.LineSegment(p2, p3).toShape(), None, True + ) # TODO numOfSteps=None, callByMakeStraightStairsWithLanding=True # TODO To support DownSlabThickness, UpSlabThickness per flight - self.makeStraightStairs(obj,edgeP1p2,obj.DownSlabThickness.Value, - hstep,landingStep,None,'toSlabThickness', - vWidth=vWidth,align=align, - vLength=vLength,vHeight=vHeight) - # Remark: work without h=h, review + self.makeStraightStairs( + obj, + edgeP1p2, + obj.DownSlabThickness.Value, + hstep, + landingStep, + None, + "toSlabThickness", + vWidth=vWidth, + align=align, + vLength=vLength, + vHeight=vHeight, + ) + # Remark: work without h=h, review else: # TODO To support DownSlabThickness, UpSlabThickness per flight - self.makeStraightStairs(obj,edgeP1p2,obj.DownSlabThickness.Value, - obj.UpSlabThickness.Value,landingStep,None, - None,hgt=h,vWidth=vWidth,align=align, - vLength=vLength,vHeight=vHeight) + self.makeStraightStairs( + obj, + edgeP1p2, + obj.DownSlabThickness.Value, + obj.UpSlabThickness.Value, + landingStep, + None, + None, + hgt=h, + vWidth=vWidth, + align=align, + vLength=vLength, + vHeight=vHeight, + ) # TODO To review - outlineNotUsed, outlineRailL, outlineRailR, vBase2, outlineP1P2ClosedNU, outlineP3P4ClosedNU, NU, pArc, pArc1, pArc2 = self.returnOutlines(obj, edgeP1p2, obj.Align, None, obj.Width, - obj.WidthOfLanding, - obj.TreadThickness, obj.RiserHeight, obj.RailingOffsetLeft, - obj.RailingOffsetRight, obj.RailingHeightLeft, - obj.RailingHeightRight,True) + ( + outlineNotUsed, + outlineRailL, + outlineRailR, + vBase2, + outlineP1P2ClosedNU, + outlineP3P4ClosedNU, + NU, + pArc, + pArc1, + pArc2, + ) = self.returnOutlines( + obj, + edgeP1p2, + obj.Align, + None, + obj.Width, + obj.WidthOfLanding, + obj.TreadThickness, + obj.RiserHeight, + obj.RailingOffsetLeft, + obj.RailingOffsetRight, + obj.RailingHeightLeft, + obj.RailingHeightRight, + True, + ) # TODO To review self.connectRailingVector(obj, outlineRailL, outlineRailR, pArc1, pArc2) - def connectRailingVector(self, obj, outlineRailL, outlineRailR, pArcRailL, pArcRailR): - obj.OutlineLeft = outlineRailL # outlineL # outlineP1P2 - obj.OutlineRight = outlineRailR # outlineR # outlineP3P4 + obj.OutlineLeft = outlineRailL # outlineL # outlineP1P2 + obj.OutlineRight = outlineRailR # outlineR # outlineP3P4 - self.OutlineRailArcLeft = pArcRailL #obj.OutlineRailArcLeft = pArcRailL - self.OutlineRailArcRight = pArcRailR #obj.OutlineRailArcRight = pArcRailR + self.OutlineRailArcLeft = pArcRailL # obj.OutlineRailArcLeft = pArcRailL + self.OutlineRailArcRight = pArcRailR # obj.OutlineRailArcRight = pArcRailR - outlineLeftAll, outlineRightAll, outlineRailArcLeftAll, outlineRailArcRightAll = [], [], [], [] + outlineLeftAll, outlineRightAll, outlineRailArcLeftAll, outlineRailArcRightAll = ( + [], + [], + [], + [], + ) outlineRightAll.extend(obj.OutlineRight) outlineRailArcRightAll = self.OutlineRailArcRight @@ -1653,22 +2315,26 @@ class _Stairs(ArchComponent.Component): if obj.LastSegment.OutlineLeftAll: outlineLeftAll.extend(obj.LastSegment.OutlineLeftAll) - if obj.LastSegment.Proxy.OutlineRailArcLeftAll: # need if? + if obj.LastSegment.Proxy.OutlineRailArcLeftAll: # need if? outlineRailArcLeftAll.extend(obj.LastSegment.Proxy.OutlineRailArcLeftAll) - if (outlineLeftAll[-1] - obj.OutlineLeft[0]).Length < 0.01: # To avoid 2 points overlapping fail creating LineSegment # TODO to allow tolerance Part.LineSegment / edge.toShape() allow? + if ( + outlineLeftAll[-1] - obj.OutlineLeft[0] + ).Length < 0.01: # To avoid 2 points overlapping fail creating LineSegment # TODO to allow tolerance Part.LineSegment / edge.toShape() allow? # no need abs() after .Length right? del outlineLeftAll[-1] del outlineRailArcLeftAll[-1] - if (outlineRightAll[-1] - obj.LastSegment.OutlineRightAll[0]).Length < 0.01: # See above + if ( + outlineRightAll[-1] - obj.LastSegment.OutlineRightAll[0] + ).Length < 0.01: # See above del outlineRightAll[-1] del outlineRailArcRightAll[-1] - if obj.LastSegment.OutlineRightAll: # need if? + if obj.LastSegment.OutlineRightAll: # need if? outlineRightAll.extend(obj.LastSegment.OutlineRightAll) - if obj.LastSegment.Proxy.OutlineRailArcRightAll: # need if? + if obj.LastSegment.Proxy.OutlineRailArcRightAll: # need if? outlineRailArcRightAll.extend(obj.LastSegment.Proxy.OutlineRailArcRightAll) outlineLeftAll.extend(obj.OutlineLeft) @@ -1679,31 +2345,29 @@ class _Stairs(ArchComponent.Component): self.OutlineRailArcLeftAll = outlineRailArcLeftAll self.OutlineRailArcRightAll = outlineRailArcRightAll - - def makeCurvedStairs(self,obj,edge): + def makeCurvedStairs(self, obj, edge): print("Not yet implemented!") - def makeCurvedStairsWithLanding(self,obj,edge): + def makeCurvedStairsWithLanding(self, obj, edge): print("Not yet implemented!") class _ViewProviderStairs(ArchComponent.ViewProviderComponent): - "A View Provider for Stairs" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Stairs_Tree.svg" def claimChildren(self): - "Define which objects will appear as children in the tree view" if hasattr(self, "Object"): diff --git a/src/Mod/BIM/ArchStructure.py b/src/Mod/BIM/ArchStructure.py index 5b6123a14c..778f807203 100644 --- a/src/Mod/BIM/ArchStructure.py +++ b/src/Mod/BIM/ArchStructure.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -#Modified 2016-01-03 JAndersM +# Modified 2016-01-03 JAndersM -__title__= "FreeCAD Structure" +__title__ = "FreeCAD Structure" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" @@ -57,32 +57,33 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -#Reads preset profiles and categorizes them -Categories=[] -Presets=ArchProfile.readPresets() +# Reads preset profiles and categorizes them +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 +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.''' + 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") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Structure") _Structure(obj) if FreeCAD.GuiUp: _ViewProviderStructure(obj.ViewObject) @@ -109,13 +110,13 @@ def makeStructure(baseobj=None,length=None,width=None,height=None,name=None): if baseobj: w = 0 h = 0 - if hasattr(baseobj,"Width") and hasattr(baseobj,"Height"): + if hasattr(baseobj, "Width") and hasattr(baseobj, "Height"): w = baseobj.Width.Value h = baseobj.Height.Value - elif hasattr(baseobj,"Length") and hasattr(baseobj,"Width"): + elif hasattr(baseobj, "Length") and hasattr(baseobj, "Width"): w = baseobj.Length.Value h = baseobj.Width.Value - elif hasattr(baseobj,"Length") and hasattr(baseobj,"Height"): + elif hasattr(baseobj, "Length") and hasattr(baseobj, "Height"): w = baseobj.Length.Value h = baseobj.Height.Value if w and h: @@ -128,16 +129,16 @@ def makeStructure(baseobj=None,length=None,width=None,height=None,name=None): if obj.Length > obj.Height: obj.IfcType = "Beam" - obj.Label = name if name else translate("Arch","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") + 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''' +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") @@ -147,13 +148,13 @@ def makeStructuralSystem(objects=[],axes=[],name=None): print("At least one axis must be given") return if objects: - if not isinstance(objects,list): + 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") + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StructuralSystem") + obj.Label = name if name else translate("Arch", "StructuralSystem") _StructuralSystem(obj) if FreeCAD.GuiUp: _ViewProviderStructuralSystem(obj.ViewObject) @@ -163,44 +164,52 @@ def makeStructuralSystem(objects=[],axes=[],name=None): result.append(obj) if FreeCAD.GuiUp and o: o.ViewObject.hide() - Draft.formatObject(obj,o) + Draft.formatObject(obj, o) FreeCAD.ActiveDocument.recompute() if len(result) == 1: return result[0] else: return result -def placeAlongEdge(p1,p2,horizontal=False): +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") + 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) + 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. """ + """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")} + 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") @@ -209,36 +218,65 @@ class CommandStructuresFromSelection: def Activated(self): selex = FreeCADGui.Selection.getSelectionEx() if len(selex) >= 2: - FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Structures From Selection")) + FreeCAD.ActiveDocument.openTransaction( + translate("Arch", "Create Structures From Selection") + ) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") - base = selex[0].Object # The first selected object is the base for the Structure objects - for selexi in selex[1:]: # All the edges from the other objects are used as a Tool (extrusion paths) + base = selex[ + 0 + ].Object # The first selected object is the base for the Structure objects + for selexi in selex[ + 1: + ]: # All the edges from the other objects are used as a Tool (extrusion paths) if len(selexi.SubElementNames) == 0: - subelement_names = ["Edge" + str(i) for i in range(1, len(selexi.Object.Shape.Edges) + 1)] + 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")] + 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 = 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") + 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. """ + """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")} + 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 @@ -249,22 +287,35 @@ class CommandStructuralSystem: st = Draft.getObjectsOfType(sel, "Structure") ax = Draft.getObjectsOfType(sel, "Axis") if ax: - FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Structural System")) + FreeCAD.ActiveDocument.openTransaction( + translate("Arch", "Create Structural System") + ) FreeCADGui.addModule("Arch") if st: - FreeCADGui.doCommand("obj = Arch.makeStructuralSystem(" + ArchCommands.getStringList(st) + ", " + ArchCommands.getStringList(ax) + ")") + FreeCADGui.doCommand( + "obj = Arch.makeStructuralSystem(" + + ArchCommands.getStringList(st) + + ", " + + ArchCommands.getStringList(ax) + + ")" + ) else: - FreeCADGui.doCommand("obj = Arch.makeStructuralSystem(axes = " + ArchCommands.getStringList(ax) + ")") + 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") + FreeCAD.Console.PrintError( + translate("Arch", "Select at least an axis object") + "\n" + ) class _CommandStructure: - "the Arch Structure command definition" def __init__(self): @@ -273,10 +324,15 @@ class _CommandStructure: 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)")} + 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): @@ -299,16 +355,18 @@ class _CommandStructure: self.wp = None sel = FreeCADGui.Selection.getSelection() if sel: - st = Draft.getObjectsOfType(sel,"Structure") - ax = Draft.getObjectsOfType(sel,"Axis") + 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")) + 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.doCommand( + "obj = Arch.makeStructure(FreeCAD.ActiveDocument." + obj.Name + ")" + ) FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() @@ -331,14 +389,18 @@ class _CommandStructure: self.dents = ArchPrecast._DentsTaskPanel() self.precast.Dents = self.dents if self.beammode: - title=translate("Arch","First point of the beam") + 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) + 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): - + 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() @@ -349,20 +411,27 @@ class _CommandStructure: 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") + 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 # determines the type of rotation to apply to the final object - self.doc.openTransaction(translate("Arch","Create Structure")) + horiz = True # determines the type of rotation to apply to the final object + 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) + params.set_param_arch("StructureHeight", self.Length) if self.Profile is not None: - try: # try to update latest precast values - fails if dialog has been destroyed already + try: # try to update latest precast values - fails if dialog has been destroyed already self.precastvalues = self.precast.getValues() except Exception: pass @@ -375,45 +444,69 @@ class _CommandStructure: argstring = "" # fix for precast placement, since their (0,0) point is the lower left corner if self.bmode: - delta = FreeCAD.Vector(0,0-self.Width/2,0) + 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) + 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) # build the string definition for pair in self.precastvalues.items(): argstring += pair[0].lower() + "=" - if isinstance(pair[1],str): + if isinstance(pair[1], str): argstring += '"' + pair[1] + '",' else: argstring += str(pair[1]) + "," FreeCADGui.addModule("ArchPrecast") - FreeCADGui.doCommand("s = ArchPrecast.makePrecast("+argstring+")") + FreeCADGui.doCommand("s = ArchPrecast.makePrecast(" + argstring + ")") else: # metal profile - FreeCADGui.doCommand('p = Arch.makeProfile('+str(self.Profile)+')') - if (abs(self.Length - self.Profile[4]) >= 0.1) or self.bmode: # forgive rounding errors + FreeCADGui.doCommand("p = Arch.makeProfile(" + str(self.Profile) + ")") + if ( + abs(self.Length - self.Profile[4]) >= 0.1 + ) or self.bmode: # forgive rounding errors # horizontal - FreeCADGui.doCommand('s = Arch.makeStructure(p,length='+str(self.Length)+')') + FreeCADGui.doCommand( + "s = Arch.makeStructure(p,length=" + str(self.Length) + ")" + ) horiz = False else: # vertical - FreeCADGui.doCommand('s = Arch.makeStructure(p,height='+str(self.Height)+')') - #if not self.bmode: + FreeCADGui.doCommand( + "s = Arch.makeStructure(p,height=" + str(self.Height) + ")" + ) + # if not self.bmode: # FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)') - 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)+')') + 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) + + ")" + ) # calculate rotation if self.bmode and self.bpoint: - FreeCADGui.doCommand('s.Placement = Arch.placeAlongEdge('+DraftVecUtils.toString(self.bpoint)+","+DraftVecUtils.toString(point)+","+str(horiz)+")") + 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.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)") @@ -425,98 +518,108 @@ class _CommandStructure: self.Activated() def _createItemlist(self, baselist): - "create nice labels for presets in the task panel" - ilist=[] + 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)) + 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+")") + 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")) + w.setWindowTitle(translate("Arch", "Structure options")) grid = QtGui.QGridLayout(w) # mode box - labelmode = QtGui.QLabel(translate("Arch","Parameters of the structure")+":") - self.modeb = QtGui.QRadioButton(translate("Arch","Beam")) - self.modec = QtGui.QRadioButton(translate("Arch","Column")) + 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) + grid.addWidget(labelmode, 0, 0, 1, 2) + grid.addWidget(self.modeb, 1, 0, 1, 1) + grid.addWidget(self.modec, 1, 1, 1, 1) # categories box - labelc = QtGui.QLabel(translate("Arch","Category")) + 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) + self.valuec.addItems([" ", "Precast concrete"] + Categories) + grid.addWidget(labelc, 2, 0, 1, 1) + grid.addWidget(self.valuec, 2, 1, 1, 1) # presets box - labelp = QtGui.QLabel(translate("Arch","Preset")) + 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) + grid.addWidget(labelp, 3, 0, 1, 1) + grid.addWidget(self.vPresets, 3, 1, 1, 1) # length - label1 = QtGui.QLabel(translate("Arch","Length")) + 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) + 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) + 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) # width - label2 = QtGui.QLabel(translate("Arch","Width")) + 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) + 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) # height - label3 = QtGui.QLabel(translate("Arch","Height")) + 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) + 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) + 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) # horizontal button - 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) + 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) # connect slots - 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) + 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) # restore preset stored = params.get_param_arch("StructurePreset") @@ -530,70 +633,69 @@ class _CommandStructure: stored = stored.split(";") if len(stored) >= 3: if stored[1] in Categories: - self.valuec.setCurrentIndex(2+Categories.index(stored[1])) + 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): - + def update(self, point, info): "this function is called by the Snapper when the mouse is moved" if FreeCADGui.Control.activeDialog(): - try: # try to update latest precast values - fails if dialog has been destroyed already + try: # try to update latest precast values - fails if dialog has been destroyed already self.precastvalues = self.precast.getValues() except Exception: pass if self.Height >= self.Length: - delta = Vector(0,0,self.Height/2) + 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) + 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)]) + 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) + self.vLength.setText(FreeCAD.Units.Quantity(l, FreeCAD.Units.Length).UserString) else: self.tracker.off() - def setWidth(self,d): + def setWidth(self, d): self.Width = d self.tracker.width(d) - params.set_param_arch("StructureWidth",d) + params.set_param_arch("StructureWidth", d) - def setHeight(self,d): + def setHeight(self, d): self.Height = d self.tracker.height(d) if self.modeb.isChecked(): - params.set_param_arch("StructureLength",d) + params.set_param_arch("StructureLength", d) else: - params.set_param_arch("StructureHeight",d) + params.set_param_arch("StructureHeight", d) - def setLength(self,d): + def setLength(self, d): self.Length = d self.tracker.length(d) if self.modeb.isChecked(): - params.set_param_arch("StructureHeight",d) + params.set_param_arch("StructureHeight", d) else: - params.set_param_arch("StructureLength",d) + params.set_param_arch("StructureLength", d) - def setCategory(self,i): + 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]] + 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) @@ -608,9 +710,9 @@ class _CommandStructure: self.pSelect = [None] fpresets = [" "] self.vPresets.addItems(fpresets) - params.set_param_arch("StructurePreset","") + params.set_param_arch("StructurePreset", "") - def setPreset(self,i): + def setPreset(self, i): self.Profile = None elt = self.pSelect[i] @@ -618,18 +720,22 @@ class _CommandStructure: if elt in self.precast.PrecastTypes: self.precast.setPreset(elt) self.Profile = "Precast_" + elt - if elt in ["Pillar","Beam"]: + if elt in ["Pillar", "Beam"]: self.dents.form.show() else: self.dents.form.hide() - params.set_param_arch("StructurePreset",self.Profile) + 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.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])) + params.set_param_arch("StructurePreset", ";".join([str(i) for i in self.Profile])) - def switchLH(self,bmode): + def switchLH(self, bmode): if bmode: self.bmode = True @@ -657,120 +763,295 @@ class _CommandStructure: class _Structure(ArchComponent.Component): - "The Structure object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Structure" self.setProperties(obj) obj.IfcType = "Beam" - def setProperties(self,obj): + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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"] + 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.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: # PropertyStringList - 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.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: # test if the property was added but as IntegerList, then update; - type = obj.getTypeIdOfProperty('ArchSketchEdges') + 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.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"): + 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): # Supercede Arch.Component.dumps() dump = super().dumps() if not isinstance(dump, tuple): - dump = (dump,) #Python Tuple With One Item + dump = (dump,) # Python Tuple With One Item dump = dump + (self.ArchSkPropSetPickedUuid, self.ArchSkPropSetListPrev) return dump - - def loads(self,state): + def loads(self, state): self.Type = "Structure" if state == None: return - elif state[0] == 'S': # state[1] == 't', behaviour before 2024.11.28 + elif state[0] == "S": # state[1] == 't', behaviour before 2024.11.28 return - elif state[0] == 'Structure': + elif state[0] == "Structure": self.ArchSkPropSetPickedUuid = state[1] self.ArchSkPropSetListPrev = state[2] - elif state[0] != 'Structure': # model before merging super.dumps/loads() + elif state[0] != "Structure": # model before merging super.dumps/loads() self.ArchSkPropSetPickedUuid = state[0] self.ArchSkPropSetListPrev = state[1] + def onDocumentRestored(self, obj): - def onDocumentRestored(self,obj): - - ArchComponent.Component.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"): + 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"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", 0) else: - if hasattr(obj,"ArchSketchEdges"): + if hasattr(obj, "ArchSketchEdges"): obj.setEditorMode("ArchSketchEdges", 0) - if hasattr(obj,"ArchSketchPropertySet"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) # set a flag to indicate onDocumentRestored() is run - - def execute(self,obj): - + def execute(self, obj): "creates the structure shape" import Part @@ -792,19 +1073,20 @@ class _Structure(ArchComponent.Component): propSetListCur = None if Draft.getType(obj.Base) == "ArchSketch": baseProxy = obj.Base.Proxy - if hasattr(baseProxy,"getPropertySet"): + if hasattr(baseProxy, "getPropertySet"): # get full list of PropertySet propSetListCur = baseProxy.getPropertySet(obj.Base) # get updated name (if any) of the selected PropertySet - propSetSelectedNameCur = baseProxy.getPropertySet(obj.Base, - propSetUuid=propSetPickedUuidPrev) + propSetSelectedNameCur = baseProxy.getPropertySet( + obj.Base, propSetUuid=propSetPickedUuidPrev + ) if propSetSelectedNameCur: # True if selection is not deleted if propSetListPrev != propSetListCur: obj.ArchSketchPropertySet = propSetListCur obj.ArchSketchPropertySet = propSetSelectedNameCur self.ArchSkPropSetListPrev = propSetListCur - #elif propSetListPrev == propSetListCur: - #pass #nothing to do in this case + # elif propSetListPrev == propSetListCur: + # pass #nothing to do in this case # but if below, though (propSetListPrev == propSetListCur) elif propSetSelectedNamePrev != propSetSelectedNameCur: obj.ArchSketchPropertySet = propSetSelectedNameCur @@ -812,21 +1094,21 @@ class _Structure(ArchComponent.Component): if propSetListCur: if propSetListPrev != propSetListCur: obj.ArchSketchPropertySet = propSetListCur - obj.ArchSketchPropertySet = 'Default' - #else: # Seems no need ... - #obj.PropertySet = 'Default' + obj.ArchSketchPropertySet = "Default" + # else: # Seems no need ... + # obj.PropertySet = 'Default' extdata = self.getExtrusionData(obj) if extdata: sh = extdata[0] - if not isinstance(sh,list): + if not isinstance(sh, list): sh = [sh] ev = extdata[1] - if not isinstance(ev,list): + if not isinstance(ev, list): ev = [ev] pla = extdata[2] - if not isinstance(pla,list): + if not isinstance(pla, list): pla = [pla] base = [] extrusion_length = 0.0 @@ -852,7 +1134,13 @@ class _Structure(ArchComponent.Component): 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") + 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 @@ -862,7 +1150,7 @@ class _Structure(ArchComponent.Component): base = Part.makeCompound(base) obj.ComputedLength = FreeCAD.Units.Quantity(extrusion_length, FreeCAD.Units.Length) if obj.Base: - if hasattr(obj.Base,'Shape'): + if hasattr(obj.Base, "Shape"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.isValid(): @@ -878,29 +1166,32 @@ class _Structure(ArchComponent.Component): 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") + FreeCAD.Console.PrintWarning( + translate("Arch", "This mesh is an invalid solid") + "\n" + ) obj.Base.ViewObject.show() if (not base) and (not obj.Additions): - #FreeCAD.Console.PrintError(translate("Arch","Error: Invalid base object")+"\n") + # FreeCAD.Console.PrintError(translate("Arch","Error: Invalid base object")+"\n") return - base = self.processSubShapes(obj,base,pl) - self.applyShape(obj,base,pl) + base = self.processSubShapes(obj, base, pl) + self.applyShape(obj, base, pl) - def getExtrusionData(self,obj): + def getExtrusionData(self, obj): """returns (shape,extrusion vector or path,placement) or None""" - if hasattr(obj,"IfcType"): + if hasattr(obj, "IfcType"): IfcType = obj.IfcType else: IfcType = None import Part import DraftGeomUtils - data = ArchComponent.Component.getExtrusionData(self,obj) + + data = ArchComponent.Component.getExtrusionData(self, obj) if data: - if not isinstance(data[0],list): + if not isinstance(data[0], list): # multifuses not considered here return data - length = obj.Length.Value + length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: @@ -909,12 +1200,12 @@ class _Structure(ArchComponent.Component): extrusion = None normal = None if obj.Base: - if hasattr(obj.Base,'Shape'): + 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): + if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces, tol=0.01): return None else: baseface = obj.Base.Shape.copy() @@ -923,38 +1214,55 @@ class _Structure(ArchComponent.Component): # Get base shape wires, and faceMaker, for Structure (slab. etc.) from Base Objects if they store and provide by getStructureBaseShapeWires() # (thickness, normal/extrusion, length, width, baseface maybe for later) of structure (slab etc.) structureBaseShapeWires = [] - baseShapeWires = [] #baseSlabWires / baseSlabOpeningWires = None + baseShapeWires = [] # baseSlabWires / baseSlabOpeningWires = None faceMaker = None - if hasattr(obj.Base, 'Proxy') and obj.ArchSketchData and \ - hasattr(obj.Base.Proxy, 'getStructureBaseShapeWires'): + if ( + hasattr(obj.Base, "Proxy") + and obj.ArchSketchData + and hasattr(obj.Base.Proxy, "getStructureBaseShapeWires") + ): propSetUuid = self.ArchSkPropSetPickedUuid # provide selected edges, or groups, in obj.ArchSketchEdges for processing in getStructureBaseShapeWires() (getSortedClusters) as override - structureBaseShapeWires = obj.Base.Proxy.getStructureBaseShapeWires(obj.Base, - propSetUuid=propSetUuid) + structureBaseShapeWires = obj.Base.Proxy.getStructureBaseShapeWires( + obj.Base, propSetUuid=propSetUuid + ) # get slab wires; use original wires if structureBaseShapeWires() provided none - if structureBaseShapeWires: # would be false (none) if both base ArchSketch and obj do not have the edges stored / inputted by user + if ( + structureBaseShapeWires + ): # would be false (none) if both base ArchSketch and obj do not have the edges stored / inputted by user # if structureBaseShapeWires is {dict} - baseShapeWires = structureBaseShapeWires.get('slabWires') - faceMaker = structureBaseShapeWires.get('faceMaker') + baseShapeWires = structureBaseShapeWires.get("slabWires") + faceMaker = structureBaseShapeWires.get("faceMaker") elif obj.Base.isDerivedFrom("Sketcher::SketchObject"): skGeom = obj.Base.GeometryFacadeList skGeomEdges = [] - skPlacement = obj.Base.Placement # Get Sketch's placement to restore later + skPlacement = ( + obj.Base.Placement + ) # Get Sketch's placement to restore later # Get ArchSketch edges to construct ArchStructure # No need to test obj.ArchSketchData ... - for ig, geom in enumerate(skGeom): + for ig, geom in enumerate(skGeom): # Construction mode edges should be ignored if # ArchSketchEdges, otherwise, ArchSketchEdges data # needs to take out those in Construction before # using as parameters. - if (not obj.ArchSketchEdges and not geom.Construction) or str(ig) in obj.ArchSketchEdges: + if (not obj.ArchSketchEdges and not geom.Construction) or str( + ig + ) in obj.ArchSketchEdges: # support Line, Arc, Circle, Ellipse, BSplineCurve for Sketch # as Base at the moment - if isinstance(geom.Geometry, (Part.LineSegment, - Part.Circle, Part.ArcOfCircle, - Part.Ellipse, Part.BSplineCurve)): + if isinstance( + geom.Geometry, + ( + Part.LineSegment, + Part.Circle, + Part.ArcOfCircle, + Part.Ellipse, + Part.BSplineCurve, + ), + ): skGeomEdgesI = geom.Geometry.toShape() skGeomEdges.append(skGeomEdgesI) clusterTransformed = [] @@ -973,9 +1281,13 @@ class _Structure(ArchComponent.Component): if not faceMaker: faceMaker = obj.FaceMaker try: - baseface = Part.makeFace(baseShapeWires,"Part::FaceMaker"+str(faceMaker)) + baseface = Part.makeFace( + baseShapeWires, "Part::FaceMaker" + str(faceMaker) + ) except Exception: - FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Facemaker returned an error") + "\n" + ) # Not returning even Part.makeFace fails, fall back to 'non-Part.makeFace' method if not baseface: for w in baseShapeWires: @@ -983,9 +1295,9 @@ class _Structure(ArchComponent.Component): p0 = w.OrderedVertexes[0].Point p1 = w.OrderedVertexes[-1].Point if p0 != p1: - e = Part.LineSegment(p0,p1).toShape() + e = Part.LineSegment(p0, p1).toShape() w.add(e) - w.fix(0.1,0,1) # fixes self-intersecting wires + w.fix(0.1, 0, 1) # fixes self-intersecting wires f = Part.Face(w) # check if it is 1st face (f) created from w in baseShapeWires; if not, fuse() if baseface: @@ -995,21 +1307,22 @@ class _Structure(ArchComponent.Component): baseface = f.copy() elif length and width and height: if (length > height) and (IfcType in ["Beam", "Column"]): - h2 = height/2 or 0.5 - w2 = width/2 or 0.5 - v1 = Vector(0,-w2,-h2) - v4 = Vector(0,-w2,h2) - v3 = Vector(0,w2,h2) - v2 = Vector(0,w2,-h2) + h2 = height / 2 or 0.5 + w2 = width / 2 or 0.5 + v1 = Vector(0, -w2, -h2) + v4 = Vector(0, -w2, h2) + v3 = Vector(0, w2, h2) + v2 = 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) + 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])) + + baseface = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) if baseface: if hasattr(obj, "Tool") and obj.Tool: tool = obj.Tool @@ -1024,22 +1337,30 @@ class _Structure(ArchComponent.Component): 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 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) + 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) + 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) # TODO to use ArchSketch's 'normal' for consistency + normal = baseface.Faces[0].normalAt( + 0, 0 + ) # TODO to use ArchSketch's 'normal' for consistency base = None placement = None inverse_placement = None @@ -1056,16 +1377,19 @@ 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": + 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) + normal = Vector(0, 0, 1) if not normal.Length: - normal = Vector(0,0,1) + normal = Vector(0, 0, 1) extrusion = normal if (length > height) and (IfcType in ["Beam", "Column"]): if length: @@ -1077,7 +1401,7 @@ class _Structure(ArchComponent.Component): return (base, extrusion, placement) return None - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): # check the flag indicating if we are currently in the process of # restoring document; if not, no further code is run as getExtrusionData() @@ -1085,31 +1409,34 @@ class _Structure(ArchComponent.Component): if FreeCAD.ActiveDocument.Restoring: return - if hasattr(obj,"IfcType"): + if hasattr(obj, "IfcType"): IfcType = obj.IfcType else: IfcType = None - self.hideSubobjects(obj,prop) - if prop in ["Shape","ResetNodes","NodesOffset"]: + self.hideSubobjects(obj, prop) + if prop in ["Shape", "ResetNodes", "NodesOffset"]: # ResetNodes is not a property but it allows us to use this function to force reset the nodes nodes = None extdata = self.getExtrusionData(obj) - if extdata and not isinstance(extdata[0],list): + 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"): + if hasattr(nodes, "CenterOfMass"): import Part - nodes = Part.LineSegment(nodes.CenterOfMass,nodes.CenterOfMass.add(extdata[1])).toShape() + + 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 hasattr(obj, "NodesOffset"): + offset = FreeCAD.Vector(0, 0, obj.NodesOffset.Value) if obj.Nodes and (prop != "ResetNodes"): - if hasattr(self,"nodes"): + if hasattr(self, "nodes"): if self.nodes: if obj.Nodes != self.nodes: # nodes are set manually: don't touch them @@ -1124,50 +1451,58 @@ class _Structure(ArchComponent.Component): if nodes: self.nodes = [v.Point.add(offset) for v in nodes.Vertexes] obj.Nodes = self.nodes - ArchComponent.Component.onChanged(self,obj,prop) + ArchComponent.Component.onChanged(self, obj, prop) - if (prop == "ArchSketchPropertySet" - and Draft.getType(obj.Base) == "ArchSketch"): + if prop == "ArchSketchPropertySet" and Draft.getType(obj.Base) == "ArchSketch": baseProxy = obj.Base.Proxy - if hasattr(baseProxy,"getPropertySet"): - uuid = baseProxy.getPropertySet(obj, - propSetName=obj.ArchSketchPropertySet) + 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"): + 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"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", 0) else: - if hasattr(obj,"ArchSketchEdges"): + if hasattr(obj, "ArchSketchEdges"): obj.setEditorMode("ArchSketchEdges", 0) - if hasattr(obj,"ArchSketchPropertySet"): + if hasattr(obj, "ArchSketchPropertySet"): obj.setEditorMode("ArchSketchPropertySet", ["ReadOnly"]) - - def getNodeEdges(self,obj): - + 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"): + + 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()) + 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): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) # setProperties of ArchComponent will be overwritten # thus setProperties from ArchComponent will be explicit called to get the properties @@ -1176,59 +1511,94 @@ class _ViewProviderStructure(ArchComponent.ViewProviderComponent): self.setProperties(vobj) vobj.ShapeColor = ArchCommands.getDefaultColor("Structure") - def setProperties(self,vobj): + 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 + 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) + 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.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) + 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"] + 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): + def onDocumentRestored(self, vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + + 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): + def updateData(self, obj, prop): if prop == "Nodes": if obj.Nodes: - if hasattr(self,"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) + 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"): + 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]) + 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"): + if hasattr(obj.ViewObject, "NodeType"): + if hasattr(obj, "IfcType"): IfcType = obj.IfcType else: IfcType = None @@ -1237,16 +1607,17 @@ class _ViewProviderStructure(ArchComponent.ViewProviderComponent): else: obj.ViewObject.NodeType = "Linear" else: - ArchComponent.ViewProviderComponent.updateData(self,obj,prop) + ArchComponent.ViewProviderComponent.updateData(self, obj, prop) - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): if prop == "ShowNodes": - if hasattr(self,"nodes"): + 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() @@ -1274,32 +1645,32 @@ class _ViewProviderStructure(ArchComponent.ViewProviderComponent): 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") + self.updateData(vobj.Object, "Nodes") + self.onChanged(vobj, "NodeColor") + self.onChanged(vobj, "NodeLine") + self.onChanged(vobj, "NodeSize") elif prop == "NodeColor": - if hasattr(self,"mat"): + 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]]) + 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"): + if hasattr(self, "linestyle"): self.linestyle.lineWidth = vobj.NodeLine elif prop == "NodeSize": - if hasattr(self,"pointstyle"): + if hasattr(self, "pointstyle"): self.pointstyle.pointSize = vobj.NodeSize elif prop == "NodeType": - self.updateData(vobj.Object,"Nodes") + self.updateData(vobj.Object, "Nodes") else: - ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) + ArchComponent.ViewProviderComponent.onChanged(self, vobj, prop) - def setEdit(self,vobj,mode): + def setEdit(self, vobj, mode): if mode != 0: return None @@ -1312,7 +1683,7 @@ class _ViewProviderStructure(ArchComponent.ViewProviderComponent): class StructureTaskPanel(ArchComponent.ComponentTaskPanel): - def __init__(self,obj): + def __init__(self, obj): ArchComponent.ComponentTaskPanel.__init__(self) self.nodes_widget = QtGui.QWidget() @@ -1335,34 +1706,56 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): 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)) + 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)) + 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)) + 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)) + 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)) + 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) + QtCore.QObject.connect( + self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool + ) self.form = [self.form, self.nodes_widget, self.extrusion_widget] self.Object = obj @@ -1376,75 +1769,119 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): def resetNodes(self): - self.Object.Proxy.onChanged(self.Object,"ResetNodes") + self.Object.Proxy.onChanged(self.Object, "ResetNodes") - def extendNodes(self,other=None): + 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:")) + 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") + 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") + 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") + 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) + 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") + 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]] + 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)] + self.Object.Nodes = [ + self.Object.Nodes[0], + self.Object.Placement.inverse().multVec(intersect), + ] - def connectNodes(self,other=None): + 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:")) + 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") + 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") + 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") + 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) + 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") + 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]] + 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]] + 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)] + other.Nodes = [ + other.Nodes[0], + other.Placement.inverse().multVec(intersect), + ] def toggleNodes(self): @@ -1455,8 +1892,8 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): else: self.nodevis = [] for obj in FreeCAD.ActiveDocument.Objects: - if hasattr(obj.ViewObject,"ShowNodes"): - self.nodevis.append([obj,obj.ViewObject.ShowNodes]) + if hasattr(obj.ViewObject, "ShowNodes"): + self.nodevis.append([obj, obj.ViewObject.ShowNodes]) obj.ViewObject.ShowNodes = True def setSelectionFromTool(self): @@ -1470,8 +1907,12 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): 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) + 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): @@ -1482,17 +1923,31 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): # Add entirely selected objects objectList.append(selExi.Object) else: - subElementsNames = [subElementName for subElementName in selExi.SubElementNames if subElementName.startswith("Edge")] + 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"), locked=True) + 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) + 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): @@ -1508,33 +1963,55 @@ class StructureTaskPanel(ArchComponent.ComponentTaskPanel): class StructSelectionObserver: - def __init__(self,callback): + def __init__(self, callback): self.callback = callback def addSelection(self, docName, objName, sub, pos): - print("got ",objName) + print("got ", objName) obj = FreeCAD.getDocument(docName).getObject(objName) self.callback(obj) -class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects can now be based on axes - +class _StructuralSystem( + ArchComponent.Component +): # OBSOLETE - All Arch objects can now be based on axes "The Structural System object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__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 + 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): + def loads(self, state): self.Type = "StructuralSystem" - def execute(self,obj): + def execute(self, obj): "creates the structure shape" import Part @@ -1543,7 +2020,7 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects # creating base shape pl = obj.Placement if obj.Base: - if hasattr(obj.Base,'Shape'): + if hasattr(obj.Base, "Shape"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.Solids: @@ -1553,19 +2030,19 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects # applying axes pts = self.getAxisPoints(obj) - if hasattr(obj,"Align"): - if obj.Align == False : + if hasattr(obj, "Align"): + if obj.Align == False: apl = self.getAxisPlacement(obj) if obj.Align: apl = None - else : + else: apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): sh = obj.Base.Shape.copy() - if hasattr(obj,"Exclude"): + if hasattr(obj, "Exclude"): if i in obj.Exclude: continue if apl: @@ -1575,7 +2052,7 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects if fsh: base = Part.makeCompound(fsh) - base = self.processSubShapes(obj,base,pl) + base = self.processSubShapes(obj, base, pl) if base: if not base.isNull(): @@ -1583,19 +2060,22 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects if base.Volume < 0: base.reverse() if base.Volume < 0: - FreeCAD.Console.PrintError(translate("Arch","Could not compute a shape")) + 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): + def getAxisPoints(self, obj): "returns the gridpoints of linked axes" import DraftGeomUtils + pts = [] if len(obj.Axes) == 1: - if hasattr(obj,"Align"): + 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: @@ -1607,16 +2087,16 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects pts.append(e.Vertexes[0].Point) else: for e in obj.Axes[0].Shape.Edges: - pts.append(e.Vertexes[0].Point) + 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)) + pts.extend(DraftGeomUtils.findIntersection(e1, e2)) return pts - def getAxisPlacement(self,obj): + def getAxisPlacement(self, obj): "returns an axis placement" if obj.Axes: return obj.Axes[0].Placement @@ -1624,12 +2104,12 @@ class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects class _ViewProviderStructuralSystem(ArchComponent.ViewProviderComponent): - "A View Provider for the Structural System object" def getIcon(self): import Arch_rc + return ":/icons/Arch_StructuralSystem_Tree.svg" @@ -1642,10 +2122,13 @@ if FreeCAD.GuiUp: 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") - } + 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 diff --git a/src/Mod/BIM/ArchTruss.py b/src/Mod/BIM/ArchTruss.py index b6f8403603..e41625c2f0 100644 --- a/src/Mod/BIM/ArchTruss.py +++ b/src/Mod/BIM/ArchTruss.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Arch Truss" +__title__ = "FreeCAD Arch Truss" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchTruss # \ingroup ARCH @@ -43,104 +43,185 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond -rodmodes = ("/|/|/|", - "/\\/\\/\\", - "/|\\|/|\\", - ) +rodmodes = ( + "/|/|/|", + "/\\/\\/\\", + "/|\\|/|\\", +) class Truss(ArchComponent.Component): - "The truss object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Truss" self.setProperties(obj) obj.IfcType = "Beam" - def setProperties(self,obj): + def setProperties(self, obj): pl = obj.PropertiesList if not "TrussAngle" in pl: - obj.addProperty("App::PropertyAngle","TrussAngle","Truss", - QT_TRANSLATE_NOOP("App::Property","The angle of the truss"), locked=True) - obj.setEditorMode("TrussAngle",1) + obj.addProperty( + "App::PropertyAngle", + "TrussAngle", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The angle of the truss"), + locked=True, + ) + obj.setEditorMode("TrussAngle", 1) if not "SlantType" in pl: - obj.addProperty("App::PropertyEnumeration","SlantType","Truss", - QT_TRANSLATE_NOOP("App::Property","The slant type of this truss"), locked=True) - obj.SlantType = ["Simple","Double"] + obj.addProperty( + "App::PropertyEnumeration", + "SlantType", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The slant type of this truss"), + locked=True, + ) + obj.SlantType = ["Simple", "Double"] if not "Normal" in pl: - obj.addProperty("App::PropertyVector","Normal","Truss", - QT_TRANSLATE_NOOP("App::Property","The normal direction of this truss"), locked=True) - obj.Normal = FreeCAD.Vector(0,0,1) + obj.addProperty( + "App::PropertyVector", + "Normal", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The normal direction of this truss"), + locked=True, + ) + obj.Normal = FreeCAD.Vector(0, 0, 1) if not "HeightStart" in pl: - obj.addProperty("App::PropertyLength","HeightStart","Truss", - QT_TRANSLATE_NOOP("App::Property","The height of the truss at the start position"), locked=True) + obj.addProperty( + "App::PropertyLength", + "HeightStart", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The height of the truss at the start position"), + locked=True, + ) obj.HeightStart = 100 if not "HeightEnd" in pl: - obj.addProperty("App::PropertyLength","HeightEnd","Truss", - QT_TRANSLATE_NOOP("App::Property","The height of the truss at the end position"), locked=True) + obj.addProperty( + "App::PropertyLength", + "HeightEnd", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The height of the truss at the end position"), + locked=True, + ) obj.HeightEnd = 200 if not "StrutStartOffset" in pl: - obj.addProperty("App::PropertyDistance","StrutStartOffset","Truss", - QT_TRANSLATE_NOOP("App::Property","An optional start offset for the top strut"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "StrutStartOffset", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "An optional start offset for the top strut"), + locked=True, + ) if not "StrutEndOffset" in pl: - obj.addProperty("App::PropertyDistance","StrutEndOffset","Truss", - QT_TRANSLATE_NOOP("App::Property","An optional end offset for the top strut"), locked=True) + obj.addProperty( + "App::PropertyDistance", + "StrutEndOffset", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "An optional end offset for the top strut"), + locked=True, + ) if not "StrutHeight" in pl: - obj.addProperty("App::PropertyLength","StrutHeight","Truss", - QT_TRANSLATE_NOOP("App::Property","The height of the main top and bottom elements of the truss"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StrutHeight", + "Truss", + QT_TRANSLATE_NOOP( + "App::Property", "The height of the main top and bottom elements of the truss" + ), + locked=True, + ) obj.StrutHeight = 10 if not "StrutWidth" in pl: - obj.addProperty("App::PropertyLength","StrutWidth","Truss", - QT_TRANSLATE_NOOP("App::Property","The width of the main top and bottom elements of the truss"), locked=True) + obj.addProperty( + "App::PropertyLength", + "StrutWidth", + "Truss", + QT_TRANSLATE_NOOP( + "App::Property", "The width of the main top and bottom elements of the truss" + ), + locked=True, + ) obj.StrutWidth = 5 if not "RodType" in pl: - obj.addProperty("App::PropertyEnumeration","RodType","Truss", - QT_TRANSLATE_NOOP("App::Property","The type of the middle element of the truss"), locked=True) - obj.RodType = ["Round","Square"] + obj.addProperty( + "App::PropertyEnumeration", + "RodType", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The type of the middle element of the truss"), + locked=True, + ) + obj.RodType = ["Round", "Square"] if not "RodDirection" in pl: - obj.addProperty("App::PropertyEnumeration","RodDirection","Truss", - QT_TRANSLATE_NOOP("App::Property","The direction of the rods"), locked=True) - obj.RodDirection = ["Forward","Backward"] + obj.addProperty( + "App::PropertyEnumeration", + "RodDirection", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The direction of the rods"), + locked=True, + ) + obj.RodDirection = ["Forward", "Backward"] if not "RodSize" in pl: - obj.addProperty("App::PropertyLength","RodSize","Truss", - QT_TRANSLATE_NOOP("App::Property","The diameter or side of the rods"), locked=True) + obj.addProperty( + "App::PropertyLength", + "RodSize", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The diameter or side of the rods"), + locked=True, + ) obj.RodSize = 2 if not "RodSections" in pl: - obj.addProperty("App::PropertyInteger","RodSections","Truss", - QT_TRANSLATE_NOOP("App::Property","The number of rod sections"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "RodSections", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "The number of rod sections"), + locked=True, + ) obj.RodSections = 3 if not "RodEnd" in pl: - obj.addProperty("App::PropertyBool","RodEnd","Truss", - QT_TRANSLATE_NOOP("App::Property","If the truss has a rod at its endpoint or not"), locked=True) + obj.addProperty( + "App::PropertyBool", + "RodEnd", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "If the truss has a rod at its endpoint or not"), + locked=True, + ) if not "RodMode" in pl: - obj.addProperty("App::PropertyEnumeration","RodMode","Truss", - QT_TRANSLATE_NOOP("App::Property","How to draw the rods"), locked=True) + obj.addProperty( + "App::PropertyEnumeration", + "RodMode", + "Truss", + QT_TRANSLATE_NOOP("App::Property", "How to draw the rods"), + locked=True, + ) obj.RodMode = rodmodes - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) + ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) - def loads(self,state): + def loads(self, state): self.Type = "Truss" - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): - ArchComponent.Component.onChanged(self,obj,prop) + ArchComponent.Component.onChanged(self, obj, prop) - def execute(self,obj): + def execute(self, obj): if self.clone(obj): return @@ -153,19 +234,19 @@ class Truss(ArchComponent.Component): # test properties if not obj.StrutWidth.Value or not obj.StrutHeight.Value: - FreeCAD.Console.PrintLog(obj.Label+": Invalid strut size\n") + FreeCAD.Console.PrintLog(obj.Label + ": Invalid strut size\n") return if not obj.HeightStart.Value or not obj.HeightEnd.Value: - FreeCAD.Console.PrintLog(obj.Label+": Invalid truss heights\n") + FreeCAD.Console.PrintLog(obj.Label + ": Invalid truss heights\n") return if not obj.RodSections or not obj.RodSize.Value: - FreeCAD.Console.PrintLog(obj.Label+": Invalid rod config\n") + FreeCAD.Console.PrintLog(obj.Label + ": Invalid rod config\n") return if not obj.Base: - FreeCAD.Console.PrintLog(obj.Label+": No base\n") + FreeCAD.Console.PrintLog(obj.Label + ": No base\n") return - if (not hasattr(obj.Base,"Shape")) or (len(obj.Base.Shape.Vertexes) > 2): - FreeCAD.Console.PrintLog(obj.Label+": No base edge\n") + if (not hasattr(obj.Base, "Shape")) or (len(obj.Base.Shape.Vertexes) > 2): + FreeCAD.Console.PrintLog(obj.Label + ": No base edge\n") return # baseline @@ -173,11 +254,11 @@ class Truss(ArchComponent.Component): v1 = obj.Base.Shape.Vertexes[-1].Point if obj.SlantType == "Simple": - topstrut,bottomstrut,rods,angle = self.makeTruss(obj,v0,v1) + topstrut, bottomstrut, rods, angle = self.makeTruss(obj, v0, v1) else: v3 = v0.add((v1.sub(v0)).multiply(0.5)) - topstrut1,bottomstrut1,rods1,angle = self.makeTruss(obj,v0,v3) - topstrut2,bottomstrut2,rods2,angle = self.makeTruss(obj,v1,v3) + topstrut1, bottomstrut1, rods1, angle = self.makeTruss(obj, v0, v3) + topstrut2, bottomstrut2, rods2, angle = self.makeTruss(obj, v1, v3) topstrut = topstrut1.fuse(topstrut2) topstrut = topstrut.removeSplitter() bottomstrut = bottomstrut1.fuse(bottomstrut2) @@ -187,32 +268,32 @@ class Truss(ArchComponent.Component): rods = rods1 + rods2 # mount shape - shape = Part.makeCompound([topstrut,bottomstrut]+rods) - shape = self.processSubShapes(obj,shape,pl) - self.applyShape(obj,shape,pl) + shape = Part.makeCompound([topstrut, bottomstrut] + rods) + shape = self.processSubShapes(obj, shape, pl) + self.applyShape(obj, shape, pl) obj.TrussAngle = angle - def makeTruss(self,obj,v0,v1): + def makeTruss(self, obj, v0, v1): import Part # get normal direction normal = obj.Normal if not normal.Length: - normal = FreeCAD.Vector(0,0,1) + normal = FreeCAD.Vector(0, 0, 1) # create base profile maindir = v1.sub(v0) sidedir = normal.cross(maindir) if not sidedir.Length: - FreeCAD.Console.PrintLog(obj.Label+": normal and base are parallel\n") + FreeCAD.Console.PrintLog(obj.Label + ": normal and base are parallel\n") return sidedir.normalize() - p0 = v0.add(FreeCAD.Vector(sidedir).negative().multiply(obj.StrutWidth.Value/2)) - p1 = p0.add(FreeCAD.Vector(sidedir).multiply(obj.StrutWidth.Value/2).multiply(2)) + p0 = v0.add(FreeCAD.Vector(sidedir).negative().multiply(obj.StrutWidth.Value / 2)) + p1 = p0.add(FreeCAD.Vector(sidedir).multiply(obj.StrutWidth.Value / 2).multiply(2)) p2 = p1.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight)) p3 = p0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight)) - trussprofile = Part.Face(Part.makePolygon([p0,p1,p2,p3,p0])) + trussprofile = Part.Face(Part.makePolygon([p0, p1, p2, p3, p0])) # create bottom strut bottomstrut = trussprofile.extrude(maindir) @@ -237,101 +318,103 @@ class Truss(ArchComponent.Component): # create rod profile on the XY plane if obj.RodType == "Round": - rodprofile = Part.Face(Part.Wire([Part.makeCircle(obj.RodSize/2)])) + rodprofile = Part.Face(Part.Wire([Part.makeCircle(obj.RodSize / 2)])) else: - rodprofile = Part.Face(Part.makePlane(obj.RodSize,obj.RodSize,FreeCAD.Vector(-obj.RodSize/2,-obj.RodSize/2,0))) + rodprofile = Part.Face( + Part.makePlane( + obj.RodSize, obj.RodSize, FreeCAD.Vector(-obj.RodSize / 2, -obj.RodSize / 2, 0) + ) + ) # create rods rods = [] - bottomrodstart = v0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value/2)) - toprodstart = v2.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value/2).negative()) - bottomrodvec = FreeCAD.Vector(maindir).multiply(1/obj.RodSections) - toprodvec = FreeCAD.Vector(topdir).multiply(1/obj.RodSections) + bottomrodstart = v0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value / 2)) + toprodstart = v2.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value / 2).negative()) + bottomrodvec = FreeCAD.Vector(maindir).multiply(1 / obj.RodSections) + toprodvec = FreeCAD.Vector(topdir).multiply(1 / obj.RodSections) bottomrodpos = [bottomrodstart] toprodpos = [toprodstart] if obj.RodMode == rodmodes[0]: # /|/|/| - for i in range(1,obj.RodSections+1): + for i in range(1, obj.RodSections + 1): if (i > 1) or (obj.StrutStartOffset.Value >= 0): # do not add first vert rod if negative offset - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) toprodpos.append(toprodpos[-1].add(toprodvec)) if obj.RodDirection == "Forward": - rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) else: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) elif obj.RodMode == rodmodes[1]: # /\/\/\ fw = True - for i in range(1,obj.RodSections+1): + for i in range(1, obj.RodSections + 1): bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) toprodpos.append(toprodpos[-1].add(toprodvec)) if obj.RodDirection == "Forward": if fw: - rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) else: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) else: if fw: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) else: - rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) fw = not fw elif obj.RodMode == rodmodes[2]: # /|\|/|\ fw = True - for i in range(1,obj.RodSections+1): + for i in range(1, obj.RodSections + 1): if (i > 1) or (obj.StrutStartOffset.Value >= 0): # do not add first vert rod if negative offset - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) toprodpos.append(toprodpos[-1].add(toprodvec)) if obj.RodDirection == "Forward": if fw: - rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) else: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) else: if fw: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) else: - rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) fw = not fw # add end rod if obj.RodEnd: - rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1])) + rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) # trim rods rods = [rod.cut(topstrut).cut(bottomstrut) for rod in rods] - return topstrut,bottomstrut,rods,angle - - def makeRod(self,profile,p1,p2): + return topstrut, bottomstrut, rods, angle + def makeRod(self, profile, p1, p2): """makes a rod by extruding profile between p1 and p2""" - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),p2.sub(p1)) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), p2.sub(p1)) rod = profile.copy().translate(p1) - rod = rod.rotate(p1,rot.Axis,math.degrees(rot.Angle)) + rod = rod.rotate(p1, rot.Axis, math.degrees(rot.Angle)) rod = rod.extrude(p2.sub(p1)) return rod class ViewProviderTruss(ArchComponent.ViewProviderComponent): - - "A View Provider for the Truss object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc + return ":/icons/Arch_Truss_Tree.svg" diff --git a/src/Mod/BIM/ArchVRM.py b/src/Mod/BIM/ArchVRM.py index 33f74f23d5..61c46fd2de 100644 --- a/src/Mod/BIM/ArchVRM.py +++ b/src/Mod/BIM/ArchVRM.py @@ -43,17 +43,18 @@ import Part from draftutils import params -MAXLOOP = 10 # the max number of loop before abort +MAXLOOP = 10 # the max number of loop before abort # WARNING: in this module, faces are lists whose first item is the actual OCC face, the # other items being additional information such as color, etc. DEBUG = params.get_param_arch("ShowVRMDebug") + class Renderer: "A renderer object" - def __init__(self,wp=None): + def __init__(self, wp=None): """ Creates a renderer with a default Draft WorkingPlane Use like this: @@ -70,9 +71,11 @@ class Renderer: self.wp = wp else: import WorkingPlane + self.wp = WorkingPlane.PlaneBase() - if DEBUG: print("Renderer initialized on " + str(self.wp)) + if DEBUG: + print("Renderer initialized on " + str(self.wp)) def __str__(self): return "Arch Renderer: " + str(len(self.faces)) + " faces projected on " + str(self.wp) @@ -94,54 +97,62 @@ class Renderer: self.sections = [] self.hiddenEdges = [] - def setWorkingPlane(self,wp): + def setWorkingPlane(self, wp): "sets a Draft WorkingPlane or Placement for this renderer" - if isinstance(wp,FreeCAD.Placement): + if isinstance(wp, FreeCAD.Placement): self.wp.align_to_placement(wp) else: self.wp = wp - if DEBUG: print("Renderer set on " + str(self.wp)) + if DEBUG: + print("Renderer set on " + str(self.wp)) - def addFaces(self,faces,color=(0.9,0.9,0.9,1.0)): + def addFaces(self, faces, color=(0.9, 0.9, 0.9, 1.0)): "add individual faces to this renderer, optionally with a color" - if DEBUG: print("adding ", len(faces), " faces. Warning, these will get lost if using cut() or join()") + if DEBUG: + print( + "adding ", + len(faces), + " faces. Warning, these will get lost if using cut() or join()", + ) for f in faces: - self.faces.append([f,color]) + self.faces.append([f, color]) self.resetFlags() - def addObjects(self,objs): + def addObjects(self, objs): "add objects to this renderer" for o in objs: if o.isDerivedFrom("Part::Feature"): self.objects.append(o) color = o.ViewObject.ShapeColor if o.Shape.Faces: - self.shapes.append([o.Shape,color]) + self.shapes.append([o.Shape, color]) for f in o.Shape.Faces: - self.faces.append([f,color]) + self.faces.append([f, color]) self.resetFlags() - if DEBUG: print("adding ", len(self.objects), " objects, ", len(self.faces), " faces") + if DEBUG: + print("adding ", len(self.objects), " objects, ", len(self.faces), " faces") - def addShapes(self,shapes,color=(0.9,0.9,0.9,1.0)): + def addShapes(self, shapes, color=(0.9, 0.9, 0.9, 1.0)): "add shapes to this renderer, optionally with a color. Warning, these will get lost if using join()" - if DEBUG: print("adding ", len(shapes), " shapes") + if DEBUG: + print("adding ", len(shapes), " shapes") for s in shapes: if s.Faces: - self.shapes.append([s,color]) + self.shapes.append([s, color]) for f in s.Faces: - self.faces.append([f,color]) + self.faces.append([f, color]) self.resetFlags() def info(self): "Prints info about the contents of this renderer" - r = str(self)+"\n" + r = str(self) + "\n" r += "oriented: " + str(self.oriented) + "\n" r += "trimmed: " + str(self.trimmed) + "\n" r += "sorted: " + str(self.sorted) + "\n" r += "contains " + str(len(self.faces)) + " faces\n" for i in range(len(self.faces)): r += " face " + str(i) + " : center " + str(self.faces[i][0].CenterOfMass) - r += " : normal " + str(self.faces[i][0].normalAt(0,0)) + r += " : normal " + str(self.faces[i][0].normalAt(0, 0)) r += ", " + str(len(self.faces[i][0].Vertexes)) + " verts" r += ", color: " + self.getFill(self.faces[i][1]) + "\n" return r @@ -150,22 +161,30 @@ class Renderer: "Add labels on the model to identify faces" c = 0 for f in self.faces: - l = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel","facelabel") + l = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel", "facelabel") l.BasePosition = f[0].CenterOfMass l.LabelText = str(c) c += 1 - def isVisible(self,face): + def isVisible(self, face): "returns True if the given face points in the view direction" - normal = face[0].normalAt(0,0) - if DEBUG: print("checking face normal ", normal, " against ", self.wp.axis, " : ", math.degrees(normal.getAngle(self.wp.axis))) - if normal.getAngle(self.wp.axis) < math.pi/2: + normal = face[0].normalAt(0, 0) + if DEBUG: + print( + "checking face normal ", + normal, + " against ", + self.wp.axis, + " : ", + math.degrees(normal.getAngle(self.wp.axis)), + ) + if normal.getAngle(self.wp.axis) < math.pi / 2: return True return False def reorient(self): "reorients the faces on the WP" - #print("VRM: start reorient") + # print("VRM: start reorient") if not self.faces: return self.faces = [self.projectFace(f) for f in self.faces] @@ -174,7 +193,7 @@ class Renderer: if self.hiddenEdges: self.hiddenEdges = [self.projectEdge(e) for e in self.hiddenEdges] self.oriented = True - #print("VRM: end reorient") + # print("VRM: end reorient") def removeHidden(self): "removes faces pointing outwards" @@ -184,53 +203,56 @@ class Renderer: for f in self.faces: if self.isVisible(f): faces.append(f) - if DEBUG: print(len(self.faces)-len(faces) , " faces removed, ", len(faces), " faces retained") + if DEBUG: + print(len(self.faces) - len(faces), " faces removed, ", len(faces), " faces retained") self.faces = faces self.trimmed = True - def projectFace(self,face): + def projectFace(self, face): "projects a single face on the WP" - #print("VRM: projectFace start: ",len(face[0].Vertexes)," verts, ",len(face[0].Edges)," edges") + # print("VRM: projectFace start: ",len(face[0].Vertexes)," verts, ",len(face[0].Edges)," edges") wires = [] if not face[0].Wires: - if DEBUG: print("Error: Unable to project face on the WP") + if DEBUG: + print("Error: Unable to project face on the WP") return None - norm = face[0].normalAt(0,0) + norm = face[0].normalAt(0, 0) for w in face[0].Wires: verts = [] edges = Part.__sortEdges__(w.Edges) - #print(len(edges)," edges after sorting") + # print(len(edges)," edges after sorting") for e in edges: v = e.Vertexes[0].Point - #print(v) + # print(v) v = self.wp.get_local_coords(v) verts.append(v) verts.append(verts[0]) if len(verts) > 2: - #print("new wire with ",len(verts)) + # print("new wire with ",len(verts)) wires.append(Part.makePolygon(verts)) try: sh = ArchCommands.makeFace(wires) except Exception: - if DEBUG: print("Error: Unable to project face on the WP") + if DEBUG: + print("Error: Unable to project face on the WP") return None else: # restoring flipped normals vnorm = self.wp.get_local_coords(norm) - if vnorm.getAngle(sh.normalAt(0,0)) > 1: + if vnorm.getAngle(sh.normalAt(0, 0)) > 1: sh.reverse() - #print("VRM: projectFace end: ",len(sh.Vertexes)," verts") - return [sh]+face[1:] + # print("VRM: projectFace end: ",len(sh.Vertexes)," verts") + return [sh] + face[1:] - def projectEdge(self,edge): + def projectEdge(self, edge): "projects a single edge on the WP" if len(edge.Vertexes) > 1: v1 = self.wp.get_local_coords(edge.Vertexes[0].Point) v2 = self.wp.get_local_coords(edge.Vertexes[-1].Point) - return Part.LineSegment(v1,v2).toShape() + return Part.LineSegment(v1, v2).toShape() return edge - def flattenFace(self,face): + def flattenFace(self, face): "Returns a face where all vertices have Z = 0" wires = [] for w in face[0].Wires: @@ -238,30 +260,33 @@ class Renderer: edges = Part.__sortEdges__(w.Edges) for e in edges: v = e.Vertexes[0].Point - verts.append(FreeCAD.Vector(v.x,v.y,0)) + verts.append(FreeCAD.Vector(v.x, v.y, 0)) verts.append(verts[0]) wires.append(Part.makePolygon(verts)) try: sh = Part.Face(wires) except Part.OCCError: - if DEBUG: print("Error: Unable to flatten face") + if DEBUG: + print("Error: Unable to flatten face") return None else: - return [sh]+face[1:] + return [sh] + face[1:] - def cut(self,cutplane,hidden=False): + def cut(self, cutplane, hidden=False): "Cuts through the shapes with a given cut plane and builds section faces" - if DEBUG: print("\n\n======> Starting cut\n\n") + if DEBUG: + print("\n\n======> Starting cut\n\n") if self.iscut: return if not self.shapes: - if DEBUG: print("No objects to make sections") + if DEBUG: + print("No objects to make sections") else: - fill = (1.0,1.0,1.0,1.0) + fill = (1.0, 1.0, 1.0, 1.0) shps = [] for sh in self.shapes: shps.append(sh[0]) - cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(cutplane,shps) + cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume(cutplane, shps) if cutface and cutvolume: shapes = [] faces = [] @@ -269,28 +294,36 @@ class Renderer: for sh in self.shapes: for sol in sh[0].Solids: c = sol.cut(cutvolume) - shapes.append([c]+sh[1:]) + shapes.append([c] + sh[1:]) for f in c.Faces: - faces.append([f]+sh[1:]) - #print("iscoplanar:",f.Vertexes[0].Point,f.normalAt(0,0),cutface.Vertexes[0].Point,cutface.normalAt(0,0)) - if DraftGeomUtils.isCoplanar([f,cutface]): + faces.append([f] + sh[1:]) + # print("iscoplanar:",f.Vertexes[0].Point,f.normalAt(0,0),cutface.Vertexes[0].Point,cutface.normalAt(0,0)) + if DraftGeomUtils.isCoplanar([f, cutface]): print("COPLANAR") - sections.append([f,fill]) + sections.append([f, fill]) if hidden: c = sol.cut(invcutvolume) self.hiddenEdges.extend(c.Edges) self.shapes = shapes self.faces = faces self.sections = sections - if DEBUG: print("Built ",len(self.sections)," sections, ", len(self.faces), " faces retained") + if DEBUG: + print( + "Built ", + len(self.sections), + " sections, ", + len(self.faces), + " faces retained", + ) self.iscut = True self.oriented = False self.trimmed = False self.sorted = False self.joined = False - if DEBUG: print("\n\n======> Finished cut\n\n") + if DEBUG: + print("\n\n======> Finished cut\n\n") - def isInside(self,vert,face): + def isInside(self, vert, face): "Returns True if the vert is inside the face in Z projection" if not face: @@ -302,11 +335,11 @@ class Renderer: for e in face[0].Edges: p1 = e.Vertexes[0].Point p2 = e.Vertexes[-1].Point - if p.y > min(p1.y,p2.y): - if p.y <= max(p1.y,p2.y): - if p.x <= max(p1.x,p2.x): + if p.y > min(p1.y, p2.y): + if p.y <= max(p1.y, p2.y): + if p.x <= max(p1.x, p2.x): if p1.y != p2.y: - xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x + xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x if (p1.x == p2.x) or (p.x <= xinters): count += 1 if count % 2 == 0: @@ -314,7 +347,7 @@ class Renderer: else: return True - def zOverlaps(self,face1,face2): + def zOverlaps(self, face1, face2): "Checks if face1 overlaps face2 in Z direction" face1 = self.flattenFace(face1) face2 = self.flattenFace(face2) @@ -324,26 +357,28 @@ class Renderer: # first we check if one of the verts is inside the other face for v in face1[0].Vertexes: - if self.isInside(v,face2): + if self.isInside(v, face2): return True # even so, faces can still overlap if their edges cross each other for e1 in face1[0].Edges: for e2 in face2[0].Edges: - if DraftGeomUtils.findIntersection(e1,e2): + if DraftGeomUtils.findIntersection(e1, e2): return True return False - def compare(self,face1,face2): + def compare(self, face1, face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" - #print(face1,face2) + # print(face1,face2) if not face1: - if DEBUG: print("Warning, undefined face!") + if DEBUG: + print("Warning, undefined face!") return 31 elif not face2: - if DEBUG: print("Warning, undefined face!" ) + if DEBUG: + print("Warning, undefined face!") return 32 # theory from @@ -354,7 +389,8 @@ class Renderer: b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible - if DEBUG: print("doing test 1") + if DEBUG: + print("doing test 1") if b1.XMax < b2.XMin: return 0 if b1.XMin > b2.XMax: @@ -363,24 +399,28 @@ class Renderer: return 0 if b1.YMin > b2.YMax: return 0 - if DEBUG: print("failed, faces bboxes are not distinct") + if DEBUG: + print("failed, faces bboxes are not distinct") # test 2: if Z bounds don't overlap, it's easy to know the closest - if DEBUG: print("doing test 2") + if DEBUG: + print("doing test 2") if b1.ZMax < b2.ZMin: return 2 if b2.ZMax < b1.ZMin: return 1 - if DEBUG: print("failed, faces Z are not distinct") + if DEBUG: + print("failed, faces Z are not distinct") # test 3: all verts of face1 are in front or behind the plane of face2 - if DEBUG: print("doing test 3") - norm = face2[0].normalAt(0,0) + if DEBUG: + print("doing test 3") + norm = face2[0].normalAt(0, 0) behind = 0 front = 0 for v in face1[0].Vertexes: dv = v.Point.sub(face2[0].Vertexes[0].Point) - dv = DraftVecUtils.project(dv,norm) + dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 @@ -389,21 +429,24 @@ class Renderer: behind += 1 else: front += 1 - if DEBUG: print("front: ",front," behind: ",behind) + if DEBUG: + print("front: ", front, " behind: ", behind) if behind == len(face1[0].Vertexes): return 2 elif front == len(face1[0].Vertexes): return 1 - if DEBUG: print("failed, cannot say if face 1 is in front or behind") + if DEBUG: + print("failed, cannot say if face 1 is in front or behind") # test 4: all verts of face2 are in front or behind the plane of face1 - if DEBUG: print("doing test 4") - norm = face1[0].normalAt(0,0) + if DEBUG: + print("doing test 4") + norm = face1[0].normalAt(0, 0) behind = 0 front = 0 for v in face2[0].Vertexes: dv = v.Point.sub(face1[0].Vertexes[0].Point) - dv = DraftVecUtils.project(dv,norm) + dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 @@ -412,27 +455,33 @@ class Renderer: behind += 1 else: front += 1 - if DEBUG: print("front: ",front," behind: ",behind) + if DEBUG: + print("front: ", front, " behind: ", behind) if behind == len(face2[0].Vertexes): return 1 elif front == len(face2[0].Vertexes): return 2 - if DEBUG: print("failed, cannot say if face 2 is in front or behind") + if DEBUG: + print("failed, cannot say if face 2 is in front or behind") # test 5: see if faces projections don't overlap, vertexwise - if DEBUG: print("doing test 5") - if not self.zOverlaps(face1,face2): + if DEBUG: + print("doing test 5") + if not self.zOverlaps(face1, face2): return 0 - elif not self.zOverlaps(face2,face1): + elif not self.zOverlaps(face2, face1): return 0 - if DEBUG: print("failed, faces are overlapping") + if DEBUG: + print("failed, faces are overlapping") - if DEBUG: print("Houston, all tests passed, and still no results") + if DEBUG: + print("Houston, all tests passed, and still no results") return 0 - def join(self,otype): + def join(self, otype): "joins the objects of same type" import Part + walls = [] structs = [] objs = [] @@ -444,9 +493,9 @@ class Renderer: structs.append(o) else: objs.append(o) - for g in [walls,structs]: + for g in [walls, structs]: if g: - print("group:",g) + print("group:", g) col = g[0].ViewObject.DiffuseColor[0] s = g[0].Shape for o in g[1:]: @@ -455,18 +504,24 @@ class Renderer: fs = fs.removeSplitter() except Part.OCCError: print("shape fusion failed") - objs.append([o.Shape,o.ViewObject.DiffuseColor[0]]) + objs.append([o.Shape, o.ViewObject.DiffuseColor[0]]) else: s = fs - objs.append([s,col]) + objs.append([s, col]) - def findPosition(self,f1,faces): + def findPosition(self, f1, faces): "Finds the position of a face in a list of faces" l = None h = None for f2 in faces: - if DEBUG: print("comparing face",str(self.faces.index(f1))," with face",str(self.faces.index(f2))) - r = self.compare(f1,f2) + if DEBUG: + print( + "comparing face", + str(self.faces.index(f1)), + " with face", + str(self.faces.index(f2)), + ) + r = self.compare(f1, f2) if r == 1: l = faces.index(f2) elif r == 2: @@ -484,28 +539,34 @@ class Renderer: def sort(self): "projects a shape on the WP" - if DEBUG: print("\n\n======> Starting sort\n\n") + if DEBUG: + print("\n\n======> Starting sort\n\n") if len(self.faces) <= 1: return if not self.trimmed: self.removeHidden() - if DEBUG: print("Done hidden face removal") + if DEBUG: + print("Done hidden face removal") if len(self.faces) == 1: return if not self.oriented: self.reorient() - if DEBUG: print("Done reorientation") + if DEBUG: + print("Done reorientation") faces = self.faces[:] - if DEBUG: print("sorting ",len(self.faces)," faces") + if DEBUG: + print("sorting ", len(self.faces), " faces") sfaces = [] loopcount = 0 notfoundstack = 0 while faces: - if DEBUG: print("loop ", loopcount) + if DEBUG: + print("loop ", loopcount) f1 = faces[0] if sfaces and (notfoundstack < len(faces)): - if DEBUG: print("using ordered stack, notfound = ",notfoundstack) - p = self.findPosition(f1,sfaces) + if DEBUG: + print("using ordered stack, notfound = ", notfoundstack) + p = self.findPosition(f1, sfaces) if p is None: # no position found, we move the face to the end of the pile faces.remove(f1) @@ -514,16 +575,23 @@ class Renderer: else: # position found, we insert it faces.remove(f1) - sfaces.insert(p,f1) + sfaces.insert(p, f1) notfoundstack = 0 else: # either there is no stack, or no more face can be compared # find a root, 2 faces that can be compared - if DEBUG: print("using unordered stack, notfound = ",notfoundstack) + if DEBUG: + print("using unordered stack, notfound = ", notfoundstack) for f2 in faces[1:]: - if DEBUG: print("comparing face",str(self.faces.index(f1))," with face",str(self.faces.index(f2))) - r = self.compare(f1,f2) - print("comparison result:",r) + if DEBUG: + print( + "comparing face", + str(self.faces.index(f1)), + " with face", + str(self.faces.index(f2)), + ) + r = self.compare(f1, f2) + print("comparison result:", r) if r == 1: faces.remove(f2) sfaces.append(f2) @@ -551,13 +619,22 @@ class Renderer: faces.append(f1) loopcount += 1 if loopcount == MAXLOOP * len(self.faces): - if DEBUG: print("Too many loops, aborting.") + if DEBUG: + print("Too many loops, aborting.") break - if DEBUG: print("done Z sorting. ", len(sfaces), " faces retained, ", len(self.faces)-len(sfaces), " faces lost.") + if DEBUG: + print( + "done Z sorting. ", + len(sfaces), + " faces retained, ", + len(self.faces) - len(sfaces), + " faces lost.", + ) self.faces = sfaces self.sorted = True - if DEBUG: print("\n\n======> Finished sort\n\n") + if DEBUG: + print("\n\n======> Finished sort\n\n") def buildDummy(self): "Builds a dummy object with faces spaced on the Z axis, for visual check" @@ -567,95 +644,119 @@ class Renderer: faces = [] for f in self.faces[:]: ff = self.flattenFace(f)[0] - ff.translate(FreeCAD.Vector(0,0,z)) + ff.translate(FreeCAD.Vector(0, 0, z)) faces.append(ff) z += 1 if faces: c = Part.makeCompound(faces) Part.show(c) - def getFill(self,fill): + def getFill(self, fill): "Returns a SVG fill value" - r = str(hex(int(fill[0]*255)))[2:].zfill(2) - g = str(hex(int(fill[1]*255)))[2:].zfill(2) - b = str(hex(int(fill[2]*255)))[2:].zfill(2) - col = "#"+r+g+b + r = str(hex(int(fill[0] * 255)))[2:].zfill(2) + g = str(hex(int(fill[1] * 255)))[2:].zfill(2) + b = str(hex(int(fill[2] * 255)))[2:].zfill(2) + col = "#" + r + g + b return col - def getPathData(self,w): + def getPathData(self, w): "Returns a SVG path data string from a 2D wire" + def tostr(val): - return str(round(val,DraftVecUtils.precision())) + return str(round(val, DraftVecUtils.precision())) + edges = Part.__sortEdges__(w.Edges) v = edges[0].Vertexes[0].Point - svg = 'M '+ tostr(v.x) +' '+ tostr(v.y) + ' ' + svg = "M " + tostr(v.x) + " " + tostr(v.y) + " " for e in edges: - if (DraftGeomUtils.geomType(e) == "Line") or (DraftGeomUtils.geomType(e) == "BSplineCurve"): + if (DraftGeomUtils.geomType(e) == "Line") or ( + DraftGeomUtils.geomType(e) == "BSplineCurve" + ): v = e.Vertexes[-1].Point - svg += 'L '+ tostr(v.x) +' '+ tostr(v.y) + ' ' + svg += "L " + tostr(v.x) + " " + tostr(v.y) + " " elif DraftGeomUtils.geomType(e) == "Circle": r = e.Curve.Radius v = e.Vertexes[-1].Point - svg += 'A '+ tostr(r) + ' '+ tostr(r) +' 0 0 1 '+ tostr(v.x) +' ' - svg += tostr(v.y) + ' ' + svg += "A " + tostr(r) + " " + tostr(r) + " 0 0 1 " + tostr(v.x) + " " + svg += tostr(v.y) + " " if len(edges) > 1: - svg += 'Z ' + svg += "Z " return svg - def getViewSVG(self,linewidth=0.01): + def getViewSVG(self, linewidth=0.01): "Returns a SVG fragment from viewed faces" - if DEBUG: print("Printing ", len(self.faces), " faces") + if DEBUG: + print("Printing ", len(self.faces), " faces") if not self.sorted: self.sort() - svg = '\n' for f in self.faces: if f: fill = self.getFill(f[1]) - svg +=' \n' - svg += '\n' + svg += "\n" return svg - def getSectionSVG(self,linewidth=0.02,fillpattern=None): + def getSectionSVG(self, linewidth=0.02, fillpattern=None): "Returns a SVG fragment from cut faces" - if DEBUG: print("Printing ", len(self.sections), " sections") + if DEBUG: + print("Printing ", len(self.sections), " sections") if not self.oriented: self.reorient() - svg = '\n' for f in self.sections: if f: if fillpattern: - if "#" in fillpattern: # color + if "#" in fillpattern: # color fill = fillpattern else: - fill="url(#"+fillpattern+")" # pattern name + fill = "url(#" + fillpattern + ")" # pattern name else: - fill = 'none' # none - svg +='\n' - svg += '\n' + svg += "\n" return svg - def getHiddenSVG(self,linewidth=0.02): + def getHiddenSVG(self, linewidth=0.02): "Returns a SVG fragment from cut geometry" - if DEBUG: print("Printing ", len(self.sections), " hidden faces") + if DEBUG: + print("Printing ", len(self.sections), " hidden faces") if not self.oriented: self.reorient() - svg = '\n' + svg = ( + '\n' + ) for e in self.hiddenEdges: - svg +=' 0 ##( or 1 ? ) width = 0 # Get width of each edge segment from Base Objects if they store it # (Adding support in SketchFeaturePython, DWire...) widths = [] # [] or None are both False - if hasattr(obj,"ArchSketchData") and obj.ArchSketchData and Draft.getType(obj.Base) == "ArchSketch": - if hasattr(obj.Base, 'Proxy'): # TODO Any need to test ? - if hasattr(obj.Base.Proxy, 'getWidths'): + if ( + hasattr(obj, "ArchSketchData") + and obj.ArchSketchData + and Draft.getType(obj.Base) == "ArchSketch" + ): + if hasattr(obj.Base, "Proxy"): # TODO Any need to test ? + if hasattr(obj.Base.Proxy, "getWidths"): # Return a list of Width corresponding to indexes of sorted # edges of Sketch. - widths = obj.Base.Proxy.getWidths(obj.Base, - propSetUuid=propSetUuid) + widths = obj.Base.Proxy.getWidths(obj.Base, propSetUuid=propSetUuid) # Get width of each edge/wall segment from ArchWall.OverrideWidth if # Base Object does not provide it if not widths: @@ -741,7 +962,9 @@ class _Wall(ArchComponent.Component): except Exception: print("ArchSketchObject add-on module is not installed yet") try: - widths = ArchSketchObject.sortSketchWidth(obj.Base, obj.OverrideWidth, obj.ArchSketchEdges) + widths = ArchSketchObject.sortSketchWidth( + obj.Base, obj.OverrideWidth, obj.ArchSketchEdges + ) except Exception: widths = obj.OverrideWidth else: @@ -754,7 +977,7 @@ class _Wall(ArchComponent.Component): else: # having no width is valid for walls so the user doesn't need to be warned # it just disables extrusions and return none - #print ("Width & OverrideWidth & base.getWidths() should not be all 0 or None or [] empty list ") + # print ("Width & OverrideWidth & base.getWidths() should not be all 0 or None or [] empty list ") return None # Set 'default' width - for filling in any item in the list == 0 or None @@ -766,13 +989,16 @@ class _Wall(ArchComponent.Component): # Get align of each edge segment from Base Objects if they store it. # (Adding support in SketchFeaturePython, DWire...) aligns = [] - if hasattr(obj,"ArchSketchData") and obj.ArchSketchData and Draft.getType(obj.Base) == "ArchSketch": - if hasattr(obj.Base, 'Proxy'): - if hasattr(obj.Base.Proxy, 'getAligns'): + if ( + hasattr(obj, "ArchSketchData") + and obj.ArchSketchData + and Draft.getType(obj.Base) == "ArchSketch" + ): + if hasattr(obj.Base, "Proxy"): + if hasattr(obj.Base.Proxy, "getAligns"): # Return a list of Align corresponds to indexes of sorted # edges of Sketch. - aligns = obj.Base.Proxy.getAligns(obj.Base, - propSetUuid=propSetUuid) + aligns = obj.Base.Proxy.getAligns(obj.Base, propSetUuid=propSetUuid) # Get align of each edge/wall segment from ArchWall.OverrideAlign if # Base Object does not provide it if not aligns: @@ -787,7 +1013,9 @@ class _Wall(ArchComponent.Component): except Exception: print("ArchSketchObject add-on module is not installed yet") try: - aligns = ArchSketchObject.sortSketchAlign(obj.Base, obj.OverrideAlign, obj.ArchSketchEdges) + aligns = ArchSketchObject.sortSketchAlign( + obj.Base, obj.OverrideAlign, obj.ArchSketchEdges + ) except Exception: aligns = obj.OverrideAlign else: @@ -804,13 +1032,16 @@ class _Wall(ArchComponent.Component): # Get offset of each edge segment from Base Objects if they store it # (Adding support in SketchFeaturePython, DWire...) offsets = [] # [] or None are both False - if hasattr(obj,"ArchSketchData") and obj.ArchSketchData and Draft.getType(obj.Base) == "ArchSketch": - if hasattr(obj.Base, 'Proxy'): - if hasattr(obj.Base.Proxy, 'getOffsets'): + if ( + hasattr(obj, "ArchSketchData") + and obj.ArchSketchData + and Draft.getType(obj.Base) == "ArchSketch" + ): + if hasattr(obj.Base, "Proxy"): + if hasattr(obj.Base.Proxy, "getOffsets"): # Return a list of Offset corresponding to indexes of sorted # edges of Sketch. - offsets = obj.Base.Proxy.getOffsets(obj.Base, - propSetUuid=propSetUuid) + offsets = obj.Base.Proxy.getOffsets(obj.Base, propSetUuid=propSetUuid) # Get offset of each edge/wall segment from ArchWall.OverrideOffset if # Base Object does not provide it if not offsets: @@ -818,8 +1049,10 @@ class _Wall(ArchComponent.Component): if obj.Base and obj.Base.isDerivedFrom("Sketcher::SketchObject"): # If Base Object is ordinary Sketch (or when ArchSketch.getOffsets() not implemented yet):- # sort the offset list in OverrideOffset to correspond to indexes of sorted edges of Sketch - if hasattr(ArchSketchObject, 'sortSketchOffset'): - offsets = ArchSketchObject.sortSketchOffset(obj.Base, obj.OverrideOffset, obj.ArchSketchEdges) + if hasattr(ArchSketchObject, "sortSketchOffset"): + offsets = ArchSketchObject.sortSketchOffset( + obj.Base, obj.OverrideOffset, obj.ArchSketchEdges + ) else: offsets = obj.OverrideOffset else: @@ -838,13 +1071,13 @@ class _Wall(ArchComponent.Component): height = self.getParentHeight(obj) if not height: return None - if obj.Normal == Vector(0,0,0): - if obj.Base and hasattr(obj.Base,'Shape'): + if obj.Normal == Vector(0, 0, 0): + if obj.Base and hasattr(obj.Base, "Shape"): normal = DraftGeomUtils.get_shape_normal(obj.Base.Shape) if normal is None: - normal = Vector(0,0,1) + normal = Vector(0, 0, 1) else: - normal = Vector(0,0,1) + normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) base = None @@ -853,9 +1086,9 @@ class _Wall(ArchComponent.Component): # build wall layers layers = [] - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: - if hasattr(obj.Material,"Materials"): + if hasattr(obj.Material, "Materials"): thicknesses = [abs(t) for t in obj.Material.Thicknesses] # multimaterials varwidth = 0 @@ -863,7 +1096,7 @@ class _Wall(ArchComponent.Component): if restwidth > 0: varwidth = [t for t in thicknesses if t == 0] if varwidth: - varwidth = restwidth/len(varwidth) + varwidth = restwidth / len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) @@ -872,7 +1105,7 @@ class _Wall(ArchComponent.Component): # Check if there is obj.Base and its validity to proceed if self.ensureBase(obj): - if hasattr(obj.Base,'Shape'): + if hasattr(obj.Base, "Shape"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None @@ -884,10 +1117,10 @@ class _Wall(ArchComponent.Component): # original position. elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: - face = obj.Base.Shape.Faces[obj.Face-1] - if obj.Normal != Vector(0,0,0): - normal = face.normalAt(0,0) - if normal.getAngle(Vector(0,0,1)) > math.pi/4: + face = obj.Base.Shape.Faces[obj.Face - 1] + if obj.Normal != Vector(0, 0, 0): + normal = face.normalAt(0, 0) + if normal.getAngle(Vector(0, 0, 1)) > math.pi / 4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": @@ -898,7 +1131,7 @@ class _Wall(ArchComponent.Component): normal.multiply(height) base = face.extrude(normal) base, placement = self.rebase(base) - return (base,normal,placement) + return (base, normal, placement) # If the Base has faces, but no specific one has been # selected, rebase the faces and continue. @@ -906,15 +1139,21 @@ class _Wall(ArchComponent.Component): if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: - base,placement = self.rebase(obj.Base.Shape) + base, placement = self.rebase(obj.Base.Shape) - elif hasattr(obj.Base, 'Proxy') and obj.ArchSketchData and \ - hasattr(obj.Base.Proxy, 'getWallBaseShapeEdgesInfo'): - wallBaseShapeEdgesInfo = obj.Base.Proxy.getWallBaseShapeEdgesInfo(obj.Base, - propSetUuid=propSetUuid) - #get wall edges (not wires); use original edges if getWallBaseShapeEdgesInfo() provided none + elif ( + hasattr(obj.Base, "Proxy") + and obj.ArchSketchData + and hasattr(obj.Base.Proxy, "getWallBaseShapeEdgesInfo") + ): + wallBaseShapeEdgesInfo = obj.Base.Proxy.getWallBaseShapeEdgesInfo( + obj.Base, propSetUuid=propSetUuid + ) + # get wall edges (not wires); use original edges if getWallBaseShapeEdgesInfo() provided none if wallBaseShapeEdgesInfo: - self.basewires = wallBaseShapeEdgesInfo.get('wallAxis') # 'wallEdges' # widths, aligns, offsets? + self.basewires = wallBaseShapeEdgesInfo.get( + "wallAxis" + ) # 'wallEdges' # widths, aligns, offsets? # Sort Sketch edges consistently with below procedures # without using Sketch.Shape.Edges - found the latter order @@ -926,17 +1165,20 @@ class _Wall(ArchComponent.Component): skPlacement = obj.Base.Placement # Get Sketch's placement to restore later # Get ArchSketch edges to construct ArchWall # No need to test obj.ArchSketchData ... - for ig, geom in enumerate(skGeom): + for ig, geom in enumerate(skGeom): # Construction mode edges should be ignored if # ArchSketchEdges, otherwise, ArchSketchEdges data # needs to take out those in Construction before # using as parameters. - if (not obj.ArchSketchEdges and not geom.Construction) or str(ig) in obj.ArchSketchEdges: + if (not obj.ArchSketchEdges and not geom.Construction) or str( + ig + ) in obj.ArchSketchEdges: # support Line, Arc, Circle, Ellipse for Sketch # as Base at the moment - if isinstance(geom.Geometry, (Part.LineSegment, - Part.Circle, Part.ArcOfCircle, - Part.Ellipse)): + if isinstance( + geom.Geometry, + (Part.LineSegment, Part.Circle, Part.ArcOfCircle, Part.Ellipse), + ): skGeomEdgesI = geom.Geometry.toShape() skGeomEdges.append(skGeomEdgesI) @@ -945,7 +1187,9 @@ class _Wall(ArchComponent.Component): for edge in cluster: # TODO 2023.11.26: Multiplication order should be switched? # So far 'no problem' as 'edge.placement' is always '0,0,0' ? - edge.Placement = edge.Placement.multiply(skPlacement) ## TODO add attribute to skip Transform... + edge.Placement = edge.Placement.multiply( + skPlacement + ) ## TODO add attribute to skip Transform... clusterTransformed.append(edge) # Only use cluster of edges rather than turning into wire @@ -957,12 +1201,14 @@ class _Wall(ArchComponent.Component): # sketch.getGlobalPlacement() - # https://forum.freecad.org/viewtopic.php?f=22&t=39341&p=334275#p334275 # normal = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) - normal = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1)) + normal = obj.Base.getGlobalPlacement().Rotation.multVec( + FreeCAD.Vector(0, 0, 1) + ) - else: #For all objects except Sketch, single edge or more + else: # For all objects except Sketch, single edge or more # See discussion - https://forum.freecad.org/viewtopic.php?t=86365 # See discussion - https://forum.freecad.org/viewtopic.php?t=82207&start=10 - #self.basewires = obj.Base.Shape.Wires + # self.basewires = obj.Base.Shape.Wires # # Now, adopt approach same as for Sketch self.basewires = [] @@ -980,15 +1226,15 @@ class _Wall(ArchComponent.Component): # See FC discussion - # https://forum.freecad.org/viewtopic.php?f=23&t=48275&p=413745#p413745 - #self.basewires = [] - #for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): + # self.basewires = [] + # for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): # for c in Part.sortEdges(cluster): # self.basewires.append(Part.Wire(c)) # if not sketch, e.g. Dwire, can have wire which is 3d # so not on the placement's working plane - below # applied to Sketch not applicable here - #normal = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1)) - #normal = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) + # normal = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1)) + # normal = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) if self.basewires: if (len(self.basewires) == 1) and layers: @@ -999,23 +1245,25 @@ class _Wall(ArchComponent.Component): layeroffset = 0 baseface = None self.connectEdges = [] - for i,wire in enumerate(self.basewires): + for i, wire in enumerate(self.basewires): # Check number of edges per 'wire' and get the 1st edge - if isinstance(wire,Part.Wire): + if isinstance(wire, Part.Wire): edgeNum = len(wire.Edges) e = wire.Edges[0] - elif isinstance(wire[0],Part.Edge): + elif isinstance(wire[0], Part.Edge): edgeNum = len(wire) e = wire[0] - for n in range(0,edgeNum,1): # why these not work - range(edgeNum), range(0,edgeNum) ... + for n in range( + 0, edgeNum, 1 + ): # why these not work - range(edgeNum), range(0,edgeNum) ... # Fill the aligns list with ArchWall's default # align entry and with same number of items as # number of edges try: - if aligns[n] not in ['Left', 'Right', 'Center']: + if aligns[n] not in ["Left", "Right", "Center"]: aligns[n] = align except Exception: aligns.append(align) @@ -1041,7 +1289,7 @@ class _Wall(ArchComponent.Component): # normal of the face/sketch and the direction the # wire was drawn in. IE: along the width direction # of the wall. - if isinstance(e.Curve,(Part.Circle,Part.Ellipse)): + if isinstance(e.Curve, (Part.Circle, Part.Ellipse)): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(e).cross(normal) @@ -1051,7 +1299,7 @@ class _Wall(ArchComponent.Component): face = None curAligns = aligns[0] - #off = obj.Offset.Value # off is no longer used + # off = obj.Offset.Value # off is no longer used if curAligns == "Left": @@ -1059,8 +1307,8 @@ class _Wall(ArchComponent.Component): curWidth = [] for n in range(edgeNum): curWidth.append(abs(layers[i])) - #off = off+layeroffset # off is no longer used - offsets = [x+layeroffset for x in offsets] + # off = off+layeroffset # off is no longer used + offsets = [x + layeroffset for x in offsets] dvec.multiply(curWidth[0]) layeroffset += abs(curWidth[0]) else: @@ -1070,33 +1318,39 @@ class _Wall(ArchComponent.Component): # Now DraftGeomUtils.offsetWire() support # similar effect as ArchWall Offset # - #if off: + # if off: # dvec2 = DraftVecUtils.scaleTo(dvec,off) # wire = DraftGeomUtils.offsetWire(wire,dvec2) # Get the 'offseted' wire taking into account # of Width and Align of each edge, and overall # Offset - wNe2 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=curWidth, - offsetMode=None, - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) + wNe2 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=curWidth, + offsetMode=None, + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) # Get the 'base' wire taking into account of # width and align of each edge - wNe1 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=curWidth, - offsetMode="BasewireMode", - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) + wNe1 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=curWidth, + offsetMode="BasewireMode", + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) elif curAligns == "Right": dvec = dvec.negative() @@ -1104,8 +1358,8 @@ class _Wall(ArchComponent.Component): curWidth = [] for n in range(edgeNum): curWidth.append(abs(layers[i])) - #off = off+layeroffset # off is no longer used - offsets = [x+layeroffset for x in offsets] + # off = off+layeroffset # off is no longer used + offsets = [x + layeroffset for x in offsets] dvec.multiply(curWidth[0]) layeroffset += abs(curWidth[0]) else: @@ -1114,60 +1368,70 @@ class _Wall(ArchComponent.Component): # Now DraftGeomUtils.offsetWire() support similar effect as ArchWall Offset # - #if off: + # if off: # dvec2 = DraftVecUtils.scaleTo(dvec,off) # wire = DraftGeomUtils.offsetWire(wire,dvec2) - wNe2 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=curWidth, - offsetMode=None, - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) - wNe1 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=curWidth, - offsetMode="BasewireMode", - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) + wNe2 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=curWidth, + offsetMode=None, + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) + wNe1 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=curWidth, + offsetMode="BasewireMode", + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) elif curAligns == "Center": if layers: - totalwidth=sum([abs(l) for l in layers]) + totalwidth = sum([abs(l) for l in layers]) curWidth = abs(layers[i]) - off = totalwidth/2-layeroffset + off = totalwidth / 2 - layeroffset d1 = Vector(dvec).multiply(off) - wNe1 = DraftGeomUtils.offsetWire(wire, d1, - wireNedge=True) + wNe1 = DraftGeomUtils.offsetWire(wire, d1, wireNedge=True) layeroffset += curWidth - off = totalwidth/2-layeroffset + off = totalwidth / 2 - layeroffset d1 = Vector(dvec).multiply(off) - wNe2 = DraftGeomUtils.offsetWire(wire, d1, - wireNedge=True) + wNe2 = DraftGeomUtils.offsetWire(wire, d1, wireNedge=True) else: dvec.multiply(width) - wNe2 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=widths, - offsetMode=None, - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) - wNe1 = DraftGeomUtils.offsetWire(wire, dvec, - bind=False, - occ=False, - widthList=widths, - offsetMode="BasewireMode", - alignList=aligns, - normal=normal, - basewireOffset=offsets, - wireNedge=True) + wNe2 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=widths, + offsetMode=None, + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) + wNe1 = DraftGeomUtils.offsetWire( + wire, + dvec, + bind=False, + occ=False, + widthList=widths, + offsetMode="BasewireMode", + alignList=aligns, + normal=normal, + basewireOffset=offsets, + wireNedge=True, + ) w2 = wNe2[0] w1 = wNe1[0] face = DraftGeomUtils.bind(w1, w2, per_segment=True) @@ -1212,15 +1476,15 @@ class _Wall(ArchComponent.Component): baseface.append(face) # The above make Refine methods below (in else) useless, regardless removeSpitters yet to be improved for cases do not work well - ''' Whether layers or not, all baseface.append(face) ''' + """ Whether layers or not, all baseface.append(face) """ else: baseface = [face] - ''' Whether layers or not, all baseface = [face] ''' + """ Whether layers or not, all baseface = [face] """ if baseface: - base,placement = self.rebase(baseface) + base, placement = self.rebase(baseface) # Build Wall if there is no obj.Base or even obj.Base is not valid else: @@ -1230,32 +1494,33 @@ class _Wall(ArchComponent.Component): base = [] for l in layers: if l > 0: - l2 = length/2 or 0.5 - w1 = -totalwidth/2 + offset + l2 = length / 2 or 0.5 + w1 = -totalwidth / 2 + offset w2 = w1 + l - v1 = Vector(-l2,w1,0) - v2 = Vector(l2,w1,0) - v3 = Vector(l2,w2,0) - v4 = Vector(-l2,w2,0) - base.append(Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))) + v1 = Vector(-l2, w1, 0) + v2 = Vector(l2, w1, 0) + v3 = Vector(l2, w2, 0) + v4 = Vector(-l2, w2, 0) + base.append(Part.Face(Part.makePolygon([v1, v2, v3, v4, v1]))) offset += abs(l) 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) - base = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) + 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) + base = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) placement = FreeCAD.Placement() if base and placement: normal.normalize() extrusion = normal.multiply(height) if placement.Rotation.Angle > 0: extrusion = placement.inverse().Rotation.multVec(extrusion) - return (base,extrusion,placement) + return (base, extrusion, placement) return None + class _ViewProviderWall(ArchComponent.ViewProviderComponent): """The view provider for the wall object. @@ -1265,8 +1530,8 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): The view provider to turn into a wall view provider. """ - def __init__(self,vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + def __init__(self, vobj): + ArchComponent.ViewProviderComponent.__init__(self, vobj) vobj.ShapeColor = ArchCommands.getDefaultColor("Wall") def getIcon(self): @@ -1282,14 +1547,15 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): """ import Arch_rc - if hasattr(self,"Object"): + + if hasattr(self, "Object"): if self.Object.CloneOf: return ":/icons/Arch_Wall_Clone.svg" elif (not self.Object.Base) and self.Object.Additions: return ":/icons/Arch_Wall_Tree_Assembly.svg" return ":/icons/Arch_Wall_Tree.svg" - def attach(self,vobj): + def attach(self, vobj): """Add display modes' data to the coin scenegraph. Add each display mode as a coin node, whose parent is this view @@ -1305,14 +1571,15 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): self.Object = vobj.Object from pivy import coin + tex = coin.SoTexture2() - image = Draft.loadTexture(Draft.svgpatterns()['simple'][1], 128) + image = Draft.loadTexture(Draft.svgpatterns()["simple"][1], 128) if not image is None: tex.image = image texcoords = coin.SoTextureCoordinatePlane() s = params.get_param_arch("patternScale") - texcoords.directionS.setValue(s,0,0) - texcoords.directionT.setValue(0,s,0) + texcoords.directionS.setValue(s, 0, 0) + texcoords.directionT.setValue(0, s, 0) self.fcoords = coin.SoCoordinate3() self.fset = coin.SoIndexedFaceSet() sep = coin.SoSeparator() @@ -1321,9 +1588,9 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): sep.addChild(self.fcoords) sep.addChild(self.fset) vobj.RootNode.addChild(sep) - ArchComponent.ViewProviderComponent.attach(self,vobj) + ArchComponent.ViewProviderComponent.attach(self, vobj) - def updateData(self,obj,prop): + def updateData(self, obj, prop): """Method called when the host object has a property changed. If the host object's Placement, Shape, or Material has changed, and the @@ -1338,31 +1605,47 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): The name of the property that has changed. """ - if prop in ["Placement","Shape","Material"]: + if prop in ["Placement", "Shape", "Material"]: if obj.ViewObject.DisplayMode == "Footprint": obj.ViewObject.Proxy.setDisplayMode("Footprint") - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material and obj.Shape: - if hasattr(obj.Material,"Materials"): - activematerials = [obj.Material.Materials[i] for i in range(len(obj.Material.Materials)) if obj.Material.Thicknesses[i] >= 0] + if hasattr(obj.Material, "Materials"): + activematerials = [ + obj.Material.Materials[i] + for i in range(len(obj.Material.Materials)) + if obj.Material.Thicknesses[i] >= 0 + ] if len(activematerials) == len(obj.Shape.Solids): cols = [] - for i,mat in enumerate(activematerials): + for i, mat in enumerate(activematerials): c = obj.ViewObject.ShapeColor - c = (c[0],c[1],c[2],1.0-obj.ViewObject.Transparency/100.0) - if 'DiffuseColor' in mat.Material: - if "(" in mat.Material['DiffuseColor']: - c = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) - if 'Transparency' in mat.Material: - c = (c[0],c[1],c[2],1.0-float(mat.Material['Transparency'])) + c = (c[0], c[1], c[2], 1.0 - obj.ViewObject.Transparency / 100.0) + if "DiffuseColor" in mat.Material: + if "(" in mat.Material["DiffuseColor"]: + c = tuple( + [ + float(f) + for f in mat.Material["DiffuseColor"] + .strip("()") + .split(",") + ] + ) + if "Transparency" in mat.Material: + c = ( + c[0], + c[1], + c[2], + 1.0 - float(mat.Material["Transparency"]), + ) cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))]) obj.ViewObject.DiffuseColor = cols - ArchComponent.ViewProviderComponent.updateData(self,obj,prop) + ArchComponent.ViewProviderComponent.updateData(self, obj, prop) if len(obj.ViewObject.DiffuseColor) > 1: # force-reset colors if changed obj.ViewObject.DiffuseColor = obj.ViewObject.DiffuseColor - def getDisplayModes(self,vobj): + def getDisplayModes(self, vobj): """Define the display modes unique to the Arch Wall. Define mode Footprint, which only displays the footprint of the wall. @@ -1374,10 +1657,10 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): List containing the names of the new display modes. """ - modes = ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj)+["Footprint"] + modes = ArchComponent.ViewProviderComponent.getDisplayModes(self, vobj) + ["Footprint"] return modes - def setDisplayMode(self,mode): + def setDisplayMode(self, mode): """Method called when the display mode changes. Called when the display mode changes, this method can be used to set @@ -1403,7 +1686,7 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): self.fset.coordIndex.deleteValues(0) self.fcoords.point.deleteValues(0) if mode == "Footprint": - if hasattr(self,"Object"): + if hasattr(self, "Object"): faces = self.Object.Proxy.getFootprint(self.Object) if faces: verts = [] @@ -1412,35 +1695,35 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): for face in faces: tri = face.tessellate(1) for v in tri[0]: - verts.append([v.x,v.y,v.z]) + verts.append([v.x, v.y, v.z]) for f in tri[1]: - fdata.extend([f[0]+idx,f[1]+idx,f[2]+idx,-1]) + fdata.extend([f[0] + idx, f[1] + idx, f[2] + idx, -1]) idx += len(tri[0]) self.fcoords.point.setValues(verts) - self.fset.coordIndex.setValues(0,len(fdata),fdata) + self.fset.coordIndex.setValues(0, len(fdata), fdata) return "Wireframe" - return ArchComponent.ViewProviderComponent.setDisplayMode(self,mode) + return ArchComponent.ViewProviderComponent.setDisplayMode(self, mode) def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return super().contextMenuAddEdit(menu) - actionFlipDirection = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Wall_Tree.svg"), - translate("Arch", "Flip Direction"), - menu) - QtCore.QObject.connect(actionFlipDirection, - QtCore.SIGNAL("triggered()"), - self.flipDirection) + actionFlipDirection = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_Wall_Tree.svg"), translate("Arch", "Flip Direction"), menu + ) + QtCore.QObject.connect( + actionFlipDirection, QtCore.SIGNAL("triggered()"), self.flipDirection + ) menu.addAction(actionFlipDirection) super().contextMenuAddToggleSubcomponents(menu) def flipDirection(self): - if hasattr(self,"Object") and self.Object: + if hasattr(self, "Object") and self.Object: obj = self.Object if obj.Align == "Left": obj.Align = "Right" diff --git a/src/Mod/BIM/ArchWindow.py b/src/Mod/BIM/ArchWindow.py index f04fff0192..39c556e5c5 100644 --- a/src/Mod/BIM/ArchWindow.py +++ b/src/Mod/BIM/ArchWindow.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Window" +__title__ = "FreeCAD Window" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package ArchWindow # \ingroup ARCH @@ -57,26 +57,38 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctxt,txt): + def translate(ctxt, txt): return txt - def QT_TRANSLATE_NOOP(ctxt,txt): + + def QT_TRANSLATE_NOOP(ctxt, txt): return txt + # \endcond # presets -WindowPartTypes = ["Frame","Solid panel","Glass panel","Louvre"] -WindowOpeningModes = ["None","Arc 90","Arc 90 inv","Arc 45","Arc 45 inv","Arc 180", - "Arc 180 inv","Triangle","Triangle inv","Sliding","Sliding inv"] +WindowPartTypes = ["Frame", "Solid panel", "Glass panel", "Louvre"] +WindowOpeningModes = [ + "None", + "Arc 90", + "Arc 90 inv", + "Arc 45", + "Arc 45 inv", + "Arc 180", + "Arc 180 inv", + "Triangle", + "Triangle inv", + "Sliding", + "Sliding inv", +] WindowPresets = ArchWindowPresets.WindowPresets -def recolorize(attr): # names is [docname,objname] - +def recolorize(attr): # names is [docname,objname] """Recolorizes an object or a [documentname,objectname] list This basically calls the Proxy.colorize(obj) methods of objects that have one.""" - if isinstance(attr,list): + if isinstance(attr, list): if attr[0] in FreeCAD.listDocuments(): doc = FreeCAD.getDocument(attr[0]) obj = doc.getObject(attr[1]) @@ -84,20 +96,18 @@ def recolorize(attr): # names is [docname,objname] if obj.ViewObject: if obj.ViewObject.Proxy: obj.ViewObject.Proxy.colorize(obj) - elif hasattr(attr,"ViewObject") and attr.ViewObject: + elif hasattr(attr, "ViewObject") and attr.ViewObject: obj = attr - if hasattr(obj.ViewObject,"Proxy") and hasattr(obj.ViewObject.Proxy,"colorize"): + if hasattr(obj.ViewObject, "Proxy") and hasattr(obj.ViewObject.Proxy, "colorize"): obj.ViewObject.Proxy.colorize(obj) - class _Window(ArchComponent.Component): - "The Window object" - def __init__(self,obj): + def __init__(self, obj): - ArchComponent.Component.__init__(self,obj) + ArchComponent.Component.__init__(self, obj) self.Type = "Window" self.setProperties(obj) obj.IfcType = "Window" @@ -106,81 +116,205 @@ class _Window(ArchComponent.Component): # Add features in the SketchArch External Add-on self.addSketchArchFeatures(obj) - def addSketchArchFeatures(self,obj,linkObj=None,mode=None): - ''' - To add features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) - - import ArchSketchObject module, and - - set properties that are common to ArchObjects (including Links) and ArchSketch - to support the additional features + def addSketchArchFeatures(self, obj, linkObj=None, mode=None): + """ + To add features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) + - import ArchSketchObject module, and + - set properties that are common to ArchObjects (including Links) and ArchSketch + to support the additional features - To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install - ''' + To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install + """ try: import ArchSketchObject + ArchSketchObject.ArchSketch.setPropertiesLinkCommon(self, obj, linkObj, mode) except: pass - def setProperties(self,obj,mode=None): + def setProperties(self, obj, mode=None): lp = obj.PropertiesList if not "Hosts" in lp: - obj.addProperty("App::PropertyLinkList","Hosts","Window",QT_TRANSLATE_NOOP("App::Property","The objects that host this window"), locked=True) + obj.addProperty( + "App::PropertyLinkList", + "Hosts", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The objects that host this window"), + locked=True, + ) if not "WindowParts" in lp: - obj.addProperty("App::PropertyStringList","WindowParts","Window",QT_TRANSLATE_NOOP("App::Property","The components of this window"), locked=True) - obj.setEditorMode("WindowParts",2) + obj.addProperty( + "App::PropertyStringList", + "WindowParts", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The components of this window"), + locked=True, + ) + obj.setEditorMode("WindowParts", 2) if not "HoleDepth" in lp: - obj.addProperty("App::PropertyLength","HoleDepth","Window",QT_TRANSLATE_NOOP("App::Property","The depth of the hole that this window makes in its host object. If 0, the value will be calculated automatically."), locked=True) + obj.addProperty( + "App::PropertyLength", + "HoleDepth", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", + "The depth of the hole that this window makes in its host object. If 0, the value will be calculated automatically.", + ), + locked=True, + ) if not "Subvolume" in lp: - obj.addProperty("App::PropertyLink","Subvolume","Window",QT_TRANSLATE_NOOP("App::Property","An optional object that defines a volume to be subtracted from hosts of this window"), locked=True) + obj.addProperty( + "App::PropertyLink", + "Subvolume", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", + "An optional object that defines a volume to be subtracted from hosts of this window", + ), + locked=True, + ) if not "Width" in lp: - obj.addProperty("App::PropertyLength","Width","Window",QT_TRANSLATE_NOOP("App::Property","The width of this window"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Width", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The width of this window"), + locked=True, + ) if not "Height" in lp: - obj.addProperty("App::PropertyLength","Height","Window",QT_TRANSLATE_NOOP("App::Property","The height of this window"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Height", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The height of this window"), + locked=True, + ) if not "Normal" in lp: - obj.addProperty("App::PropertyVector","Normal","Window",QT_TRANSLATE_NOOP("App::Property","The normal direction of this window"), locked=True) + obj.addProperty( + "App::PropertyVector", + "Normal", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The normal direction of this window"), + locked=True, + ) # Automatic Normal Reverse if not "AutoNormalReversed" in lp: - obj.addProperty("App::PropertyBool","AutoNormalReversed","Window",QT_TRANSLATE_NOOP("App::Property","When normal direction is in auto mode (0,0,0), use reversed normal direction of the Base Sketch, i.e. -z."), locked=True) - if mode == 'ODR': + obj.addProperty( + "App::PropertyBool", + "AutoNormalReversed", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", + "When normal direction is in auto mode (0,0,0), use reversed normal direction of the Base Sketch, i.e. -z.", + ), + locked=True, + ) + if mode == "ODR": obj.AutoNormalReversed = False # To maintain auto extrusion behaviour before introduction of this flag, this remains False if this is called by onDocumentRestored() elif mode == None: obj.AutoNormalReversed = True # To enable new extrusion behaviour which is consistent with Window intuitive creation tool after introduction of this flag, this is set True. if not "Preset" in lp: - obj.addProperty("App::PropertyInteger","Preset","Window",QT_TRANSLATE_NOOP("App::Property","The preset number this window is based on"), locked=True) - obj.setEditorMode("Preset",2) + obj.addProperty( + "App::PropertyInteger", + "Preset", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The preset number this window is based on"), + locked=True, + ) + obj.setEditorMode("Preset", 2) if not "Frame" in lp: - obj.addProperty("App::PropertyLength","Frame","Window",QT_TRANSLATE_NOOP("App::Property", - "The frame depth of this window. Measured from front face to back face horizontally (i.e. perpendicular to the window elevation plane)."), - locked=True) + obj.addProperty( + "App::PropertyLength", + "Frame", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", + "The frame depth of this window. Measured from front face to back face horizontally (i.e. perpendicular to the window elevation plane).", + ), + locked=True, + ) if not "Offset" in lp: - obj.addProperty("App::PropertyLength","Offset","Window",QT_TRANSLATE_NOOP("App::Property","The offset size of this window"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Offset", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The offset size of this window"), + locked=True, + ) if not "Area" in lp: - obj.addProperty("App::PropertyArea","Area","Window",QT_TRANSLATE_NOOP("App::Property","The area of this window"), locked=True) + obj.addProperty( + "App::PropertyArea", + "Area", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The area of this window"), + locked=True, + ) if not "LouvreWidth" in lp: - obj.addProperty("App::PropertyLength","LouvreWidth","Window",QT_TRANSLATE_NOOP("App::Property","The width of louvre elements"), locked=True) + obj.addProperty( + "App::PropertyLength", + "LouvreWidth", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The width of louvre elements"), + locked=True, + ) if not "LouvreSpacing" in lp: - obj.addProperty("App::PropertyLength","LouvreSpacing","Window",QT_TRANSLATE_NOOP("App::Property","The space between louvre elements"), locked=True) + obj.addProperty( + "App::PropertyLength", + "LouvreSpacing", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The space between louvre elements"), + locked=True, + ) if not "Opening" in lp: - obj.addProperty("App::PropertyPercent","Opening","Window",QT_TRANSLATE_NOOP("App::Property","Opens the subcomponents that have a hinge defined"), locked=True) + obj.addProperty( + "App::PropertyPercent", + "Opening", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", "Opens the subcomponents that have a hinge defined" + ), + locked=True, + ) if not "HoleWire" in lp: - obj.addProperty("App::PropertyInteger","HoleWire","Window",QT_TRANSLATE_NOOP("App::Property","The number of the wire that defines the hole. If 0, the value will be calculated automatically"), locked=True) + obj.addProperty( + "App::PropertyInteger", + "HoleWire", + "Window", + QT_TRANSLATE_NOOP( + "App::Property", + "The number of the wire that defines the hole. If 0, the value will be calculated automatically", + ), + locked=True, + ) if not "SymbolPlan" in lp: - obj.addProperty("App::PropertyBool","SymbolPlan","Window",QT_TRANSLATE_NOOP("App::Property","Shows plan opening symbols if available"), locked=True) + obj.addProperty( + "App::PropertyBool", + "SymbolPlan", + "Window", + QT_TRANSLATE_NOOP("App::Property", "Shows plan opening symbols if available"), + locked=True, + ) if not "SymbolElevation" in lp: - obj.addProperty("App::PropertyBool","SymbolElevation","Window",QT_TRANSLATE_NOOP("App::Property","Show elevation opening symbols if available"), locked=True) - obj.setEditorMode("VerticalArea",2) - obj.setEditorMode("HorizontalArea",2) - obj.setEditorMode("PerimeterLength",2) + obj.addProperty( + "App::PropertyBool", + "SymbolElevation", + "Window", + QT_TRANSLATE_NOOP("App::Property", "Show elevation opening symbols if available"), + locked=True, + ) + obj.setEditorMode("VerticalArea", 2) + obj.setEditorMode("HorizontalArea", 2) + obj.setEditorMode("PerimeterLength", 2) # Sill change related properties self.setSillProperties(obj) def setSillProperties(self, orgObj, linkObj=None): - ''' Set properties which support Sill change. - Support both Arch Window and Link of Arch Window. - ''' + """Set properties which support Sill change. + Support both Arch Window and Link of Arch Window. + """ if linkObj: obj = linkObj @@ -191,89 +325,106 @@ class _Window(ArchComponent.Component): # 'Sill' support if not "Sill" in prop: - obj.addProperty("App::PropertyLength","Sill","Window",QT_TRANSLATE_NOOP("App::Property","The height of this window's sill"), locked=True) + obj.addProperty( + "App::PropertyLength", + "Sill", + "Window", + QT_TRANSLATE_NOOP("App::Property", "The height of this window's sill"), + locked=True, + ) # Link has no Proxy, so needs to use PropertyPythonObject - sillProp = ['baseSill','basePosZ','atthOffZ'] + sillProp = ["baseSill", "basePosZ", "atthOffZ"] for i in sillProp: if i not in prop: obj.addProperty("App::PropertyPythonObject", i) - def onDocumentRestored(self,obj): + def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self,obj) - self.setProperties(obj,mode='ODR') + ArchComponent.Component.onDocumentRestored(self, obj) + self.setProperties(obj, mode="ODR") # Add features in the SketchArch External Add-on - self.addSketchArchFeatures(obj, mode='ODR') + self.addSketchArchFeatures(obj, mode="ODR") # TODO 2025.6.27 : Seems Sill already triggered onChanged() upon document restored - NO need codes below in onDocumentRestored() # Need to restore 'initial' settings as corresponding codes in onChanged() does upon object creation - #self.baseSill = obj.Sill.Value - #self.basePos = obj.Base.Placement.Base - #self.atthOff = None - #if hasattr(obj, 'AttachmentOffsetXyzAndRotation'): + # self.baseSill = obj.Sill.Value + # self.basePos = obj.Base.Placement.Base + # self.atthOff = None + # if hasattr(obj, 'AttachmentOffsetXyzAndRotation'): # self.atthOff = obj.AttachmentOffsetXyzAndRotation.Base - def loads(self,state): + def loads(self, state): self.Type = "Window" - def onBeforeChange(self,obj,prop): + def onBeforeChange(self, obj, prop): - if prop in ["Base","WindowParts","Placement","HoleDepth","Height","Width","Hosts"]: - setattr(self,prop,getattr(obj,prop)) - if prop in ["Height","Width"] and obj.CloneOf is None: + if prop in ["Base", "WindowParts", "Placement", "HoleDepth", "Height", "Width", "Hosts"]: + setattr(self, prop, getattr(obj, prop)) + if prop in ["Height", "Width"] and obj.CloneOf is None: self.TouchOnShapeChange = True # touch hosts after next "Shape" change - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): - self.hideSubobjects(obj,prop) + self.hideSubobjects(obj, prop) if prop == "Sill": self.setSillProperties(obj) # Can't wait until onDocumentRestored self.onSillChanged(obj) elif not "Restore" in obj.State: - if prop in ["Base","WindowParts","Placement","HoleDepth","Height","Width","Hosts","Shape"]: + if prop in [ + "Base", + "WindowParts", + "Placement", + "HoleDepth", + "Height", + "Width", + "Hosts", + "Shape", + ]: # anti-recursive loops, bc the base sketch will touch the Placement all the time touchhosts = False if prop == "Shape": - if hasattr(self,"TouchOnShapeChange") and self.TouchOnShapeChange: + if hasattr(self, "TouchOnShapeChange") and self.TouchOnShapeChange: self.TouchOnShapeChange = False touchhosts = True - elif hasattr(self,prop) and getattr(self,prop) != getattr(obj,prop): + elif hasattr(self, prop) and getattr(self, prop) != getattr(obj, prop): touchhosts = True if touchhosts: hosts = self.Hosts if hasattr(self, "Hosts") else [] hosts += obj.Hosts if hasattr(obj, "Hosts") else [] - for host in set(hosts): # use set to remove duplicates + for host in set(hosts): # use set to remove duplicates # mark host to recompute so it can detect this object host.touch() - if prop in ["Width","Height","Frame"]: + if prop in ["Width", "Height", "Frame"]: if obj.Base: - if hasattr(obj.Base,"Constraints") and (prop in [c.Name for c in obj.Base.Constraints]): - val = getattr(obj,prop).Value + if hasattr(obj.Base, "Constraints") and ( + prop in [c.Name for c in obj.Base.Constraints] + ): + val = getattr(obj, prop).Value if val > 0: - obj.Base.setDatum(prop,val) + obj.Base.setDatum(prop, val) else: - ArchComponent.Component.onChanged(self,obj,prop) + ArchComponent.Component.onChanged(self, obj, prop) - - def buildShapes(self,obj): + def buildShapes(self, obj): import Part import DraftGeomUtils import math + self.sshapes = [] self.vshapes = [] shapes = [] rotdata = None - for i in range(int(len(obj.WindowParts)/5)): + for i in range(int(len(obj.WindowParts) / 5)): wires = [] hinge = None omode = None ssymbols = [] vsymbols = [] - wstr = obj.WindowParts[(i*5)+2].split(',') + wstr = obj.WindowParts[(i * 5) + 2].split(",") for s in wstr: if "Wire" in s: j = int(s[4:]) @@ -281,7 +432,7 @@ class _Window(ArchComponent.Component): if len(obj.Base.Shape.Wires) >= j: wires.append(obj.Base.Shape.Wires[j]) elif "Edge" in s: - hinge = int(s[4:])-1 + hinge = int(s[4:]) - 1 elif "Mode" in s: omode = int(s[4:]) if omode >= len(WindowOpeningModes): @@ -296,32 +447,38 @@ class _Window(ArchComponent.Component): wires.remove(ext) shape = Part.Face(ext) norm = None - if hasattr(obj,"Normal"): # TODO Any reason need this test? - if obj.Normal: # TODO v=Vector(0,0,0), if v: print('true') - true: It always return True? Why this test? + if hasattr(obj, "Normal"): # TODO Any reason need this test? + if ( + obj.Normal + ): # TODO v=Vector(0,0,0), if v: print('true') - true: It always return True? Why this test? if not DraftVecUtils.isNull(obj.Normal): norm = obj.Normal if not norm: if not obj.AutoNormalReversed: - norm = shape.normalAt(0,0) # TODO Should use Sketch's normal, to avoid possible difference in edge direction of various wires, for consistency? + norm = shape.normalAt( + 0, 0 + ) # TODO Should use Sketch's normal, to avoid possible difference in edge direction of various wires, for consistency? else: # elif obj.AutoNormalReversed: - norm = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1)) + norm = obj.Base.getGlobalPlacement().Rotation.multVec( + FreeCAD.Vector(0, 0, 1) + ) norm = norm.negative() if hinge and omode: opening = None - if hasattr(obj,"Opening"): + if hasattr(obj, "Opening"): if obj.Opening: - opening = obj.Opening/100.0 + opening = obj.Opening / 100.0 e = obj.Base.Shape.Edges[hinge] ev1 = e.Vertexes[0].Point ev2 = e.Vertexes[-1].Point # choose the one with lowest z to draw the symbol if ev2.z < ev1.z: - ev1,ev2 = ev2,ev1 + ev1, ev2 = ev2, ev1 # find the point most distant from the hinge p = None d = 0 for v in shape.Vertexes: - dist = v.Point.distanceToLine(ev1,ev2.sub(ev1)) + dist = v.Point.distanceToLine(ev1, ev2.sub(ev1)) if dist > d: d = dist p = v.Point @@ -329,120 +486,120 @@ class _Window(ArchComponent.Component): # bring that point to the level of ev1 if needed chord = p.sub(ev1) enorm = ev2.sub(ev1) - proj = DraftVecUtils.project(chord,enorm) + proj = DraftVecUtils.project(chord, enorm) v1 = ev1 if proj.Length > 0: - #chord = p.sub(ev1.add(proj)) - #p = v1.add(chord) + # chord = p.sub(ev1.add(proj)) + # p = v1.add(chord) p = p.sub(proj) chord = p.sub(ev1) # calculate symbols - v4 = p.add(DraftVecUtils.scale(enorm,0.5)) - if omode == 1: # Arc 90 - v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/4,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + v4 = p.add(DraftVecUtils.scale(enorm, 0.5)) + if omode == 1: # Arc 90 + v2 = v1.add(DraftVecUtils.rotate(chord, math.pi / 4, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, math.pi / 2, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),90*opening] - elif omode == 2: # Arc -90 - v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/4,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), 90 * opening] + elif omode == 2: # Arc -90 + v2 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 4, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 2, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),-90*opening] - elif omode == 3: # Arc 45 - v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/8,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,math.pi/4,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), -90 * opening] + elif omode == 3: # Arc 45 + v2 = v1.add(DraftVecUtils.rotate(chord, math.pi / 8, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, math.pi / 4, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),45*opening] - elif omode == 4: # Arc -45 - v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/8,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi/4,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), 45 * opening] + elif omode == 4: # Arc -45 + v2 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 8, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 4, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),-45*opening] - elif omode == 5: # Arc 180 - v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,math.pi,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), -45 * opening] + elif omode == 5: # Arc 180 + v2 = v1.add(DraftVecUtils.rotate(chord, math.pi / 2, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, math.pi, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),180*opening] - elif omode == 6: # Arc -180 - v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) - v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi,enorm)) - ssymbols.append(Part.Arc(p,v2,v3).toShape()) - ssymbols.append(Part.LineSegment(v3,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), 180 * opening] + elif omode == 6: # Arc -180 + v2 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 2, enorm)) + v3 = v1.add(DraftVecUtils.rotate(chord, -math.pi, enorm)) + ssymbols.append(Part.Arc(p, v2, v3).toShape()) + ssymbols.append(Part.LineSegment(v3, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [ev1,ev2.sub(ev1),-180*opening] - elif omode == 7: # tri - v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) - ssymbols.append(Part.LineSegment(p,v2).toShape()) - ssymbols.append(Part.LineSegment(v2,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [ev1, ev2.sub(ev1), -180 * opening] + elif omode == 7: # tri + v2 = v1.add(DraftVecUtils.rotate(chord, math.pi / 2, enorm)) + ssymbols.append(Part.LineSegment(p, v2).toShape()) + ssymbols.append(Part.LineSegment(v2, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),90*opening] - elif omode == 8: # -tri - v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) - ssymbols.append(Part.LineSegment(p,v2).toShape()) - ssymbols.append(Part.LineSegment(v2,v1).toShape()) - vsymbols.append(Part.LineSegment(v1,v4).toShape()) - vsymbols.append(Part.LineSegment(v4,ev2).toShape()) + rotdata = [v1, ev2.sub(ev1), 90 * opening] + elif omode == 8: # -tri + v2 = v1.add(DraftVecUtils.rotate(chord, -math.pi / 2, enorm)) + ssymbols.append(Part.LineSegment(p, v2).toShape()) + ssymbols.append(Part.LineSegment(v2, v1).toShape()) + vsymbols.append(Part.LineSegment(v1, v4).toShape()) + vsymbols.append(Part.LineSegment(v4, ev2).toShape()) if opening: - rotdata = [v1,ev2.sub(ev1),-90*opening] - elif omode == 9: # sliding + rotdata = [v1, ev2.sub(ev1), -90 * opening] + elif omode == 9: # sliding pass - elif omode == 10: # -sliding + elif omode == 10: # -sliding pass exv = FreeCAD.Vector() zov = FreeCAD.Vector() V = 0 - thk = obj.WindowParts[(i*5)+3] + thk = obj.WindowParts[(i * 5) + 3] if "+V" in thk: thk = thk[:-2] V = obj.Frame.Value thk = float(thk) + V if thk: - exv = DraftVecUtils.scaleTo(norm,thk) + exv = DraftVecUtils.scaleTo(norm, thk) shape = shape.extrude(exv) for w in wires: f = Part.Face(w) f = f.extrude(exv) shape = shape.cut(f) - if obj.WindowParts[(i*5)+4]: + if obj.WindowParts[(i * 5) + 4]: V = 0 - zof = obj.WindowParts[(i*5)+4] + zof = obj.WindowParts[(i * 5) + 4] if "+V" in zof: zof = zof[:-2] V = obj.Offset.Value zof = float(zof) + V if zof: - zov = DraftVecUtils.scaleTo(norm,zof) + zov = DraftVecUtils.scaleTo(norm, zof) shape.translate(zov) if hinge and omode and 0 < omode < 9: if DraftVecUtils.angle(chord, norm, enorm) < 0: - if omode%2 == 0: + if omode % 2 == 0: zov = zov.add(exv) else: - if omode%2 == 1: + if omode % 2 == 1: zov = zov.add(exv) for symb in ssymbols: symb.translate(zov) @@ -450,32 +607,36 @@ class _Window(ArchComponent.Component): symb.translate(zov) if rotdata: rotdata[0] = rotdata[0].add(zov) - if obj.WindowParts[(i*5)+1] == "Louvre": - if hasattr(obj,"LouvreWidth"): + if obj.WindowParts[(i * 5) + 1] == "Louvre": + if hasattr(obj, "LouvreWidth"): if obj.LouvreWidth and obj.LouvreSpacing: bb = shape.BoundBox bb.enlarge(10) - step = obj.LouvreWidth.Value+obj.LouvreSpacing.Value + step = obj.LouvreWidth.Value + obj.LouvreSpacing.Value if step < bb.ZLength: - box = Part.makeBox(bb.XLength,bb.YLength,obj.LouvreSpacing.Value) + box = Part.makeBox(bb.XLength, bb.YLength, obj.LouvreSpacing.Value) boxes = [] - for i in range(int(bb.ZLength/step)+1): + for i in range(int(bb.ZLength / step) + 1): b = box.copy() - b.translate(FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin+i*step)) + b.translate( + FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin + i * step) + ) boxes.append(b) self.boxes = Part.makeCompound(boxes) - #rot = obj.Base.Placement.Rotation - #self.boxes.rotate(self.boxes.BoundBox.Center,rot.Axis,math.degrees(rot.Angle)) - self.boxes.translate(shape.BoundBox.Center.sub(self.boxes.BoundBox.Center)) + # rot = obj.Base.Placement.Rotation + # self.boxes.rotate(self.boxes.BoundBox.Center,rot.Axis,math.degrees(rot.Angle)) + self.boxes.translate( + shape.BoundBox.Center.sub(self.boxes.BoundBox.Center) + ) shape = shape.cut(self.boxes) if rotdata: - shape.rotate(rotdata[0],rotdata[1],rotdata[2]) + shape.rotate(rotdata[0], rotdata[1], rotdata[2]) shapes.append(shape) self.sshapes.extend(ssymbols) self.vshapes.extend(vsymbols) return shapes - def execute(self,obj): + def execute(self, obj): if self.clone(obj): clonedProxy = obj.CloneOf.Proxy @@ -492,14 +653,15 @@ class _Window(ArchComponent.Component): import Part import DraftGeomUtils import math + pl = obj.Placement base = None self.sshapes = [] self.vshapes = [] if obj.Base: - if hasattr(obj,'Shape'): - if hasattr(obj,"WindowParts"): - if obj.WindowParts and (len(obj.WindowParts)%5 == 0): + if hasattr(obj, "Shape"): + if hasattr(obj, "WindowParts"): + if obj.WindowParts and (len(obj.WindowParts) % 5 == 0): shapes = self.buildShapes(obj) if shapes: base = Part.makeCompound(shapes) @@ -507,51 +669,52 @@ class _Window(ArchComponent.Component): if obj.Base.Shape.Solids: base = obj.Base.Shape.copy() # obj placement is already added by applyShape() below - #if not DraftGeomUtils.isNull(pl): + # if not DraftGeomUtils.isNull(pl): # base.Placement = base.Placement.multiply(pl) else: print("Arch: Bad formatting of window parts definitions") - base = self.processSubShapes(obj,base) + base = self.processSubShapes(obj, base) if base: if not base.isNull(): b = [] if self.sshapes: - if hasattr(obj,"SymbolPlan"): + if hasattr(obj, "SymbolPlan"): if obj.SymbolPlan: b.extend(self.sshapes) else: b.extend(self.sshapes) if self.vshapes: - if hasattr(obj,"SymbolElevation"): + if hasattr(obj, "SymbolElevation"): if obj.SymbolElevation: b.extend(self.vshapes) else: b.extend(self.vshapes) if b: - base = Part.makeCompound([base]+b) - #base = Part.makeCompound([base]+self.sshapes+self.vshapes) - self.applyShape(obj,base,pl,allowinvalid=True,allownosolid=True) + base = Part.makeCompound([base] + b) + # base = Part.makeCompound([base]+self.sshapes+self.vshapes) + self.applyShape(obj, base, pl, allowinvalid=True, allownosolid=True) obj.Placement = pl else: obj.Shape = Part.Shape() - if hasattr(obj,"Area"): + if hasattr(obj, "Area"): obj.Area = obj.Width.Value * obj.Height.Value self.executeSketchArchFeatures(obj) def executeSketchArchFeatures(self, obj, linkObj=None, index=None, linkElement=None): - ''' - To execute features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) - - import ArchSketchObject module, and - - execute features that are common to ArchObjects (including Links) and ArchSketch + """ + To execute features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) + - import ArchSketchObject module, and + - execute features that are common to ArchObjects (including Links) and ArchSketch - To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install - ''' + To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install + """ # To execute features in SketchArch External Add-on try: import ArchSketchObject # Why needed ? Should have try: addSketchArchFeatures() before ! Need 'per method' ? + # Execute SketchArch Feature - Intuitive Automatic Placement for Arch Windows/Doors, Equipment etc. # see https://forum.freecad.org/viewtopic.php?f=23&t=50802 ArchSketchObject.updateAttachmentOffset(obj, linkObj) @@ -559,11 +722,11 @@ class _Window(ArchComponent.Component): pass def appLinkExecute(self, obj, linkObj, index, linkElement): - ''' - Default Link Execute method() - - See https://forum.freecad.org/viewtopic.php?f=22&t=42184&start=10#p361124 - @realthunder added support to Links to run Linked Scripted Object's methods() - ''' + """ + Default Link Execute method() - + See https://forum.freecad.org/viewtopic.php?f=22&t=42184&start=10#p361124 + @realthunder added support to Links to run Linked Scripted Object's methods() + """ # Sill change support self.setSillProperties(obj, linkObj) @@ -584,10 +747,12 @@ class _Window(ArchComponent.Component): else: obj = orgObj - val = getattr(obj,'Sill').Value - if (getattr(obj, 'baseSill', None) is None and - getattr(obj, 'basePosZ', None) is None and - getattr(obj, 'atthOffZ', None) is None): # TODO Any cases only 1 or 2 are not None? + val = getattr(obj, "Sill").Value + if ( + getattr(obj, "baseSill", None) is None + and getattr(obj, "basePosZ", None) is None + and getattr(obj, "atthOffZ", None) is None + ): # TODO Any cases only 1 or 2 are not None? obj.baseSill = val # Not to change Base's Placement, would change all Clones and # Link's disposition unexpectedly to users, undesirable. @@ -595,18 +760,22 @@ class _Window(ArchComponent.Component): # self.basePos = obj.Base.Placement.Base obj.basePosZ = obj.Placement.Base.z obj.atthOffZ = None - if hasattr(obj, 'AttachmentOffsetXyzAndRotation'): + if hasattr(obj, "AttachmentOffsetXyzAndRotation"): obj.atthOffZ = obj.AttachmentOffsetXyzAndRotation.Base.z return import ArchSketchObject # Need to import per method + host = None if obj.Hosts: host = obj.Hosts[0] - if (hasattr(obj, 'AttachToAxisOrSketch') and - obj.AttachToAxisOrSketch == "Host" and - host and Draft.getType(host.Base) == "ArchSketch" and - hasattr(ArchSketchObject, 'updateAttachmentOffset')): + if ( + hasattr(obj, "AttachToAxisOrSketch") + and obj.AttachToAxisOrSketch == "Host" + and host + and Draft.getType(host.Base) == "ArchSketch" + and hasattr(ArchSketchObject, "updateAttachmentOffset") + ): SketchArch = True else: SketchArch = False @@ -616,13 +785,13 @@ class _Window(ArchComponent.Component): # a Wall or other Arch object at the moment). # # SketchArch or Not - if hasattr(obj, 'AttachmentOffsetXyzAndRotation'): + if hasattr(obj, "AttachmentOffsetXyzAndRotation"): objAttOff = obj.AttachmentOffsetXyzAndRotation objAttOff.Base.z = obj.atthOffZ + (obj.Sill.Value - obj.baseSill) obj.AttachmentOffsetXyzAndRotation = objAttOff if not SketchArch: # Not to change Base's Placement - #obj.Base.Placement.Base.z = self.basePos.z + (obj.Sill.Value - self.baseSill) + # obj.Base.Placement.Base.z = self.basePos.z + (obj.Sill.Value - self.baseSill) obj.Placement.Base.z = obj.basePosZ + (obj.Sill.Value - obj.baseSill) def getSubFace(self): @@ -630,22 +799,21 @@ class _Window(ArchComponent.Component): # creation of subface from HoleWire (getSubWire) raise NotImplementedError - def getSubVolume(self,obj,plac=None, host=None): - + def getSubVolume(self, obj, plac=None, host=None): "returns a subvolume for cutting in a base object" # check if this is a clone or not, setup orig if positive orig = None - if Draft.isClone(obj,"Window"): - if hasattr(obj,"CloneOf"): # TODO need to check this? + if Draft.isClone(obj, "Window"): + if hasattr(obj, "CloneOf"): # TODO need to check this? orig = obj.CloneOf # TODO Why always need tests e.g. hasattr(obj,"Subvolme"), hasattr(obj,"ClonOf") etc.? # check if we have a custom subvolume - if hasattr(obj,"Subvolume"): # TODO To support Links + if hasattr(obj, "Subvolume"): # TODO To support Links if obj.Subvolume: - if hasattr(obj.Subvolume,'Shape'): + if hasattr(obj.Subvolume, "Shape"): if not obj.Subvolume.Shape.isNull(): sh = obj.Subvolume.Shape.copy() pl = FreeCAD.Placement(sh.Placement) @@ -657,11 +825,13 @@ class _Window(ArchComponent.Component): # getting extrusion depth width = 0 - if hasattr(obj,"HoleDepth"): # if this is a clone, the original's HoleDepth is overridden if HoleDepth is set in the clone # TODO To support Links + if hasattr( + obj, "HoleDepth" + ): # if this is a clone, the original's HoleDepth is overridden if HoleDepth is set in the clone # TODO To support Links if obj.HoleDepth.Value: width = obj.HoleDepth.Value if not width: - if orig and hasattr(orig,"HoleDepth"): + if orig and hasattr(orig, "HoleDepth"): if orig.HoleDepth.Value: width = orig.HoleDepth.Value if not width: @@ -671,13 +841,16 @@ class _Window(ArchComponent.Component): # TODO Consider to turn below codes to getWidths/getSortedWidths() in ArchWall (below codes copied and modified from ArchWall) propSetUuid = host.Proxy.ArchSkPropSetPickedUuid widths = [] # [] or None are both False - if hasattr(host,"ArchSketchData") and host.ArchSketchData and Draft.getType(host.Base) == "ArchSketch": - if hasattr(host.Base, 'Proxy'): # TODO Any need to test ? - if hasattr(host.Base.Proxy, 'getWidths'): + if ( + hasattr(host, "ArchSketchData") + and host.ArchSketchData + and Draft.getType(host.Base) == "ArchSketch" + ): + if hasattr(host.Base, "Proxy"): # TODO Any need to test ? + if hasattr(host.Base.Proxy, "getWidths"): # Return a list of Width corresponding to indexes # of sorted edges of Sketch. - widths = host.Base.Proxy.getWidths(host.Base, - propSetUuid=propSetUuid) + widths = host.Base.Proxy.getWidths(host.Base, propSetUuid=propSetUuid) if not widths: if host.OverrideWidth: # TODO No need to test as in ArchWall if host.Base is Sketch and sortSketchWidth(), just need the max value @@ -689,9 +862,9 @@ class _Window(ArchComponent.Component): # Consider adding a variable to store the layer's # thickness as deduced, so the figure there could be # used directly without re-calculated here below. - if hasattr(host,"Material"): + if hasattr(host, "Material"): if host.Material: - if hasattr(host.Material,"Materials"): + if hasattr(host.Material, "Materials"): thicknesses = [abs(t) for t in host.Material.Thicknesses] totalThk = sum(thicknesses) # Append totalThk to widths, find max below @@ -703,28 +876,38 @@ class _Window(ArchComponent.Component): width += 100 elif obj.Base: # If host is not Wall b = obj.Base.Shape.BoundBox - width = max(b.XLength,b.YLength,b.ZLength) # TODO Fix this, the width would be too much in many cases - if not width: # TODO Should not happen, it means there is no Base (Sketch or another FC object) in Clone either - width = 1.1112 # some weird value to have little chance to overlap with an existing face + width = max( + b.XLength, b.YLength, b.ZLength + ) # TODO Fix this, the width would be too much in many cases + if ( + not width + ): # TODO Should not happen, it means there is no Base (Sketch or another FC object) in Clone either + width = ( + 1.1112 # some weird value to have little chance to overlap with an existing face + ) # setup base if orig: - base = orig.Base # always use original's base; clone's base should not be used to supersede original's base + base = ( + orig.Base + ) # always use original's base; clone's base should not be used to supersede original's base else: base = obj.Base # finding which wire to use to drill the hole f = None - if hasattr(obj,"HoleWire"): # if this is a clone, the original's HoleWire is overridden if HoleWire is set in the clone # TODO To support Links + if hasattr( + obj, "HoleWire" + ): # if this is a clone, the original's HoleWire is overridden if HoleWire is set in the clone # TODO To support Links if obj.HoleWire > 0: if obj.HoleWire <= len(base.Shape.Wires): - f = base.Shape.Wires[obj.HoleWire-1] + f = base.Shape.Wires[obj.HoleWire - 1] if not f: - if orig and hasattr(orig,"HoleDepth"): + if orig and hasattr(orig, "HoleDepth"): # check original's HoleWire if orig.HoleWire > 0: if orig.HoleWire <= len(base.Shape.Wires): - f = base.Shape.Wires[obj.HoleWire-1] + f = base.Shape.Wires[obj.HoleWire - 1] if not f: # finding biggest wire in the base shape max_length = 0 @@ -734,13 +917,14 @@ class _Window(ArchComponent.Component): f = w if f: import Part + f = Part.Face(f) - norm = f.normalAt(0,0) - if hasattr(obj,"Normal"): + norm = f.normalAt(0, 0) + if hasattr(obj, "Normal"): if obj.Normal: if not DraftVecUtils.isNull(obj.Normal): norm = obj.Normal - v1 = DraftVecUtils.scaleTo(norm,width) + v1 = DraftVecUtils.scaleTo(norm, width) f.translate(v1) v2 = v1.negative() v2 = Vector(v1).multiply(-2) @@ -752,29 +936,28 @@ class _Window(ArchComponent.Component): return f return None - def computeAreas(self,obj): + def computeAreas(self, obj): return - class _ViewProviderWindow(ArchComponent.ViewProviderComponent): - "A View Provider for the Window object" - def __init__(self,vobj): + def __init__(self, vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) + ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): import Arch_rc - if hasattr(self,"Object"): - if hasattr(self.Object,"CloneOf"): + + if hasattr(self, "Object"): + if hasattr(self.Object, "CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Window_Clone.svg" return ":/icons/Arch_Window_Tree.svg" - def updateData(self,obj,prop): + def updateData(self, obj, prop): if prop == "Shape": if obj.Base: @@ -785,9 +968,9 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): obj.ViewObject.update() self.colorize(obj) elif prop == "CloneOf": - if hasattr(obj,"CloneOf") and obj.CloneOf: + if hasattr(obj, "CloneOf") and obj.CloneOf: mat = None - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: mat = obj.Material if not mat: @@ -796,19 +979,19 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): obj.ViewObject.DiffuseColor = obj.CloneOf.ViewObject.DiffuseColor obj.ViewObject.update() - def onDelete(self,vobj,subelements): + def onDelete(self, vobj, subelements): for o in vobj.Object.Hosts: o.touch() return True - def onChanged(self,vobj,prop): + def onChanged(self, vobj, prop): if prop == "ShapeAppearance": self.colorize(vobj.Object) - ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) + ArchComponent.ViewProviderComponent.onChanged(self, vobj, prop) - def colorize(self,obj): + def colorize(self, obj): def _shapeAppearanceMaterialIsSame(sapp_mat1, sapp_mat2): for prop in ( @@ -850,25 +1033,25 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): sapp = [] for i in range(len(solids)): color = None - if obj.WindowParts and len(obj.WindowParts) > i*5: + if obj.WindowParts and len(obj.WindowParts) > i * 5: # WindowParts-based window - name = obj.WindowParts[(i*5)] - mtype = obj.WindowParts[(i*5)+1] - color = self.getSolidMaterial(obj,arch_mat,name,mtype) - elif obj.Base and hasattr(obj.Base,"Shape"): + name = obj.WindowParts[(i * 5)] + mtype = obj.WindowParts[(i * 5) + 1] + color = self.getSolidMaterial(obj, arch_mat, name, mtype) + elif obj.Base and hasattr(obj.Base, "Shape"): # Type-based window: obj.Base furnishes the window solids sol1 = self.getSolidSignature(solids[i]) # here we look for all the ways to retrieve a name for each # solid. Currently we look for similar solids in the - if hasattr(obj.Base,"Group"): + if hasattr(obj.Base, "Group"): for child in obj.Base.Group: - if hasattr(child,"Shape") and child.Shape and child.Shape.Solids: + if hasattr(child, "Shape") and child.Shape and child.Shape.Solids: sol2 = self.getSolidSignature(child.Shape) if sol1 == sol2: - color = self.getSolidMaterial(obj,arch_mat,child.Label) + color = self.getSolidMaterial(obj, arch_mat, child.Label) break if color is None: - typeidx = (i*5)+1 + typeidx = (i * 5) + 1 if typeidx < len(obj.WindowParts): typ = obj.WindowParts[typeidx] if typ == WindowPartTypes[2]: # "Glass panel" @@ -878,44 +1061,50 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): sapp_mat = base_sapp_mat else: # color is an RGBA tuple (0.0-1.0) - sapp_mat = FreeCAD.Material() # ShapeAppearance material with default v0.21 properties. - sapp_mat.DiffuseColor = color[:3] + (1.0, ) + sapp_mat = ( + FreeCAD.Material() + ) # ShapeAppearance material with default v0.21 properties. + sapp_mat.DiffuseColor = color[:3] + (1.0,) sapp_mat.Transparency = 1.0 - color[3] - sapp.extend((sapp_mat, ) * len(solids[i].Faces)) + sapp.extend((sapp_mat,) * len(solids[i].Faces)) if clone is not None: obj = clone if not _shapeAppearanceIsSame(obj.ViewObject.ShapeAppearance, sapp): obj.ViewObject.ShapeAppearance = sapp - def getSolidSignature(self,solid): - + def getSolidSignature(self, solid): """Returns a tuple defining as uniquely as possible a solid""" - return (solid.ShapeType,round(solid.Volume,3),round(solid.Area,3),round(solid.Length,3)) - - def getSolidMaterial(self,obj,arch_mat,name,mtype=None): + return ( + solid.ShapeType, + round(solid.Volume, 3), + round(solid.Area, 3), + round(solid.Length, 3), + ) + def getSolidMaterial(self, obj, arch_mat, name, mtype=None): """returns an RGBA tuple of floats (0.0 - 1.0)""" color = None - if arch_mat is not None and hasattr(arch_mat,"Materials") and arch_mat.Names: + if arch_mat is not None and hasattr(arch_mat, "Materials") and arch_mat.Names: mat = None if name in arch_mat.Names: mat = arch_mat.Materials[arch_mat.Names.index(name)] elif mtype is not None and (mtype in arch_mat.Names): mat = arch_mat.Materials[arch_mat.Names.index(mtype)] if mat: - if 'DiffuseColor' in mat.Material: - if "(" in mat.Material['DiffuseColor']: - color = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) - if color and ('Transparency' in mat.Material): - t = float(mat.Material['Transparency'])/100.0 - color = color[:3] + (1.0-t, ) + if "DiffuseColor" in mat.Material: + if "(" in mat.Material["DiffuseColor"]: + color = tuple( + [float(f) for f in mat.Material["DiffuseColor"].strip("()").split(",")] + ) + if color and ("Transparency" in mat.Material): + t = float(mat.Material["Transparency"]) / 100.0 + color = color[:3] + (1.0 - t,) return color def getHingeEdgeIndices(self): - """returns a list of hinge edge indices (0-based)""" # WindowParts example: @@ -928,7 +1117,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): for i in range(len(parts) // 5): for s in parts[(i * 5) + 2].split(","): if "Edge" in s: - idxs.append(int(s[4:]) - 1) # Edge indices in string are 1-based. + idxs.append(int(s[4:]) - 1) # Edge indices in string are 1-based. return idxs def setEdit(self, vobj, mode): @@ -937,7 +1126,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): taskd = _ArchWindowTaskPanel() taskd.obj = self.Object - self.sets = [vobj.DisplayMode,vobj.Transparency] + self.sets = [vobj.DisplayMode, vobj.Transparency] vobj.DisplayMode = "Shaded" vobj.Transparency = 80 if self.Object.Base: @@ -952,7 +1141,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): vobj.DisplayMode = self.sets[0] vobj.Transparency = self.sets[1] - vobj.DiffuseColor = vobj.DiffuseColor # reset face colors + vobj.DiffuseColor = vobj.DiffuseColor # reset face colors if self.Object.Base: self.Object.Base.ViewObject.hide() FreeCADGui.Control.closeDialog() @@ -960,7 +1149,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): def setupContextMenu(self, vobj, menu): - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return hingeIdxs = self.getHingeEdgeIndices() @@ -968,34 +1157,38 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): super().contextMenuAddEdit(menu) if len(hingeIdxs) > 0: - actionInvertOpening = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), - translate("Arch", "Invert Opening Direction"), - menu) - QtCore.QObject.connect(actionInvertOpening, - QtCore.SIGNAL("triggered()"), - self.invertOpening) + actionInvertOpening = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), + translate("Arch", "Invert Opening Direction"), + menu, + ) + QtCore.QObject.connect( + actionInvertOpening, QtCore.SIGNAL("triggered()"), self.invertOpening + ) menu.addAction(actionInvertOpening) if len(hingeIdxs) == 1: - actionInvertHinge = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), - translate("Arch", "Invert Hinge Position"), - menu) - QtCore.QObject.connect(actionInvertHinge, - QtCore.SIGNAL("triggered()"), - self.invertHinge) + actionInvertHinge = QtGui.QAction( + QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), + translate("Arch", "Invert Hinge Position"), + menu, + ) + QtCore.QObject.connect( + actionInvertHinge, QtCore.SIGNAL("triggered()"), self.invertHinge + ) menu.addAction(actionInvertHinge) super().contextMenuAddToggleSubcomponents(menu) def invertOpening(self): - """swaps the opening modes found in this window""" - pairs = [["Mode"+str(i),"Mode"+str(i+1)] for i in range(1,len(WindowOpeningModes),2)] + pairs = [ + ["Mode" + str(i), "Mode" + str(i + 1)] for i in range(1, len(WindowOpeningModes), 2) + ] self.invertPairs(pairs) def invertHinge(self): - """swaps the hinge edge of a single hinge edge window""" idxs = self.getHingeEdgeIndices() @@ -1008,7 +1201,7 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): sta = end end += len(wire.Edges) if sta <= idx < end: - new = idx + 2 # A rectangular wire is assumed. + new = idx + 2 # A rectangular wire is assumed. if not (sta <= new < end): new = idx - 2 break @@ -1019,33 +1212,32 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): # the same side of the wall self.invertOpening() - def invertPairs(self,pairs): - + def invertPairs(self, pairs): """scans the WindowParts of this window and swaps the two elements of each pair, if found""" - if hasattr(self,"Object"): + if hasattr(self, "Object"): windowparts = self.Object.WindowParts nparts = [] for part in windowparts: for pair in pairs: if pair[0] in part: - part = part.replace(pair[0],pair[1]) + part = part.replace(pair[0], pair[1]) break elif pair[1] in part: - part = part.replace(pair[1],pair[0]) + part = part.replace(pair[1], pair[0]) break nparts.append(part) if nparts != self.Object.WindowParts: self.Object.WindowParts = nparts FreeCAD.ActiveDocument.recompute() else: - FreeCAD.Console.PrintWarning(translate("Arch","This window has no defined opening")+"\n") - + FreeCAD.Console.PrintWarning( + translate("Arch", "This window has no defined opening") + "\n" + ) class _ArchWindowTaskPanel: - - '''The TaskPanel for Arch Windows''' + """The TaskPanel for Arch Windows""" def __init__(self): @@ -1057,13 +1249,13 @@ class _ArchWindowTaskPanel: self.title = QtGui.QLabel(self.baseform) self.grid.addWidget(self.title, 0, 0, 1, 7) self.basepanel = ArchComponent.ComponentTaskPanel() - self.form = [self.baseform,self.basepanel.baseform] + self.form = [self.baseform, self.basepanel.baseform] # base object self.tree = QtGui.QTreeWidget(self.baseform) self.grid.addWidget(self.tree, 1, 0, 1, 7) self.tree.setColumnCount(1) - self.tree.setMaximumSize(QtCore.QSize(500,24)) + self.tree.setMaximumSize(QtCore.QSize(500, 24)) self.tree.header().hide() # hole @@ -1092,20 +1284,20 @@ class _ArchWindowTaskPanel: self.addButton.setObjectName("addButton") self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) self.grid.addWidget(self.addButton, 4, 0, 1, 1) - self.addButton.setMaximumSize(QtCore.QSize(70,40)) + self.addButton.setMaximumSize(QtCore.QSize(70, 40)) self.editButton = QtGui.QPushButton(self.baseform) self.editButton.setObjectName("editButton") self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg")) self.grid.addWidget(self.editButton, 4, 2, 1, 3) - self.editButton.setMaximumSize(QtCore.QSize(60,40)) + self.editButton.setMaximumSize(QtCore.QSize(60, 40)) self.editButton.setEnabled(False) self.delButton = QtGui.QPushButton(self.baseform) self.delButton.setObjectName("delButton") self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg")) self.grid.addWidget(self.delButton, 4, 6, 1, 1) - self.delButton.setMaximumSize(QtCore.QSize(70,40)) + self.delButton.setMaximumSize(QtCore.QSize(70, 40)) self.delButton.setEnabled(False) # invert buttons @@ -1186,13 +1378,19 @@ class _ArchWindowTaskPanel: self.createButton.setVisible(False) QtCore.QObject.connect(self.holeButton, QtCore.SIGNAL("clicked()"), self.selectHole) - QtCore.QObject.connect(self.holeNumber, QtCore.SIGNAL("textEdited(QString)"), self.setHoleNumber) + QtCore.QObject.connect( + self.holeNumber, QtCore.SIGNAL("textEdited(QString)"), self.setHoleNumber + ) QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editElement) QtCore.QObject.connect(self.createButton, QtCore.SIGNAL("clicked()"), self.create) - QtCore.QObject.connect(self.comptree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check) - QtCore.QObject.connect(self.wiretree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.select) + QtCore.QObject.connect( + self.comptree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check + ) + QtCore.QObject.connect( + self.wiretree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.select + ) QtCore.QObject.connect(self.field6, QtCore.SIGNAL("clicked()"), self.addEdge) self.update() @@ -1210,17 +1408,18 @@ class _ArchWindowTaskPanel: return QtGui.QDialogButtonBox.Close - def check(self,wid,col): + def check(self, wid, col): self.editButton.setEnabled(True) self.delButton.setEnabled(True) - def select(self,wid,col): + def select(self, wid, col): FreeCADGui.Selection.clearSelection() - ws = '' + ws = "" for it in self.wiretree.selectedItems(): - if ws: ws += "," + if ws: + ws += "," ws += str(it.text(0)) w = int(str(it.text(0)[4:])) if self.obj: @@ -1229,39 +1428,48 @@ class _ArchWindowTaskPanel: for e in edges: for i in range(len(self.obj.Base.Shape.Edges)): if e.hashCode() == self.obj.Base.Shape.Edges[i].hashCode(): - FreeCADGui.Selection.addSelection(self.obj.Base,"Edge"+str(i+1)) + FreeCADGui.Selection.addSelection( + self.obj.Base, "Edge" + str(i + 1) + ) self.field3.setText(ws) def selectHole(self): - "takes a selected edge to determine current Hole Wire" s = FreeCADGui.Selection.getSelectionEx() if s and self.obj: if s[0].SubElementNames: if "Edge" in s[0].SubElementNames[0]: - for i,w in enumerate(self.obj.Base.Shape.Wires): + for i, w in enumerate(self.obj.Base.Shape.Wires): for e in w.Edges: if e.hashCode() == s[0].SubObjects[0].hashCode(): - self.holeNumber.setText(str(i+1)) - self.setHoleNumber(str(i+1)) + self.holeNumber.setText(str(i + 1)) + self.setHoleNumber(str(i + 1)) break - def setHoleNumber(self,val): - + def setHoleNumber(self, val): "sets the HoleWire obj property" if val.isdigit(): val = int(val) if self.obj: - if not hasattr(self.obj,"HoleWire"): - self.obj.addProperty("App::PropertyInteger","HoleWire","Arch",QT_TRANSLATE_NOOP("App::Property","The number of the wire that defines the hole. A value of 0 means automatic"), locked=True) + if not hasattr(self.obj, "HoleWire"): + self.obj.addProperty( + "App::PropertyInteger", + "HoleWire", + "Arch", + QT_TRANSLATE_NOOP( + "App::Property", + "The number of the wire that defines the hole. A value of 0 means automatic", + ), + locked=True, + ) self.obj.HoleWire = val - def getIcon(self,obj): + def getIcon(self, obj): - if hasattr(obj.ViewObject,"Proxy"): - if hasattr(obj.ViewObject.Proxy,"getIcon"): + if hasattr(obj.ViewObject, "Proxy"): + if hasattr(obj.ViewObject.Proxy, "getIcon"): return QtGui.QIcon(obj.ViewObject.Proxy.getIcon()) elif obj.isDerivedFrom("Sketcher::SketchObject"): return QtGui.QIcon(":/icons/Sketcher_Sketch.svg") @@ -1270,8 +1478,7 @@ class _ArchWindowTaskPanel: return QtGui.QIcon(":/icons/Part_3D_object.svg") def update(self): - - 'fills the tree widgets' + "fills the tree widgets" self.tree.clear() self.wiretree.clear() @@ -1279,22 +1486,22 @@ class _ArchWindowTaskPanel: if self.obj: if self.obj.Base: item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,self.obj.Base.Name) - item.setIcon(0,self.getIcon(self.obj.Base)) - if hasattr(self.obj.Base,'Shape'): + item.setText(0, self.obj.Base.Name) + item.setIcon(0, self.getIcon(self.obj.Base)) + if hasattr(self.obj.Base, "Shape"): i = 0 for w in self.obj.Base.Shape.Wires: if w.isClosed(): item = QtGui.QTreeWidgetItem(self.wiretree) - item.setText(0,"Wire" + str(i)) - item.setIcon(0,QtGui.QIcon(":/icons/Draft_Draft.svg")) + item.setText(0, "Wire" + str(i)) + item.setIcon(0, QtGui.QIcon(":/icons/Draft_Draft.svg")) i += 1 if self.obj.WindowParts: - for p in range(0,len(self.obj.WindowParts),5): + for p in range(0, len(self.obj.WindowParts), 5): item = QtGui.QTreeWidgetItem(self.comptree) - item.setText(0,self.obj.WindowParts[p]) + item.setText(0, self.obj.WindowParts[p]) item.setIcon(0, QtGui.QIcon(":/icons/Part_3D_object.svg")) - if hasattr(self.obj,"HoleWire"): + if hasattr(self.obj, "HoleWire"): self.holeNumber.setText(str(self.obj.HoleWire)) else: self.holeNumber.setText("0") @@ -1309,13 +1516,12 @@ class _ArchWindowTaskPanel: break def addElement(self): + "opens the component creation dialog" - 'opens the component creation dialog' - - self.field1.setText('') - self.field3.setText('') - self.field4.setText('') - self.field5.setText('') + self.field1.setText("") + self.field3.setText("") + self.field4.setText("") + self.field5.setText("") self.field6.setText(QtGui.QApplication.translate("Arch", "Get selected edge", None)) self.field7.setCurrentIndex(0) self.addp4.setChecked(False) @@ -1365,11 +1571,13 @@ class _ArchWindowTaskPanel: if self.obj: if comp in self.obj.WindowParts: ind = self.obj.WindowParts.index(comp) - self.field6.setText(QtGui.QApplication.translate("Arch", "Get selected edge", None)) + self.field6.setText( + QtGui.QApplication.translate("Arch", "Get selected edge", None) + ) self.field7.setCurrentIndex(0) for i in range(5): - f = getattr(self,"field"+str(i+1)) - t = self.obj.WindowParts[ind+i] + f = getattr(self, "field" + str(i + 1)) + t = self.obj.WindowParts[ind + i] if i == 1: # special behaviour for types if t in WindowPartTypes: @@ -1392,7 +1600,7 @@ class _ArchWindowTaskPanel: if wires: f.setText(",".join(wires)) - elif i in [3,4]: + elif i in [3, 4]: if "+V" in t: t = t[:-2] if i == 3: @@ -1404,34 +1612,36 @@ class _ArchWindowTaskPanel: self.addp4.setChecked(False) else: self.addp5.setChecked(False) - f.setProperty("text",FreeCAD.Units.Quantity(float(t),FreeCAD.Units.Length).UserString) + f.setProperty( + "text", + FreeCAD.Units.Quantity(float(t), FreeCAD.Units.Length).UserString, + ) else: f.setText(t) def create(self): - - 'adds a new component' + "adds a new component" # testing if fields are ok ok = True ar = [] for i in range(5): - if i == 1: # type (1) - n = getattr(self,"field"+str(i+1)).currentIndex() + if i == 1: # type (1) + n = getattr(self, "field" + str(i + 1)).currentIndex() if n in range(len(WindowPartTypes)): t = WindowPartTypes[n] else: # if type was not specified or is invalid, we set a default t = WindowPartTypes[0] - else: # name (0) - t = str(getattr(self,"field"+str(i+1)).property("text")) + else: # name (0) + t = str(getattr(self, "field" + str(i + 1)).property("text")) if t in WindowPartTypes: - t = t + "_" # avoiding part names similar to types + t = t + "_" # avoiding part names similar to types if t == "": - if not(i in [1,5]): + if not (i in [1, 5]): ok = False else: - if i > 2: # thickness (3), offset (4) + if i > 2: # thickness (3), offset (4) try: q = FreeCAD.Units.Quantity(t) t = str(q.Value) @@ -1441,7 +1651,7 @@ class _ArchWindowTaskPanel: if i == 4: if self.addp5.isChecked(): t += "+V" - except (ValueError,TypeError): + except (ValueError, TypeError): ok = False elif i == 2: # check additional opening parameters @@ -1449,7 +1659,7 @@ class _ArchWindowTaskPanel: n = self.field7.currentIndex() if (hinge.startswith("Edge")) and (n > 0): # remove accelerator added by Qt - hinge = hinge.replace("&","") + hinge = hinge.replace("&", "") t += "," + hinge + ",Mode" + str(n) ar.append(t) @@ -1459,13 +1669,13 @@ class _ArchWindowTaskPanel: if ar[0] in parts: b = parts.index(ar[0]) for i in range(5): - parts[b+i] = ar[i] + parts[b + i] = ar[i] else: parts.extend(ar) self.obj.WindowParts = parts self.update() else: - FreeCAD.Console.PrintWarning(translate("Arch", "Unable to create component")+"\n") + FreeCAD.Console.PrintWarning(translate("Arch", "Unable to create component") + "\n") self.newtitle.setVisible(False) self.new1.setVisible(False) @@ -1505,12 +1715,20 @@ class _ArchWindowTaskPanel: TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Window elements", None)) self.holeLabel.setText(QtGui.QApplication.translate("Arch", "Hole wire", None)) - self.holeNumber.setToolTip(QtGui.QApplication.translate("Arch", "The number of the wire that defines a hole in the host object. A value of zero will automatically adopt the largest wire", None)) + self.holeNumber.setToolTip( + QtGui.QApplication.translate( + "Arch", + "The number of the wire that defines a hole in the host object. A value of zero will automatically adopt the largest wire", + None, + ) + ) self.holeButton.setText(QtGui.QApplication.translate("Arch", "Pick Selected", None)) self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None)) self.addButton.setText(QtGui.QApplication.translate("Arch", "Add", None)) self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit", None)) - self.createButton.setText(QtGui.QApplication.translate("Arch", "Create/Update Component", None)) + self.createButton.setText( + QtGui.QApplication.translate("Arch", "Create/Update Component", None) + ) self.title.setText(QtGui.QApplication.translate("Arch", "Base 2D object", None)) self.wiretree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Wires", None)]) self.comptree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Components", None)]) @@ -1523,17 +1741,39 @@ class _ArchWindowTaskPanel: self.new6.setText(QtGui.QApplication.translate("Arch", "Hinge", None)) self.new7.setText(QtGui.QApplication.translate("Arch", "Opening mode", None)) self.addp4.setText(QtGui.QApplication.translate("Arch", "+ Frame property", None)) - self.addp4.setToolTip(QtGui.QApplication.translate("Arch", "If this is checked, the window's Frame property value will be added to the value entered here", None)) + self.addp4.setToolTip( + QtGui.QApplication.translate( + "Arch", + "If this is checked, the window's Frame property value will be added to the value entered here", + None, + ) + ) self.addp5.setText(QtGui.QApplication.translate("Arch", "+ Offset property", None)) - self.addp5.setToolTip(QtGui.QApplication.translate("Arch", "If this is checked, the window's Offset property value will be added to the value entered here", None)) + self.addp5.setToolTip( + QtGui.QApplication.translate( + "Arch", + "If this is checked, the window's Offset property value will be added to the value entered here", + None, + ) + ) self.field6.setText(QtGui.QApplication.translate("Arch", "Get Selected Edge", None)) - self.field6.setToolTip(QtGui.QApplication.translate("Arch", "Press to retrieve the selected edge", None)) - self.invertOpeningButton.setText(QtGui.QApplication.translate("Arch", "Invert Opening Direction", None)) - self.invertHingeButton.setText(QtGui.QApplication.translate("Arch", "Invert Hinge Position", None)) + self.field6.setToolTip( + QtGui.QApplication.translate("Arch", "Press to retrieve the selected edge", None) + ) + self.invertOpeningButton.setText( + QtGui.QApplication.translate("Arch", "Invert Opening Direction", None) + ) + self.invertHingeButton.setText( + QtGui.QApplication.translate("Arch", "Invert Hinge Position", None) + ) for i in range(len(WindowPartTypes)): - self.field2.setItemText(i, QtGui.QApplication.translate("Arch", WindowPartTypes[i], None)) + self.field2.setItemText( + i, QtGui.QApplication.translate("Arch", WindowPartTypes[i], None) + ) for i in range(len(WindowOpeningModes)): - self.field7.setItemText(i, QtGui.QApplication.translate("Arch", WindowOpeningModes[i], None)) + self.field7.setItemText( + i, QtGui.QApplication.translate("Arch", WindowOpeningModes[i], None) + ) def invertOpening(self): diff --git a/src/Mod/BIM/ArchWindowPresets.py b/src/Mod/BIM/ArchWindowPresets.py index 8008d73a7b..65e6fd4896 100644 --- a/src/Mod/BIM/ArchWindowPresets.py +++ b/src/Mod/BIM/ArchWindowPresets.py @@ -28,10 +28,23 @@ from FreeCAD import Vector from draftutils.translate import translate -WindowPresets = ["Fixed", "Open 1-pane", "Open 2-pane", "Sash 2-pane", "Sliding 2-pane", - "Simple door", "Glass door", "Sliding 4-pane", "Awning", "Opening only"] +WindowPresets = [ + "Fixed", + "Open 1-pane", + "Open 2-pane", + "Sash 2-pane", + "Sliding 2-pane", + "Simple door", + "Glass door", + "Sliding 4-pane", + "Awning", + "Opening only", +] -def makeWindowPreset(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2,placement=None,window_sill=None): + +def makeWindowPreset( + windowtype, width, height, h1, h2, h3, w1, w2, o1, o2, placement=None, window_sill=None +): """makeWindowPreset(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2,[placement]): makes a window object based on the given data. windowtype must be one of the names defined in Arch.WindowPresets""" @@ -40,10 +53,11 @@ def makeWindowPreset(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2,placement=None FreeCAD.Console.PrintError("No active document. Aborting\n") return - def makeSketch(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2): + def makeSketch(windowtype, width, height, h1, h2, h3, w1, w2, o1, o2): import Part import Sketcher + width = float(width) height = float(height) h1 = float(h1) @@ -55,479 +69,500 @@ def makeWindowPreset(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2,placement=None o2 = float(o2) # width, height, h1, h2, w1, w2 cannot be null (for now) # TODO allow these to be null (don't create the component if so) - if width*height*h1*h2*w1*w2 == 0: - FreeCAD.Console.PrintError("Width, Height, H1, H2, W1 and W2 parameters cannot be zero. Aborting\n") + if width * height * h1 * h2 * w1 * w2 == 0: + FreeCAD.Console.PrintError( + "Width, Height, H1, H2, W1 and W2 parameters cannot be zero. Aborting\n" + ) return # small spacing to avoid wrong auto-wires in sketch - tol = h1/10 + tol = h1 / 10 # glass size divider gla = 10 - s = FreeCAD.ActiveDocument.addObject('Sketcher::SketchObject','Sketch') - - def addRectangle(s,p1,p2,p3,p4): + s = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch") + def addRectangle(s, p1, p2, p3, p4): "adds a rectangle to the given sketch" idx = s.GeometryCount - s.addGeometry(Part.LineSegment(p1,p2)) - s.addGeometry(Part.LineSegment(p2,p3)) - s.addGeometry(Part.LineSegment(p3,p4)) - s.addGeometry(Part.LineSegment(p4,p1)) - s.addConstraint(Sketcher.Constraint('Coincident',idx,2,idx+1,1)) - s.addConstraint(Sketcher.Constraint('Coincident',idx+1,2,idx+2,1)) - s.addConstraint(Sketcher.Constraint('Coincident',idx+2,2,idx+3,1)) - s.addConstraint(Sketcher.Constraint('Coincident',idx+3,2,idx,1)) - s.addConstraint(Sketcher.Constraint('Horizontal',idx)) - s.addConstraint(Sketcher.Constraint('Horizontal',idx+2)) - s.addConstraint(Sketcher.Constraint('Vertical',idx+1)) - s.addConstraint(Sketcher.Constraint('Vertical',idx+3)) - - def addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8): + s.addGeometry(Part.LineSegment(p1, p2)) + s.addGeometry(Part.LineSegment(p2, p3)) + s.addGeometry(Part.LineSegment(p3, p4)) + s.addGeometry(Part.LineSegment(p4, p1)) + s.addConstraint(Sketcher.Constraint("Coincident", idx, 2, idx + 1, 1)) + s.addConstraint(Sketcher.Constraint("Coincident", idx + 1, 2, idx + 2, 1)) + s.addConstraint(Sketcher.Constraint("Coincident", idx + 2, 2, idx + 3, 1)) + s.addConstraint(Sketcher.Constraint("Coincident", idx + 3, 2, idx, 1)) + s.addConstraint(Sketcher.Constraint("Horizontal", idx)) + s.addConstraint(Sketcher.Constraint("Horizontal", idx + 2)) + s.addConstraint(Sketcher.Constraint("Vertical", idx + 1)) + s.addConstraint(Sketcher.Constraint("Vertical", idx + 3)) + def addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8): "adds two rectangles to the given sketch" - addRectangle(s,p1,p2,p3,p4) - addRectangle(s,p5,p6,p7,p8) - - def simpleFrame(s,width,height,h1,h2,tol): + addRectangle(s, p1, p2, p3, p4) + addRectangle(s, p5, p6, p7, p8) + def simpleFrame(s, width, height, h1, h2, tol): "creates a simple frame with constraints" - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,h1+h2,0) - p6 = Vector(width-(h1+h2),h1+h2,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',10,1,6,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,1,6,1,tol)) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, h1 + h2, 0) + p6 = Vector(width - (h1 + h2), h1 + h2, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 10, 1, 6, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 1, 6, 1, tol)) if h2 == h1: - s.renameConstraint(39,'Frame5') - s.renameConstraint(40,'Frame6') - s.renameConstraint(42,'Frame7') - s.renameConstraint(41,'Frame8') + s.renameConstraint(39, "Frame5") + s.renameConstraint(40, "Frame6") + s.renameConstraint(42, "Frame7") + s.renameConstraint(41, "Frame8") - def outerFrame(s,width,height,h1,w1,o1): + def outerFrame(s, width, height, h1, w1, o1): - p1 = Vector(0,0,0) - p2 = Vector(width,0,0) - p3 = Vector(width,height,0) - p4 = Vector(0,height,0) - p5 = Vector(h1,h1,0) - p6 = Vector(width-h1,h1,0) - p7 = Vector(width-h1,height-h1,0) - p8 = Vector(h1,height-h1,0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceY',1,height)) #16 - s.addConstraint(Sketcher.Constraint('DistanceX',0,width)) #17 - s.renameConstraint(16, 'Height') - s.renameConstraint(17, 'Width') - s.addConstraint(Sketcher.Constraint('DistanceY',6,2,2,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceX',2,2,6,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,2,0,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceY',0,2,4,2,h1)) - s.renameConstraint(18, 'Frame1') - s.renameConstraint(19, 'Frame2') - s.renameConstraint(20, 'Frame3') - s.renameConstraint(21, 'Frame4') - s.addConstraint(Sketcher.Constraint('Coincident',0,1,-1,1)) - return ["OuterFrame","Frame","Wire0,Wire1",str(w1-w2)+"+V","0.00+V"] + p1 = Vector(0, 0, 0) + p2 = Vector(width, 0, 0) + p3 = Vector(width, height, 0) + p4 = Vector(0, height, 0) + p5 = Vector(h1, h1, 0) + p6 = Vector(width - h1, h1, 0) + p7 = Vector(width - h1, height - h1, 0) + p8 = Vector(h1, height - h1, 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceY", 1, height)) # 16 + s.addConstraint(Sketcher.Constraint("DistanceX", 0, width)) # 17 + s.renameConstraint(16, "Height") + s.renameConstraint(17, "Width") + s.addConstraint(Sketcher.Constraint("DistanceY", 6, 2, 2, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceX", 2, 2, 6, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 2, 0, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceY", 0, 2, 4, 2, h1)) + s.renameConstraint(18, "Frame1") + s.renameConstraint(19, "Frame2") + s.renameConstraint(20, "Frame3") + s.renameConstraint(21, "Frame4") + s.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) + return ["OuterFrame", "Frame", "Wire0,Wire1", str(w1 - w2) + "+V", "0.00+V"] - def doorFrame(s,width,height,h1,w1,o1): + def doorFrame(s, width, height, h1, w1, o1): - p1 = Vector(0,0,0) - p2 = Vector(width,0,0) - p3 = Vector(width,height,0) - p4 = Vector(0,height,0) - p5 = Vector(h1,0,0) - p6 = Vector(width-h1,0,0) - p7 = Vector(width-h1,height-h1,0) - p8 = Vector(h1,height-h1,0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceY',1,height)) #16 - s.addConstraint(Sketcher.Constraint('DistanceX',0,width)) #17 - s.renameConstraint(16, 'Height') - s.renameConstraint(17, 'Width') - s.addConstraint(Sketcher.Constraint('DistanceY',6,2,2,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceX',2,2,6,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,2,0,2,h1)) - s.addConstraint(Sketcher.Constraint('DistanceY',0,2,4,2,0.0)) - s.addConstraint(Sketcher.Constraint('Coincident',0,1,-1,1)) - s.renameConstraint(18, 'Frame1') - s.renameConstraint(19, 'Frame2') - s.renameConstraint(20, 'Frame3') - return ["OuterFrame","Frame","Wire0,Wire1",str(w1-w2)+"+V","0.00+V"] + p1 = Vector(0, 0, 0) + p2 = Vector(width, 0, 0) + p3 = Vector(width, height, 0) + p4 = Vector(0, height, 0) + p5 = Vector(h1, 0, 0) + p6 = Vector(width - h1, 0, 0) + p7 = Vector(width - h1, height - h1, 0) + p8 = Vector(h1, height - h1, 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceY", 1, height)) # 16 + s.addConstraint(Sketcher.Constraint("DistanceX", 0, width)) # 17 + s.renameConstraint(16, "Height") + s.renameConstraint(17, "Width") + s.addConstraint(Sketcher.Constraint("DistanceY", 6, 2, 2, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceX", 2, 2, 6, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 2, 0, 2, h1)) + s.addConstraint(Sketcher.Constraint("DistanceY", 0, 2, 4, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) + s.renameConstraint(18, "Frame1") + s.renameConstraint(19, "Frame2") + s.renameConstraint(20, "Frame3") + return ["OuterFrame", "Frame", "Wire0,Wire1", str(w1 - w2) + "+V", "0.00+V"] if windowtype == "Fixed": - wp = outerFrame(s,width,height,h1,w1,o1) - wp.extend(["Glass","Glass panel","Wire1",str(w1/gla),str(w1/2)+"+V"]) + wp = outerFrame(s, width, height, h1, w1, o1) + wp.extend(["Glass", "Glass panel", "Wire1", str(w1 / gla), str(w1 / 2) + "+V"]) elif windowtype == "Open 1-pane": - wp = outerFrame(s,width,height,h1,w1,o1) - simpleFrame(s,width,height,h1,h2,tol) + wp = outerFrame(s, width, height, h1, w1, o1) + simpleFrame(s, width, height, h1, h2, tol) fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["InnerFrame","Frame","Wire2,Wire3,Edge8,Mode1",fw,str(o2)+"+V"]) - wp.extend(["InnerGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) + wp.extend(["InnerFrame", "Frame", "Wire2,Wire3,Edge8,Mode1", fw, str(o2) + "+V"]) + wp.extend( + ["InnerGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Open 2-pane": - wp = outerFrame(s,width,height,h1,w1,o1) - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector((width/2)-tol,h1+tol,0) - p3 = Vector((width/2)-tol,height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,h1+h2,0) - p6 = Vector((width/2)-h2,h1+h2,0) - p7 = Vector((width/2)-h2,height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector((width/2)+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector((width/2)+tol,height-(h1+tol),0) - p5 = Vector((width/2)+h2,h1+h2,0) - p6 = Vector(width-(h1+h2),h1+h2,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector((width/2)+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',16,1,20,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('Equal',22,14)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,2,16,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,1,18,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',9,1,19,2,tol)) - s.addConstraint(Sketcher.Constraint('PointOnObject',13,2,22)) - s.addConstraint(Sketcher.Constraint('PointOnObject',20,1,12)) + wp = outerFrame(s, width, height, h1, w1, o1) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector((width / 2) - tol, h1 + tol, 0) + p3 = Vector((width / 2) - tol, height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, h1 + h2, 0) + p6 = Vector((width / 2) - h2, h1 + h2, 0) + p7 = Vector((width / 2) - h2, height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector((width / 2) + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector((width / 2) + tol, height - (h1 + tol), 0) + p5 = Vector((width / 2) + h2, h1 + h2, 0) + p6 = Vector(width - (h1 + h2), h1 + h2, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector((width / 2) + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 16, 1, 20, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("Equal", 22, 14)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 2, 16, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 1, 18, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 9, 1, 19, 2, tol)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 13, 2, 22)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 20, 1, 12)) if h1 == h2: - s.renameConstraint(55,'Frame5') - s.renameConstraint(56,'Frame6') - s.renameConstraint(57,'Frame7') - s.renameConstraint(58,'Frame8') - s.renameConstraint(59,'Frame9') - s.renameConstraint(60,'Frame10') + s.renameConstraint(55, "Frame5") + s.renameConstraint(56, "Frame6") + s.renameConstraint(57, "Frame7") + s.renameConstraint(58, "Frame8") + s.renameConstraint(59, "Frame9") + s.renameConstraint(60, "Frame10") fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["LeftFrame","Frame","Wire2,Wire3,Edge8,Mode1",fw,str(o2)+"+V"]) - wp.extend(["LeftGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) - wp.extend(["RightFrame","Frame","Wire4,Wire5,Edge6,Mode2",fw,str(o2)+"+V"]) - wp.extend(["RightGlass","Glass panel","Wire5",str(w2/gla),str(o2+w2/2)+"+V"]) + wp.extend(["LeftFrame", "Frame", "Wire2,Wire3,Edge8,Mode1", fw, str(o2) + "+V"]) + wp.extend(["LeftGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"]) + wp.extend(["RightFrame", "Frame", "Wire4,Wire5,Edge6,Mode2", fw, str(o2) + "+V"]) + wp.extend( + ["RightGlass", "Glass panel", "Wire5", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Sash 2-pane": - wp = outerFrame(s,width,height,h1,w1,o1) - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),(height/2)-tol,0) - p4 = Vector(h1+tol,(height/2)-tol,0) - p5 = Vector(h1+h2,h1+h2,0) - p6 = Vector(width-(h1+h2),h1+h2,0) - p7 = Vector(width-(h1+h2),(height/2)-h2,0) - p8 = Vector(h1+h2,(height/2)-h2,0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector(h1+tol,(height/2)+tol,0) - p2 = Vector(width-(h1+tol),(height/2)+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,(height/2)+h2,0) - p6 = Vector(width-(h1+h2),(height/2)+h2,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',16,2,20,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,2,14,2,-h2)) - s.addConstraint(Sketcher.Constraint('Equal',23,15)) - s.addConstraint(Sketcher.Constraint('DistanceX',12,1,20,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceX',13,2,20,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,1,16,1,tol)) - s.addConstraint(Sketcher.Constraint('PointOnObject',9,2,17)) - s.addConstraint(Sketcher.Constraint('PointOnObject',16,1,11)) + wp = outerFrame(s, width, height, h1, w1, o1) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), (height / 2) - tol, 0) + p4 = Vector(h1 + tol, (height / 2) - tol, 0) + p5 = Vector(h1 + h2, h1 + h2, 0) + p6 = Vector(width - (h1 + h2), h1 + h2, 0) + p7 = Vector(width - (h1 + h2), (height / 2) - h2, 0) + p8 = Vector(h1 + h2, (height / 2) - h2, 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector(h1 + tol, (height / 2) + tol, 0) + p2 = Vector(width - (h1 + tol), (height / 2) + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, (height / 2) + h2, 0) + p6 = Vector(width - (h1 + h2), (height / 2) + h2, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 16, 2, 20, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 2, 14, 2, -h2)) + s.addConstraint(Sketcher.Constraint("Equal", 23, 15)) + s.addConstraint(Sketcher.Constraint("DistanceX", 12, 1, 20, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceX", 13, 2, 20, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 1, 16, 1, tol)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 9, 2, 17)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 16, 1, 11)) if h1 == h2: - s.renameConstraint(55,'Frame5') - s.renameConstraint(56,'Frame6') - s.renameConstraint(57,'Frame7') - s.renameConstraint(58,'Frame8') - s.renameConstraint(59,'Frame9') - s.renameConstraint(60,'F10') - s.setExpression('.Constraints.F10','-.Constraints.Frame5') + s.renameConstraint(55, "Frame5") + s.renameConstraint(56, "Frame6") + s.renameConstraint(57, "Frame7") + s.renameConstraint(58, "Frame8") + s.renameConstraint(59, "Frame9") + s.renameConstraint(60, "F10") + s.setExpression(".Constraints.F10", "-.Constraints.Frame5") fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["LowerFrame","Frame","Wire2,Wire3",fw,str(o2+w2)+"+V"]) - wp.extend(["LowerGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2+w2/2)+"+V"]) - wp.extend(["UpperFrame","Frame","Wire4,Wire5",fw,str(o2)+"+V"]) - wp.extend(["UpperGlass","Glass panel","Wire5",str(w2/gla),str(o2+w2/2)+"+V"]) + wp.extend(["LowerFrame", "Frame", "Wire2,Wire3", fw, str(o2 + w2) + "+V"]) + wp.extend( + ["LowerGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 + w2 / 2) + "+V"] + ) + wp.extend(["UpperFrame", "Frame", "Wire4,Wire5", fw, str(o2) + "+V"]) + wp.extend( + ["UpperGlass", "Glass panel", "Wire5", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Sliding 2-pane": - wp = outerFrame(s,width,height,h1,w1,o1) - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector((width/2)-tol,h1+tol,0) - p3 = Vector((width/2)-tol,height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,h1+h2,0) - p6 = Vector((width/2)-h2,h1+h2,0) - p7 = Vector((width/2)-h2,height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector((width/2)+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector((width/2)+tol,height-(h1+tol),0) - p5 = Vector((width/2)+h2,h1+h2,0) - p6 = Vector(width-(h1+h2),h1+h2,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector((width/2)+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',16,1,20,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('Equal',22,14)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,2,16,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,1,18,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',6,1,18,1,-tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',9,1,19,2,tol)) - s.addConstraint(Sketcher.Constraint('PointOnObject',13,2,22)) - s.addConstraint(Sketcher.Constraint('PointOnObject',12,2,20)) + wp = outerFrame(s, width, height, h1, w1, o1) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector((width / 2) - tol, h1 + tol, 0) + p3 = Vector((width / 2) - tol, height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, h1 + h2, 0) + p6 = Vector((width / 2) - h2, h1 + h2, 0) + p7 = Vector((width / 2) - h2, height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector((width / 2) + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector((width / 2) + tol, height - (h1 + tol), 0) + p5 = Vector((width / 2) + h2, h1 + h2, 0) + p6 = Vector(width - (h1 + h2), h1 + h2, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector((width / 2) + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 16, 1, 20, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("Equal", 22, 14)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 2, 16, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 1, 18, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 6, 1, 18, 1, -tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 9, 1, 19, 2, tol)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 13, 2, 22)) + s.addConstraint(Sketcher.Constraint("PointOnObject", 12, 2, 20)) if h1 == h2: - s.renameConstraint(55,'Frame5') - s.renameConstraint(56,'Frame6') - s.renameConstraint(57,'Frame7') - s.renameConstraint(58,'Frame8') - s.renameConstraint(59,'Frame9') - s.renameConstraint(60,'Frame10') + s.renameConstraint(55, "Frame5") + s.renameConstraint(56, "Frame6") + s.renameConstraint(57, "Frame7") + s.renameConstraint(58, "Frame8") + s.renameConstraint(59, "Frame9") + s.renameConstraint(60, "Frame10") fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["LeftFrame","Frame","Wire2,Wire3",fw,str(o2)+"+V"]) - wp.extend(["LeftGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) - wp.extend(["RightFrame","Frame","Wire4,Wire5",fw,str(o2+w2)+"+V"]) - wp.extend(["RightGlass","Glass panel","Wire5",str(w2/gla),str(o2+w2+w2/2)+"+V"]) + wp.extend(["LeftFrame", "Frame", "Wire2,Wire3", fw, str(o2) + "+V"]) + wp.extend(["LeftGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"]) + wp.extend(["RightFrame", "Frame", "Wire4,Wire5", fw, str(o2 + w2) + "+V"]) + wp.extend( + ["RightGlass", "Glass panel", "Wire5", str(w2 / gla), str(o2 + w2 + w2 / 2) + "+V"] + ) elif windowtype == "Sliding 4-pane": - wp = outerFrame(s,width,height,h1,w1,o1) - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector(width/4-tol,h1+tol,0) - p3 = Vector(width/4-tol,height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,h1+h2,0) - p6 = Vector(width/4-h2,h1+h2,0) - p7 = Vector(width/4-h2,height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector(width/4+tol,h1+tol,0) - p2 = Vector(width/2-tol,h1+tol,0) - p3 = Vector(width/2-tol,height-(h1+tol),0) - p4 = Vector(width/4+tol,height-(h1+tol),0) - p5 = Vector(width/4+h2,h1+h2,0) - p6 = Vector(width/2-h2,h1+h2,0) - p7 = Vector(width/2-h2,height-(h1+h2),0) - p8 = Vector(width/4+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector(width/2+tol,h1+tol,0) - p2 = Vector(width*3/4-tol,h1+tol,0) - p3 = Vector(width*3/4-tol,height-(h1+tol),0) - p4 = Vector(width/2+tol,height-(h1+tol),0) - p5 = Vector(width/2+h2,h1+h2,0) - p6 = Vector(width*3/4-h2,h1+h2,0) - p7 = Vector(width*3/4-h2,height-(h1+h2),0) - p8 = Vector(width/2+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - p1 = Vector(width*3/4+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector(width*3/4+tol,height-(h1+tol),0) - p5 = Vector(width*3/4+h2,h1+h2,0) - p6 = Vector(width-(h1+h2),h1+h2,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector(width*3/4+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',8,2,16,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',17,1,27,2,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',24,2,32,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',32,2,4,2,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,2,6,2,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',17,2,26,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',25,2,34,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,2,16,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',9,2,18,2,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',16,2,24,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceY',24,2,32,1,0.0)) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',13,2,9,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',13,2,9,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',16,1,20,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',16,1,20,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',21,2,17,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',24,1,28,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',24,1,28,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',29,2,25,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',29,2,25,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',32,1,36,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',32,1,36,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',37,2,33,2,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',37,2,33,2,h2)) - s.addConstraint(Sketcher.Constraint('Equal',14,22)) - s.addConstraint(Sketcher.Constraint('Equal',22,30)) - s.addConstraint(Sketcher.Constraint('Equal',30,38)) + wp = outerFrame(s, width, height, h1, w1, o1) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector(width / 4 - tol, h1 + tol, 0) + p3 = Vector(width / 4 - tol, height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, h1 + h2, 0) + p6 = Vector(width / 4 - h2, h1 + h2, 0) + p7 = Vector(width / 4 - h2, height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector(width / 4 + tol, h1 + tol, 0) + p2 = Vector(width / 2 - tol, h1 + tol, 0) + p3 = Vector(width / 2 - tol, height - (h1 + tol), 0) + p4 = Vector(width / 4 + tol, height - (h1 + tol), 0) + p5 = Vector(width / 4 + h2, h1 + h2, 0) + p6 = Vector(width / 2 - h2, h1 + h2, 0) + p7 = Vector(width / 2 - h2, height - (h1 + h2), 0) + p8 = Vector(width / 4 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector(width / 2 + tol, h1 + tol, 0) + p2 = Vector(width * 3 / 4 - tol, h1 + tol, 0) + p3 = Vector(width * 3 / 4 - tol, height - (h1 + tol), 0) + p4 = Vector(width / 2 + tol, height - (h1 + tol), 0) + p5 = Vector(width / 2 + h2, h1 + h2, 0) + p6 = Vector(width * 3 / 4 - h2, h1 + h2, 0) + p7 = Vector(width * 3 / 4 - h2, height - (h1 + h2), 0) + p8 = Vector(width / 2 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + p1 = Vector(width * 3 / 4 + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector(width * 3 / 4 + tol, height - (h1 + tol), 0) + p5 = Vector(width * 3 / 4 + h2, h1 + h2, 0) + p6 = Vector(width - (h1 + h2), h1 + h2, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector(width * 3 / 4 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 2, 16, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 17, 1, 27, 2, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 24, 2, 32, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 32, 2, 4, 2, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 2, 6, 2, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 17, 2, 26, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 25, 2, 34, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 2, 16, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 9, 2, 18, 2, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 16, 2, 24, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceY", 24, 2, 32, 1, 0.0)) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 13, 2, 9, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 13, 2, 9, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 16, 1, 20, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 16, 1, 20, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 21, 2, 17, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 24, 1, 28, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 24, 1, 28, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 29, 2, 25, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 29, 2, 25, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 32, 1, 36, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 32, 1, 36, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 37, 2, 33, 2, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 37, 2, 33, 2, h2)) + s.addConstraint(Sketcher.Constraint("Equal", 14, 22)) + s.addConstraint(Sketcher.Constraint("Equal", 22, 30)) + s.addConstraint(Sketcher.Constraint("Equal", 30, 38)) if h1 == h2: - s.renameConstraint(100,'Frame5') - s.renameConstraint(101,'Frame6') - s.renameConstraint(102,'Frame7') - s.renameConstraint(103,'Frame8') - s.renameConstraint(104,'Frame9') - s.renameConstraint(105,'Frame10') - s.renameConstraint(106,'Frame11') - s.renameConstraint(107,'Frame12') - s.renameConstraint(108,'Frame13') - s.renameConstraint(109,'Frame14') - s.renameConstraint(110,'Frame15') - s.renameConstraint(111,'Frame16') - s.renameConstraint(112,'Frame17') - s.renameConstraint(113,'Frame18') - s.renameConstraint(114,'Frame19') - s.renameConstraint(115,'Frame20') + s.renameConstraint(100, "Frame5") + s.renameConstraint(101, "Frame6") + s.renameConstraint(102, "Frame7") + s.renameConstraint(103, "Frame8") + s.renameConstraint(104, "Frame9") + s.renameConstraint(105, "Frame10") + s.renameConstraint(106, "Frame11") + s.renameConstraint(107, "Frame12") + s.renameConstraint(108, "Frame13") + s.renameConstraint(109, "Frame14") + s.renameConstraint(110, "Frame15") + s.renameConstraint(111, "Frame16") + s.renameConstraint(112, "Frame17") + s.renameConstraint(113, "Frame18") + s.renameConstraint(114, "Frame19") + s.renameConstraint(115, "Frame20") fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["LeftMostFrame","Frame","Wire2,Wire3",fw,str(o2)+"+V"]) - wp.extend(["LeftMostGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) - wp.extend(["LeftFrame","Frame","Wire4,Wire5",fw,str(o2+w2)+"+V"]) - wp.extend(["LeftGlass","Glass panel","Wire5",str(w2/gla),str(o2+w2+w2/2)+"+V"]) - wp.extend(["RightFrame","Frame","Wire6,Wire7",fw,str(o2+w2)+"+V"]) - wp.extend(["RightGlass","Glass panel","Wire7",str(w2/gla),str(o2+w2+w2/2)+"+V"]) - wp.extend(["RightMostFrame","Frame","Wire8,Wire9",fw,str(o2)+"+V"]) - wp.extend(["RightMostGlass","Glass panel","Wire9",str(w2/gla),str(o2+w2/2)+"+V"]) + wp.extend(["LeftMostFrame", "Frame", "Wire2,Wire3", fw, str(o2) + "+V"]) + wp.extend( + ["LeftMostGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) + wp.extend(["LeftFrame", "Frame", "Wire4,Wire5", fw, str(o2 + w2) + "+V"]) + wp.extend( + ["LeftGlass", "Glass panel", "Wire5", str(w2 / gla), str(o2 + w2 + w2 / 2) + "+V"] + ) + wp.extend(["RightFrame", "Frame", "Wire6,Wire7", fw, str(o2 + w2) + "+V"]) + wp.extend( + ["RightGlass", "Glass panel", "Wire7", str(w2 / gla), str(o2 + w2 + w2 / 2) + "+V"] + ) + wp.extend(["RightMostFrame", "Frame", "Wire8,Wire9", fw, str(o2) + "+V"]) + wp.extend( + ["RightMostGlass", "Glass panel", "Wire9", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Awning": - wp = outerFrame(s,width,height,h1,w1,o1) - simpleFrame(s,width,height,h1,h2,tol) + wp = outerFrame(s, width, height, h1, w1, o1) + simpleFrame(s, width, height, h1, h2, tol) fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["InnerFrame","Frame","Wire2,Wire3,Edge7,Mode1",fw,str(o2)+"+V"]) - wp.extend(["InnerGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) - + wp.extend(["InnerFrame", "Frame", "Wire2,Wire3,Edge7,Mode1", fw, str(o2) + "+V"]) + wp.extend( + ["InnerGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Simple door": - wp = doorFrame(s,width,height,h1,w1,o1) - wp.extend(["Door","Solid panel","Wire1,Edge8,Mode1",str(w2),str(o2)+"+V"]) + wp = doorFrame(s, width, height, h1, w1, o1) + wp.extend(["Door", "Solid panel", "Wire1,Edge8,Mode1", str(w2), str(o2) + "+V"]) elif windowtype == "Glass door": - wp = doorFrame(s,width,height,h1,w1,o1) - p1 = Vector(h1+tol,h1+tol,0) - p2 = Vector(width-(h1+tol),h1+tol,0) - p3 = Vector(width-(h1+tol),height-(h1+tol),0) - p4 = Vector(h1+tol,height-(h1+tol),0) - p5 = Vector(h1+h2,h1+h3,0) - p6 = Vector(width-(h1+h2),h1+h3,0) - p7 = Vector(width-(h1+h2),height-(h1+h2),0) - p8 = Vector(h1+h2,height-(h1+h2),0) - addFrame(s,p1,p2,p3,p4,p5,p6,p7,p8) - s.addConstraint(Sketcher.Constraint('DistanceX',8,1,12,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',8,1,12,1,h3)) - s.addConstraint(Sketcher.Constraint('DistanceX',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceY',14,1,10,1,h2)) - s.addConstraint(Sketcher.Constraint('DistanceX',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',4,1,8,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceX',10,1,6,1,tol)) - s.addConstraint(Sketcher.Constraint('DistanceY',10,1,6,1,tol)) + wp = doorFrame(s, width, height, h1, w1, o1) + p1 = Vector(h1 + tol, h1 + tol, 0) + p2 = Vector(width - (h1 + tol), h1 + tol, 0) + p3 = Vector(width - (h1 + tol), height - (h1 + tol), 0) + p4 = Vector(h1 + tol, height - (h1 + tol), 0) + p5 = Vector(h1 + h2, h1 + h3, 0) + p6 = Vector(width - (h1 + h2), h1 + h3, 0) + p7 = Vector(width - (h1 + h2), height - (h1 + h2), 0) + p8 = Vector(h1 + h2, height - (h1 + h2), 0) + addFrame(s, p1, p2, p3, p4, p5, p6, p7, p8) + s.addConstraint(Sketcher.Constraint("DistanceX", 8, 1, 12, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 8, 1, 12, 1, h3)) + s.addConstraint(Sketcher.Constraint("DistanceX", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceY", 14, 1, 10, 1, h2)) + s.addConstraint(Sketcher.Constraint("DistanceX", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 4, 1, 8, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceX", 10, 1, 6, 1, tol)) + s.addConstraint(Sketcher.Constraint("DistanceY", 10, 1, 6, 1, tol)) if h2 == h1: - s.renameConstraint(39,'Frame5') - s.renameConstraint(40,'Frame6') - s.renameConstraint(42,'Frame7') - s.renameConstraint(41,'Frame8') + s.renameConstraint(39, "Frame5") + s.renameConstraint(40, "Frame6") + s.renameConstraint(42, "Frame7") + s.renameConstraint(41, "Frame8") fw = str(w2) if w2 == w1: fw = "0.00+V" - wp.extend(["InnerFrame","Frame","Wire2,Wire3,Edge8,Mode1",fw,str(o2)+"+V"]) - wp.extend(["InnerGlass","Glass panel","Wire3",str(w2/gla),str(o2+w2/2)+"+V"]) + wp.extend(["InnerFrame", "Frame", "Wire2,Wire3,Edge8,Mode1", fw, str(o2) + "+V"]) + wp.extend( + ["InnerGlass", "Glass panel", "Wire3", str(w2 / gla), str(o2 + w2 / 2) + "+V"] + ) elif windowtype == "Opening only": wp = [] - p1 = Vector(0,0,0) - p2 = Vector(width,0,0) - p3 = Vector(width,height,0) - p4 = Vector(0,height,0) - addRectangle(s,p1,p2,p3,p4) - s.addConstraint(Sketcher.Constraint('Coincident',0,1,-1,1)) - s.addConstraint(Sketcher.Constraint('DistanceX',0,width)) - s.addConstraint(Sketcher.Constraint('DistanceY',1,height)) - s.renameConstraint(9,'Width') - s.renameConstraint(10,'Height') + p1 = Vector(0, 0, 0) + p2 = Vector(width, 0, 0) + p3 = Vector(width, height, 0) + p4 = Vector(0, height, 0) + addRectangle(s, p1, p2, p3, p4) + s.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) + s.addConstraint(Sketcher.Constraint("DistanceX", 0, width)) + s.addConstraint(Sketcher.Constraint("DistanceY", 1, height)) + s.renameConstraint(9, "Width") + s.renameConstraint(10, "Height") - return (s,wp) + return (s, wp) if windowtype in WindowPresets: import Arch - default = makeSketch(windowtype,width,height,h1,h2,h3,w1,w2,o1,o2) + + default = makeSketch(windowtype, width, height, h1, h2, h3, w1, w2, o1, o2) FreeCAD.ActiveDocument.recompute() if default: if placement: default[0].Placement = placement FreeCAD.ActiveDocument.recompute() - obj = Arch.makeWindow(default[0],width,height,default[1]) - obj.Preset = WindowPresets.index(windowtype)+1 + obj = Arch.makeWindow(default[0], width, height, default[1]) + obj.Preset = WindowPresets.index(windowtype) + 1 obj.Frame = w2 obj.Offset = o1 - obj.Placement = FreeCAD.Placement() # unable to find where this bug comes from... + obj.Placement = FreeCAD.Placement() # unable to find where this bug comes from... # If window_sill is provided, set obj.Sill (and trigger onChanged() # codes to track and adjust disposition of the Window object). # Do not set the property (to 0) and not trigger onChanged() # if otherwise. - #obj.Sill = window_sill if window_sill is not None else 0 + # obj.Sill = window_sill if window_sill is not None else 0 if window_sill is not None: obj.Sill = window_sill if "door" in windowtype.lower(): obj.IfcType = "Door" - obj.Label = translate("Arch","Door") + obj.Label = translate("Arch", "Door") elif "opening" in windowtype.lower(): obj.IfcType = "Opening Element" - obj.Label = translate("Arch","Opening") + obj.Label = translate("Arch", "Opening") FreeCAD.ActiveDocument.recompute() return obj diff --git a/src/Mod/BIM/BimSelect.py b/src/Mod/BIM/BimSelect.py index 2ad4efa33b..73333c78e2 100644 --- a/src/Mod/BIM/BimSelect.py +++ b/src/Mod/BIM/BimSelect.py @@ -57,10 +57,7 @@ class CyclicObjectSelector: return event = event_callback.getEvent() - if ( - event.getState() != coin.SoMouseButtonEvent.DOWN - or not self.selectableObjects - ): + if event.getState() != coin.SoMouseButtonEvent.DOWN or not self.selectableObjects: return pos = event.getPosition().getValue() diff --git a/src/Mod/BIM/BimStatus.py b/src/Mod/BIM/BimStatus.py index 577eeb3e84..590188c7c4 100644 --- a/src/Mod/BIM/BimStatus.py +++ b/src/Mod/BIM/BimStatus.py @@ -35,6 +35,7 @@ translate = FreeCAD.Qt.translate # Status bar buttons + def setStatusIcons(show=True): "shows or hides the BIM icons in the status bar" @@ -78,9 +79,7 @@ def setStatusIcons(show=True): form = FreeCADGui.PySideUic.loadUi(":/ui/dialogNudgeValue.ui") # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() - form.move( - mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center() - ) + form.move(mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center()) result = form.exec_() if not result: return @@ -122,31 +121,40 @@ def setStatusIcons(show=True): else: statuswidget = FreeCADGui.UiLoader().createWidget("Gui::ToolBar") statuswidget.setObjectName("BIMStatusWidget") - text = translate("BIMStatusWidget", "BIM status widget", - "A context menu action used to show or hide this toolbar widget") + text = translate( + "BIMStatusWidget", + "BIM status widget", + "A context menu action used to show or hide this toolbar widget", + ) statuswidget.setWindowTitle(text) - s = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/General").GetInt("ToolbarIconSize", 24) - statuswidget.setIconSize(QtCore.QSize(s,s)) + s = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/General").GetInt( + "ToolbarIconSize", 24 + ) + statuswidget.setIconSize(QtCore.QSize(s, s)) st.insertPermanentWidget(2, statuswidget) # report panels toggle button togglebutton = QtGui.QAction() togglemenu = QtGui.QMenu() - for t in ["Toggle", "Report view", "Python console", "Selection view", "Combo View"]: + for t in [ + "Toggle", + "Report view", + "Python console", + "Selection view", + "Combo View", + ]: a = QtGui.QAction(t) togglemenu.addAction(a) togglemenu.triggered.connect(toggleSaveSettings) togglebutton.setIcon(QtGui.QIcon(":/icons/BIM_TogglePanels.svg")) togglebutton.setText("") - togglebutton.setToolTip( - translate("BIM", "Toggle report panels on/off (Ctrl+0)") - ) + togglebutton.setToolTip(translate("BIM", "Toggle report panels on/off (Ctrl+0)")) togglebutton.setCheckable(True) rv = mw.findChild(QtGui.QWidget, "Python console") if rv and rv.isVisible(): togglebutton.setChecked(True) statuswidget.togglebutton = togglebutton - #togglebutton.setMenu(togglemenu) + # togglebutton.setMenu(togglemenu) togglebutton.triggered.connect(toggle) statuswidget.addAction(togglebutton) @@ -169,14 +177,12 @@ def setStatusIcons(show=True): # background toggle button bgbutton = QtGui.QAction() - #bwidth = bgbutton.fontMetrics().boundingRect("AAAA").width() - #bgbutton.setMaximumWidth(bwidth) + # bwidth = bgbutton.fontMetrics().boundingRect("AAAA").width() + # bgbutton.setMaximumWidth(bwidth) bgbutton.setIcon(QtGui.QIcon(":/icons/BIM_Background.svg")) bgbutton.setText("") bgbutton.setToolTip( - translate( - "BIM", "Toggle 3D view background between simple and gradient" - ) + translate("BIM", "Toggle 3D view background between simple and gradient") ) statuswidget.bgbutton = bgbutton bgbutton.triggered.connect(toggleBackground) @@ -192,8 +198,7 @@ def setStatusIcons(show=True): # nudge button nudge = QtGui.QPushButton(nudgeLabelsM[-1]) - nudge.setIcon( - QtGui.QIcon(":/icons/BIM_Nudge.svg")) + nudge.setIcon(QtGui.QIcon(":/icons/BIM_Nudge.svg")) nudge.setFlat(True) nudge.setToolTip( translate( diff --git a/src/Mod/BIM/Dice3DS/__init__.py b/src/Mod/BIM/Dice3DS/__init__.py index 35c2a63e13..0da33217c8 100644 --- a/src/Mod/BIM/Dice3DS/__init__.py +++ b/src/Mod/BIM/Dice3DS/__init__.py @@ -1,3 +1,3 @@ # __init__.py -__all__ = [ 'dom3ds', 'util' ] -version = (0,13) +__all__ = ["dom3ds", "util"] +version = (0, 13) diff --git a/src/Mod/BIM/Dice3DS/dom3ds.py b/src/Mod/BIM/Dice3DS/dom3ds.py index 38d6bbc126..2fbeaf1106 100644 --- a/src/Mod/BIM/Dice3DS/dom3ds.py +++ b/src/Mod/BIM/Dice3DS/dom3ds.py @@ -22,14 +22,18 @@ import numpy # Exceptions + class File3dsFormatError(Exception): "Exception in the 3DS file format" - def __init__(self,msg,tight=False): + + def __init__(self, msg, tight=False): self.msg = msg self.tight = tight + def __str__(self): return repr(self.msg) + class FBufError(Exception): "Exception when reading from FileLikeBuffers" @@ -37,28 +41,32 @@ class FBufError(Exception): # Numeric/string conversion functions, where the string uses # little-endian regardless of host byte order -def string_to_array(s,typ): + +def string_to_array(s, typ): """Convert data block from a 3DS file to a numpy array.""" - a = numpy.fromstring(s,typ) - if sys.byteorder == 'big': + a = numpy.fromstring(s, typ) + if sys.byteorder == "big": a.byteswap(True) return a + def array_to_string(a): """Convert a numpy array to data block for a 3DS file.""" - if sys.byteorder == 'big': + if sys.byteorder == "big": a = a.byteswap() return a.tostring() + def array_to_string_destructive(a): """Destructively convert a numpy array to data block for a 3DS file.""" - if sys.byteorder == 'big': + if sys.byteorder == "big": a.byteswap(True) return a.tostring() # Utility classes + class FileLikeBuffer(object): """Treat a range of a string as a quasi-file-like object. @@ -68,7 +76,7 @@ class FileLikeBuffer(object): """ - def __init__(self,s,start=0,end=None): + def __init__(self, s, start=0, end=None): self.buf = s self.start = start if end is None: @@ -77,57 +85,68 @@ class FileLikeBuffer(object): if end > len(s): raise FBufError("End of fbuf object set past end of buffer") self.end = end - assert 0 <= start <= self.end, (start,self.end) + assert 0 <= start <= self.end, (start, self.end) self.index = start + def tell(self): return self.index - def seek(self,pos): + + def seek(self, pos): self.index = pos if self.index < 0 or self.index > self.end: raise FBufError("Invalid position in fbuf") - def advance(self,n): + + def advance(self, n): self.index += n if self.index > self.end: raise FBufError("End of fbuf reached") - def read(self,length): - r = self.buf[self.index:self.index+length] + + def read(self, length): + r = self.buf[self.index : self.index + length] self.advance(length) return r - def read_fbuf(self,length): - r = FileLikeBuffer(self.buf,self.index,self.index+length) + + def read_fbuf(self, length): + r = FileLikeBuffer(self.buf, self.index, self.index + length) self.advance(length) return r + def read_to_nul(self): try: - i = self.buf.index('\0',self.index,self.end) + i = self.buf.index("\0", self.index, self.end) except IndexError: raise FBufError("End of fbuf reached in string") - r = self.buf[self.index:i] - self.index = i+1 + r = self.buf[self.index : i] + self.index = i + 1 return r + def read_rest(self): - r = self.buf[self.index:self.end] + r = self.buf[self.index : self.end] self.index = self.end return r + def room_for_chunks(self): - return self.index+6 <= self.end + return self.index + 6 <= self.end # Utility functions + def _decls_to_list(decls): - decllist = [ tuple(x.strip().split()) for x in decls.split(',') ] + decllist = [tuple(x.strip().split()) for x in decls.split(",")] return decllist + def _decls_to_vars(decls): if not decls: return () - vars = [ x.strip().split(None,1)[1] for x in decls.split(',') ] + vars = [x.strip().split(None, 1)[1] for x in decls.split(",")] return vars # Chunk metaclasses + class ChunkMetaclass(type): """Metaclass of chunk types. @@ -140,28 +159,28 @@ class ChunkMetaclass(type): chunk_taghash = {} chunk_labelhash = {} - def __new__(metatype,name,bases,clsdict): + def __new__(metatype, name, bases, clsdict): vars = [] - vars.extend(_decls_to_vars(clsdict.get('struct',''))) - vars.extend(_decls_to_vars(clsdict.get('single',''))) - vars.extend(_decls_to_vars(clsdict.get('multiple',''))) - if clsdict.get('freechunks',False): - vars.append('subchunks') - clsdict.setdefault('__slots__',[]).extend(vars) + vars.extend(_decls_to_vars(clsdict.get("struct", ""))) + vars.extend(_decls_to_vars(clsdict.get("single", ""))) + vars.extend(_decls_to_vars(clsdict.get("multiple", ""))) + if clsdict.get("freechunks", False): + vars.append("subchunks") + clsdict.setdefault("__slots__", []).extend(vars) - return type.__new__(metatype,name,bases,clsdict) + return type.__new__(metatype, name, bases, clsdict) - def __init__(self,name,bases,clsdict): - if hasattr(self,'struct'): + def __init__(self, name, bases, clsdict): + if hasattr(self, "struct"): self.struct_fields = _decls_to_list(self.struct) else: self.struct_fields = [] self.single_types = {} self.single_order = [] - if hasattr(self,'single'): + if hasattr(self, "single"): attrs = {} - for typ,attr in _decls_to_list(self.single): + for typ, attr in _decls_to_list(self.single): self.single_types[typ] = attr if attr not in attrs: attrs[attr] = 1 @@ -169,35 +188,33 @@ class ChunkMetaclass(type): self.multiple_types = {} self.multiple_order = [] - if hasattr(self,'multiple'): + if hasattr(self, "multiple"): attrs = {} - for typ,attr in _decls_to_list(self.multiple): + for typ, attr in _decls_to_list(self.multiple): self.multiple_types[typ] = attr if attr not in attrs: attrs[attr] = 1 self.multiple_order.append(attr) - if hasattr(self,'keyframe'): + if hasattr(self, "keyframe"): self.keyframe_fields = _decls_to_list(self.keyframe) - slots = ['frameno','flags','tension', - 'continuity','bias','ease_to','ease_from'] - for typ,attr in self.keyframe_fields: + slots = ["frameno", "flags", "tension", "continuity", "bias", "ease_to", "ease_from"] + for typ, attr in self.keyframe_fields: slots.append(attr) - kfdict = { '__slots__': slots } + kfdict = {"__slots__": slots} kfbases = (object,) kfname = "%s.Key" % name - self.Key = type(kfname,kfbases,kfdict) + self.Key = type(kfname, kfbases, kfdict) else: self.keyframe_fields = [] - if hasattr(self,'tag'): + if hasattr(self, "tag"): self.chunk_taghash[self.tag] = self self.chunk_labelhash[name] = self - if not hasattr(self,'label'): + if not hasattr(self, "label"): self.label = name - type.__init__(self,name,bases,clsdict) - + type.__init__(self, name, bases, clsdict) class UndefinedChunkMetaclass(ChunkMetaclass): @@ -208,18 +225,18 @@ class UndefinedChunkMetaclass(ChunkMetaclass): """ - def __new__(metatype,name,bases,clsdict): - if '__slots__' in clsdict: - if 'data' not in clsdict['__slots__']: - clsdict['__slots__'].append('data') + def __new__(metatype, name, bases, clsdict): + if "__slots__" in clsdict: + if "data" not in clsdict["__slots__"]: + clsdict["__slots__"].append("data") else: - clsdict['__slots__'] = ['data'] - return ChunkMetaclass.__new__(metatype,name,bases,clsdict) - + clsdict["__slots__"] = ["data"] + return ChunkMetaclass.__new__(metatype, name, bases, clsdict) # Base class of chunks. + class ChunkBase(object): """Base class of chunks. @@ -239,7 +256,7 @@ class ChunkBase(object): # Define constructor to accept keyword arguments, as a convenience # Init chunk arguments to nothing - def __init__(self,**kwargs): + def __init__(self, **kwargs): """Construct a chunk. This is an abstract base constructor; normally you'd @@ -252,39 +269,39 @@ class ChunkBase(object): if self.freechunks: self.subchunks = [] for v in self.single_order: - setattr(self,v,None) + setattr(self, v, None) for v in self.multiple_order: - setattr(self,v,[]) - for attr,value in kwargs.items(): - setattr(self,attr,value) + setattr(self, v, []) + for attr, value in kwargs.items(): + setattr(self, attr, value) # -------- SECTION 1: INITIALIZATION FROM CHUNK DATA -------- # These get a specific object from the fbuf @staticmethod - def get_byte(fbuf,flags): + def get_byte(fbuf, flags): return ord(fbuf.read(1)) @staticmethod - def get_short(fbuf,flags): - return struct.unpack("\n%s (0x%04X)\n' - % (cls.label,cls.label,cls.tag)) - flo.write('
\n') + flo.write( + '
\n%s (0x%04X)
\n' + % (cls.label, cls.label, cls.tag) + ) + flo.write("
\n") i = 0 - for typ,attr in cls.struct_fields: - flo.write('%s %s
\n' % (typ,attr)) + for typ, attr in cls.struct_fields: + flo.write("%s %s
\n" % (typ, attr)) i += 1 tagorder = [] d = {} - for typ,attr in cls.single_types.items(): + for typ, attr in cls.single_types.items(): if attr not in d: d[attr] = [] d[attr].append(typ) @@ -501,12 +515,11 @@ class ChunkBase(object): typs = d[attr] for typ in typs: tagorder.append(typ) - flo.write('%s %s
\n' - % (typ,typ,attr)) + flo.write('%s %s
\n' % (typ, typ, attr)) i += 1 d = {} - for typ,attr in cls.multiple_types.items(): + for typ, attr in cls.multiple_types.items(): if attr not in d: d[attr] = [] d[attr].append(typ) @@ -514,155 +527,181 @@ class ChunkBase(object): typs = d[attr] for typ in typs: tagorder.append(typ) - flo.write('%s[] %s
\n' - % (typ,typ,attr)) + flo.write('%s[] %s
\n' % (typ, typ, attr)) i += 1 if not i: - flo.write('No fields
\n') + flo.write("No fields
\n") - flo.write(' 
\n') + flo.write(" \n") done[cls.label] = 1 for typ in tagorder: - type(cls).chunk_labelhash[typ].document_html(flo,done) + type(cls).chunk_labelhash[typ].document_html(flo, done) document_html = classmethod(document_html) # Chunks we don't know anything about, maybe not even the tag + class UndefinedChunk(ChunkBase): __metaclass__ = UndefinedChunkMetaclass - def read(self,fbuf,flags): + + def read(self, fbuf, flags): self.data = fbuf.read_rest() - def dump(self,flo,indent,flags): - self.dump_header(flo,flags) + + def dump(self, flo, indent, flags): + self.dump_header(flo, flags) + def write(self): return self.data class UnknownChunk(UndefinedChunk): - __slots__ = ['tag'] + __slots__ = ["tag"] label = "UNKNOWN" - def __init__(self,tag): + + def __init__(self, tag): super().__init__(tag=tag) class ErrorChunk(UndefinedChunk): - __slots__ = ['intended_tag','intended_label','error_msg'] + __slots__ = ["intended_tag", "intended_label", "error_msg"] label = "ERROR" tag = 0xEEEE - def __init__(self,intended_tag=None,intended_label=None,error_msg=None): - super().__init__(intended_tag=intended_tag,intended_label=intended_label,error_msg=error_msg) - def dump(self,flo,indent,flags): - super(ErrorChunk,self).dump(flo,indent,flags) + + def __init__(self, intended_tag=None, intended_label=None, error_msg=None): + super().__init__( + intended_tag=intended_tag, intended_label=intended_label, error_msg=error_msg + ) + + def dump(self, flo, indent, flags): + super(ErrorChunk, self).dump(flo, indent, flags) if self.intended_tag is not None: - flo.write("%s intended_tag = 0x%04X\n" - % (indent,self.intended_tag)) + flo.write("%s intended_tag = 0x%04X\n" % (indent, self.intended_tag)) if self.intended_label is not None: - self.out_attr("intended_label",flo,indent+" ",flags) + self.out_attr("intended_label", flo, indent + " ", flags) if self.error_msg is not None: - self.out_attr("error_msg",flo,indent+" ",flags) + self.out_attr("error_msg", flo, indent + " ", flags) + def write(self): raise TypeError("Attempt to write an Error Chunk") # Chunks with common sets of attributes + class OneColorChunk(ChunkBase): - single = ("COLOR_F color, COLOR_24 color," - "LIN_COLOR_F lincolor, LIN_COLOR_24 lincolor") + single = "COLOR_F color, COLOR_24 color," "LIN_COLOR_F lincolor, LIN_COLOR_24 lincolor" + class OnePercentageChunk(ChunkBase): - single = ("INT_PERCENTAGE pct, FLOAT_PERCENTAGE pct") + single = "INT_PERCENTAGE pct, FLOAT_PERCENTAGE pct" + class OneShortValueChunk(ChunkBase): struct = "short value" + class OneFloatValueChunk(ChunkBase): struct = "float value" + class Color24Chunk(ChunkBase): struct = "byte red, byte green, byte blue" + class TextureChunk(ChunkBase): - single = ("INT_PERCENTAGE pct," - "MAT_MAPNAME filename," - "MAT_MAP_TILING tiling," - "MAT_MAP_TEXBLUR blur," - "MAT_MAP_USCALE uscale," - "MAT_MAP_VSCALE vscale," - "MAT_MAP_UOFFSET uoffset," - "MAT_MAP_VOFFSET voffset," - "MAT_MAP_ANG angle," - "MAT_MAP_COL1 color1," - "MAT_MAP_COL2 color2," - "MAT_MAP_RCOL rtint," - "MAT_MAP_GCOL gtint," - "MAT_MAP_BCOL btint") + single = ( + "INT_PERCENTAGE pct," + "MAT_MAPNAME filename," + "MAT_MAP_TILING tiling," + "MAT_MAP_TEXBLUR blur," + "MAT_MAP_USCALE uscale," + "MAT_MAP_VSCALE vscale," + "MAT_MAP_UOFFSET uoffset," + "MAT_MAP_VOFFSET voffset," + "MAT_MAP_ANG angle," + "MAT_MAP_COL1 color1," + "MAT_MAP_COL2 color2," + "MAT_MAP_RCOL rtint," + "MAT_MAP_GCOL gtint," + "MAT_MAP_BCOL btint" + ) + class TextureMaskChunk(ChunkBase): - single = ("INT_PERCENTAGE pct," - "MAT_MAPNAME filename," - "MAT_MAP_TILING tiling," - "MAT_MAP_TEXBLUR blur," - "MAT_MAP_USCALE uscale," - "MAT_MAP_VSCALE vscale," - "MAT_MAP_UOFFSET uoffset," - "MAT_MAP_VOFFSET voffset," - "MAT_MAP_ANG angle") + single = ( + "INT_PERCENTAGE pct," + "MAT_MAPNAME filename," + "MAT_MAP_TILING tiling," + "MAT_MAP_TEXBLUR blur," + "MAT_MAP_USCALE uscale," + "MAT_MAP_VSCALE vscale," + "MAT_MAP_UOFFSET uoffset," + "MAT_MAP_VOFFSET voffset," + "MAT_MAP_ANG angle" + ) + class ArrayChunk(ChunkBase): - __slots__ = ['array'] - def dump_array(self,flo,indent,flags): - flo.write("%s%s = \n" - % (indent,"array",repr(self.array.shape))) + __slots__ = ["array"] + + def dump_array(self, flo, indent, flags): + flo.write("%s%s = \n" % (indent, "array", repr(self.array.shape))) + class MatrixChunk(ArrayChunk): - def read_array(self,fbuf,flags): + def read_array(self, fbuf, flags): size = 48 - a = string_to_array(fbuf.read(size),numpy.float32) - a = numpy.reshape(a,(4,3)) - m = numpy.zeros((4,4),numpy.float32) - m[:,0:3] = a - m[3,3] = 1.0 + a = string_to_array(fbuf.read(size), numpy.float32) + a = numpy.reshape(a, (4, 3)) + m = numpy.zeros((4, 4), numpy.float32) + m[:, 0:3] = a + m[3, 3] = 1.0 self.array = numpy.array(numpy.transpose(m)) - def dump_array(self,flo,indent,flags): - super(MatrixChunk,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def dump_array(self, flo, indent, flags): + super(MatrixChunk, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return for i in range(4): - flo.write("%s %12.4g%12.4g%12.4g%12.4g\n" - % (indent,self.array[i,0],self.array[i,1], - self.array[i,2],self.array[i,3])) + flo.write( + "%s %12.4g%12.4g%12.4g%12.4g\n" + % (indent, self.array[i, 0], self.array[i, 1], self.array[i, 2], self.array[i, 3]) + ) + def write_array(self): - a = self.array[0:3,:] + a = self.array[0:3, :] a = numpy.array(numpy.transpose(a)).astype(numpy.float32) return array_to_string_destructive(a) + class TrackChunk(ChunkBase): - __slots__ = ['keys'] + __slots__ = ["keys"] struct = "short flags, long unused1, long unused2, long nkeys" - def read_array(self,fbuf,flags): + + def read_array(self, fbuf, flags): self.keys = [] for i in range(self.nkeys): kf = self.Key() - kf.frameno = self.get_long(fbuf,flags) - kf.flags = self.get_short(fbuf,flags) + kf.frameno = self.get_long(fbuf, flags) + kf.flags = self.get_short(fbuf, flags) if kf.flags & 1: - kf.tension = self.get_float(fbuf,flags) + kf.tension = self.get_float(fbuf, flags) if kf.flags & 2: - kf.continuity = self.get_float(fbuf,flags) + kf.continuity = self.get_float(fbuf, flags) if kf.flags & 4: - kf.bias = self.get_float(fbuf,flags) + kf.bias = self.get_float(fbuf, flags) if kf.flags & 8: - kf.ease_to = self.get_float(fbuf,flags) + kf.ease_to = self.get_float(fbuf, flags) if kf.flags & 16: - kf.ease_from = self.get_float(fbuf,flags) - for typ,attr in self.keyframe_fields: - setattr(kf,attr,getattr(self,"get_"+typ)(fbuf,flags)) + kf.ease_from = self.get_float(fbuf, flags) + for typ, attr in self.keyframe_fields: + setattr(kf, attr, getattr(self, "get_" + typ)(fbuf, flags)) self.keys.append(kf) + def write_array(self): s = [] for kf in self.keys: @@ -678,34 +717,35 @@ class TrackChunk(ChunkBase): s.append(self.set_float(kf.ease_to)) if kf.flags & 16: s.append(self.set_float(kf.ease_from)) - for typ,attr in self.keyframe_fields: - s.append(getattr(self,"set_"+typ)(getattr(kf,attr))) + for typ, attr in self.keyframe_fields: + s.append(getattr(self, "set_" + typ)(getattr(kf, attr))) return "".join(s) - def dump_array(self,flo,indent,flags): - super(TrackChunk,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def dump_array(self, flo, indent, flags): + super(TrackChunk, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = self.nkeys else: - n = min(flags['arraylines'],self.nkeys) + n = min(flags["arraylines"], self.nkeys) for i in range(n): kf = self.keys[i] - flo.write("%skeys[0] = %s.Key\n" % (indent,self.label)) - flo.write("%s frameno = %r\n" % (indent,kf.frameno)) - flo.write("%s flags = %r\n" % (indent,kf.flags)) + flo.write("%skeys[0] = %s.Key\n" % (indent, self.label)) + flo.write("%s frameno = %r\n" % (indent, kf.frameno)) + flo.write("%s flags = %r\n" % (indent, kf.flags)) if kf.flags & 1: - flo.write("%s tension = %r\n" % (indent,kf.tension)) + flo.write("%s tension = %r\n" % (indent, kf.tension)) if kf.flags & 2: - flo.write("%s continuity = %r\n" % (indent,kf.continuity)) + flo.write("%s continuity = %r\n" % (indent, kf.continuity)) if kf.flags & 4: - flo.write("%s bias = %r\n" % (indent,kf.bias)) + flo.write("%s bias = %r\n" % (indent, kf.bias)) if kf.flags & 8: - flo.write("%s ease_to = %r\n" % (indent,kf.ease_to)) + flo.write("%s ease_to = %r\n" % (indent, kf.ease_to)) if kf.flags & 16: - flo.write("%s ease_from = %r\n" % (indent,kf.ease_from)) - for typ,attr in self.keyframe_fields: - flo.write("%s %s = %r\n" % (indent,attr,getattr(kf,attr))) + flo.write("%s ease_from = %r\n" % (indent, kf.ease_from)) + for typ, attr in self.keyframe_fields: + flo.write("%s %s = %r\n" % (indent, attr, getattr(kf, attr))) if n < self.nkeys: flo.write("%s …\n" % indent) @@ -714,82 +754,111 @@ class TrackChunk(ChunkBase): # Magic and Version Chunks # + class M3DMAGIC(ChunkBase): tag = 0x4D4D single = "M3D_VERSION version, MDATA mdata, KFDATA kfdata" + class M3D_VERSION(ChunkBase): tag = 0x0002 struct = "long number" + class MDATA(ChunkBase): tag = 0x3D3D - single = ("MESH_VERSION version," - "MASTER_SCALE scale," - "VIEWPORT_LAYOUT layout," - "LO_SHADOW_BIAS lo_shadow_bias," - "HI_SHADOW_BIAS hi_shadow_bias," - "SHADOW_MAP_SIZE shadow_map_size," - "SHADOW_SAMPLES shadow_samples," - "SHADOW_RANGE shadow_range," - "SHADOW_FILTER shadow_filter," - #"RAY_BIAS ray_bias," - "O_CONSTS o_consts," - "AMBIENT_LIGHT ambient_light," - "SOLID_BGND solid_background," - "BIT_MAP bitmap," - "V_GRADIENT v_gradient," - "USE_BIT_MAP use_bitmap," - "USE_SOLID_BGND use_solid_background," - "USE_V_GRADIENT use_v_gradient," - "FOG fog," - "LAYER_FOG layer_fog," - "DISTANCE_CUE distance_cue," - "USE_FOG use_fog," - "USE_LAYER_FOG use_layer_fog," - "USE_DISTANCE_CUE use_distance_cue," - "DEFAULT_VIEW default_view," - "MARKER marker") + single = ( + "MESH_VERSION version," + "MASTER_SCALE scale," + "VIEWPORT_LAYOUT layout," + "LO_SHADOW_BIAS lo_shadow_bias," + "HI_SHADOW_BIAS hi_shadow_bias," + "SHADOW_MAP_SIZE shadow_map_size," + "SHADOW_SAMPLES shadow_samples," + "SHADOW_RANGE shadow_range," + "SHADOW_FILTER shadow_filter," + # "RAY_BIAS ray_bias," + "O_CONSTS o_consts," + "AMBIENT_LIGHT ambient_light," + "SOLID_BGND solid_background," + "BIT_MAP bitmap," + "V_GRADIENT v_gradient," + "USE_BIT_MAP use_bitmap," + "USE_SOLID_BGND use_solid_background," + "USE_V_GRADIENT use_v_gradient," + "FOG fog," + "LAYER_FOG layer_fog," + "DISTANCE_CUE distance_cue," + "USE_FOG use_fog," + "USE_LAYER_FOG use_layer_fog," + "USE_DISTANCE_CUE use_distance_cue," + "DEFAULT_VIEW default_view," + "MARKER marker" + ) multiple = "MAT_ENTRY materials, NAMED_OBJECT objects" + class MESH_VERSION(ChunkBase): tag = 0x3D3E struct = "long number" -class SMAGIC(UndefinedChunk): tag = 0x2D2D -class LMAGIC(UndefinedChunk): tag = 0x2D3D -class MLIBMAGIC(UndefinedChunk): tag = 0x2DAA -class PRJMAGIC(UndefinedChunk): tag = 0x3D2C -class MATMAGIC(UndefinedChunk): tag = 0x3DFF + +class SMAGIC(UndefinedChunk): + tag = 0x2D2D + + +class LMAGIC(UndefinedChunk): + tag = 0x2D3D + + +class MLIBMAGIC(UndefinedChunk): + tag = 0x2DAA + + +class PRJMAGIC(UndefinedChunk): + tag = 0x3D2C + + +class MATMAGIC(UndefinedChunk): + tag = 0x3DFF + # # Miscellaneous, Global data # + class MARKER(ChunkBase): tag = 0x0001 struct = "long sum" + class COLOR_F(ChunkBase): tag = 0x0010 struct = "float red, float green, float blue" + class COLOR_24(Color24Chunk): tag = 0x0011 + class LIN_COLOR_24(Color24Chunk): tag = 0x0012 + class LIN_COLOR_F(ChunkBase): tag = 0x0013 struct = "float red, float green, float blue" + class INT_PERCENTAGE(OneShortValueChunk): tag = 0x0030 + class FLOAT_PERCENTAGE(OneFloatValueChunk): tag = 0x0031 + class MASTER_SCALE(OneFloatValueChunk): tag = 0x0100 @@ -798,135 +867,168 @@ class MASTER_SCALE(OneFloatValueChunk): # Coloring, Lighting, Atmosphers Stuff # + class BIT_MAP(ChunkBase): tag = 0x1100 - struct = 'string filename' + struct = "string filename" swallow = True + class USE_BIT_MAP(ChunkBase): tag = 0x1101 + class SOLID_BGND(OneColorChunk): tag = 0x1200 + class USE_SOLID_BGND(ChunkBase): tag = 0x1201 + class V_GRADIENT(ChunkBase): tag = 0x1300 struct = "float midpoint" - multiple = ("COLOR_F color, LIN_COLOR_F lincolor," - "COLOR_24 color, LIN_COLOR_24 lincolor") + multiple = "COLOR_F color, LIN_COLOR_F lincolor," "COLOR_24 color, LIN_COLOR_24 lincolor" + class USE_V_GRADIENT(ChunkBase): tag = 0x1301 + class LO_SHADOW_BIAS(OneFloatValueChunk): tag = 0x1400 + class HI_SHADOW_BIAS(OneFloatValueChunk): tag = 0x1410 + class SHADOW_MAP_SIZE(OneShortValueChunk): tag = 0x1420 + class SHADOW_SAMPLES(OneShortValueChunk): tag = 0x1430 + class SHADOW_RANGE(OneShortValueChunk): tag = 0x1440 + class SHADOW_FILTER(OneFloatValueChunk): tag = 0x1450 + class RAW_BIAS(OneFloatValueChunk): tag = 0x1460 + class O_CONSTS(ChunkBase): tag = 0x1500 struct = "float plane_x, float plane_y, float plane_z" + class AMBIENT_LIGHT(OneColorChunk): tag = 0x2100 class FOG(ChunkBase): tag = 0x2200 - struct = ("float near_plane, float near_density," - "float far_plane, float far_density") + struct = "float near_plane, float near_density," "float far_plane, float far_density" single = "COLOR_F color, FOG_BGND fog_background" + class USE_FOG(ChunkBase): tag = 0x2201 + class FOG_BGND(ChunkBase): tag = 0x2210 + class DISTANCE_CUE(ChunkBase): tag = 0x2300 - struct = ("float near_plane, float near_density," - "float far_plane, float far_density") + struct = "float near_plane, float near_density," "float far_plane, float far_density" single = "DCUE_BGND dcue_background" + class USE_DISTANCE_CUE(ChunkBase): tag = 0x2301 + class LAYER_FOG(ChunkBase): tag = 0x2302 - struct = ("float fog_z_from, float fog_z_to," - "float fog_density, long fog_type") + struct = "float fog_z_from, float fog_z_to," "float fog_density, long fog_type" single = "COLOR_F color" + class USE_LAYER_FOG(ChunkBase): tag = 0x2303 + class DCUE_BGND(ChunkBase): tag = 0x2310 + # # View Data # + class DEFAULT_VIEW(ChunkBase): tag = 0x3000 - single = ('VIEW_TOP view, VIEW_BOTTOM view, VIEW_LEFT view,' - 'VIEW_RIGHT view, VIEW_FRONT view, VIEW_BACK view,' - 'VIEW_USER view, VIEW_CAMERA camera') + single = ( + "VIEW_TOP view, VIEW_BOTTOM view, VIEW_LEFT view," + "VIEW_RIGHT view, VIEW_FRONT view, VIEW_BACK view," + "VIEW_USER view, VIEW_CAMERA camera" + ) + class ViewChunk(ChunkBase): - struct = ("float target_x, float target_y," - "float target_z, float view_width") + struct = "float target_x, float target_y," "float target_z, float view_width" + class VIEW_TOP(ViewChunk): tag = 0x3010 + class VIEW_BOTTOM(ViewChunk): tag = 0x3020 + class VIEW_LEFT(ViewChunk): tag = 0x3030 + class VIEW_RIGHT(ViewChunk): tag = 0x3040 + class VIEW_FRONT(ViewChunk): tag = 0x3050 + class VIEW_BACK(ViewChunk): tag = 0x3060 + class VIEW_USER(ChunkBase): tag = 0x3070 - struct = ("float target_x, float target_y," - "float target_z, float view_width," - "float horiz_angle, float vert_angle," - "float back_angle") + struct = ( + "float target_x, float target_y," + "float target_z, float view_width," + "float horiz_angle, float vert_angle," + "float back_angle" + ) + class VIEW_CAMERA(ChunkBase): tag = 0x3080 struct = "string name" swallow = True + class VIEW_WINDOW(UndefinedChunk): tag = 0x3090 @@ -935,222 +1037,272 @@ class VIEW_WINDOW(UndefinedChunk): # Objects # + class NAMED_OBJECT(ChunkBase): tag = 0x4000 struct = "string name" - single = ("N_TRI_OBJECT obj," - "N_DIRECT_LIGHT obj," - "N_CAMERA obj," - "OBJ_HIDDEN hidden," - "OBJ_VIS_LOFTER vis_lofter," - "OBJ_DOESNT_CAST doesnt_cast," - "OBJ_MATTE matte," - "OBJ_DONT_RCVSHADOW dont_rcvshadow," - "OBJ_FAST fast," - "OBJ_PROCEDURAL procedural," - "OBJ_FROZEN frozen") + single = ( + "N_TRI_OBJECT obj," + "N_DIRECT_LIGHT obj," + "N_CAMERA obj," + "OBJ_HIDDEN hidden," + "OBJ_VIS_LOFTER vis_lofter," + "OBJ_DOESNT_CAST doesnt_cast," + "OBJ_MATTE matte," + "OBJ_DONT_RCVSHADOW dont_rcvshadow," + "OBJ_FAST fast," + "OBJ_PROCEDURAL procedural," + "OBJ_FROZEN frozen" + ) + class OBJ_HIDDEN(ChunkBase): tag = 0x4010 + class OBJ_VIS_LOFTER(ChunkBase): tag = 0x4011 + class OBJ_DOESNT_CAST(ChunkBase): tag = 0x4012 + class OBJ_MATTE(ChunkBase): tag = 0x4013 + class OBJ_FAST(ChunkBase): tag = 0x4014 + class OBJ_PROCEDURAL(ChunkBase): tag = 0x4015 + class OBJ_FROZEN(ChunkBase): tag = 0x4016 + class OBJ_DONT_RCVSHADOW(ChunkBase): tag = 0x4017 + class N_TRI_OBJECT(ChunkBase): tag = 0x4100 - single = ("POINT_ARRAY points," - "POINT_FLAG_ARRAY flags," - "FACE_ARRAY faces," - "TEX_VERTS texverts," - "MESH_MATRIX matrix," - "MESH_COLOR color," - "MESH_TEXTURE_INFO texinfo," - "PROC_NAME proc_name," - "PROC_DATA proc_data") + single = ( + "POINT_ARRAY points," + "POINT_FLAG_ARRAY flags," + "FACE_ARRAY faces," + "TEX_VERTS texverts," + "MESH_MATRIX matrix," + "MESH_COLOR color," + "MESH_TEXTURE_INFO texinfo," + "PROC_NAME proc_name," + "PROC_DATA proc_data" + ) multiple = "MSH_MAT_GROUP matlist" + class POINT_ARRAY(ArrayChunk): tag = 0x4110 struct = "short npoints" - def read_array(self,fbuf,flags): - size = 12*self.npoints - a = string_to_array(fbuf.read(size),numpy.float32) - a = numpy.reshape(a,(self.npoints,3)) + + def read_array(self, fbuf, flags): + size = 12 * self.npoints + a = string_to_array(fbuf.read(size), numpy.float32) + a = numpy.reshape(a, (self.npoints, 3)) self.array = numpy.array(a) - def dump_array(self,flo,indent,flags): - super(POINT_ARRAY,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def dump_array(self, flo, indent, flags): + super(POINT_ARRAY, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = self.npoints else: - n = min(flags['arraylines'],self.npoints) + n = min(flags["arraylines"], self.npoints) for i in range(n): - flo.write("%s %12.4g%12.4g%12.4g\n" - % (indent,self.array[i,0],self.array[i,1], - self.array[i,2])) + flo.write( + "%s %12.4g%12.4g%12.4g\n" + % (indent, self.array[i, 0], self.array[i, 1], self.array[i, 2]) + ) if n < self.npoints: flo.write("%s …\n" % indent) + def write_array(self): s = numpy.array(self.array).astype(numpy.float32) return array_to_string_destructive(s) + swallow = True + class POINT_FLAG_ARRAY(ArrayChunk): tag = 0x4111 struct = "short npoints" - def read_array(self,fbuf,flags): - size = 2*self.npoints - self.array = string_to_array(fbuf.read(size),numpy.uint16) + + def read_array(self, fbuf, flags): + size = 2 * self.npoints + self.array = string_to_array(fbuf.read(size), numpy.uint16) + def write_array(self): s = numpy.array(self.array).astype(numpy.uint16) return array_to_string_destructive(s) + swallow = True + class FACE_ARRAY(ArrayChunk): tag = 0x4120 struct = "short nfaces" - multiple = 'MSH_MAT_GROUP materials' - single = 'SMOOTH_GROUP smoothing, MSH_BOXMAP box' - def read_array(self,fbuf,flags): - size = 8*self.nfaces - a = string_to_array(fbuf.read(size),numpy.uint16) - a = numpy.reshape(a,(self.nfaces,4)) + multiple = "MSH_MAT_GROUP materials" + single = "SMOOTH_GROUP smoothing, MSH_BOXMAP box" + + def read_array(self, fbuf, flags): + size = 8 * self.nfaces + a = string_to_array(fbuf.read(size), numpy.uint16) + a = numpy.reshape(a, (self.nfaces, 4)) self.array = numpy.array(a) - def dump_array(self,flo,indent,flags): - super(FACE_ARRAY,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def dump_array(self, flo, indent, flags): + super(FACE_ARRAY, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = self.nfaces else: - n = min(flags['arraylines'],self.nfaces) + n = min(flags["arraylines"], self.nfaces) for i in range(n): - flo.write("%s %10d%10d%10d%10d\n" - % (indent,self.array[i,0],self.array[i,1], - self.array[i,2],self.array[i,3])) + flo.write( + "%s %10d%10d%10d%10d\n" + % (indent, self.array[i, 0], self.array[i, 1], self.array[i, 2], self.array[i, 3]) + ) if n < self.nfaces: flo.write("%s …\n" % indent) + def write_array(self): s = numpy.array(self.array).astype(numpy.uint16) return array_to_string_destructive(s) + class MSH_MAT_GROUP(ArrayChunk): tag = 0x4130 struct = "string name, short mfaces" - def read_array(self,fbuf,flags): - size = 2*self.mfaces - self.array = string_to_array(fbuf.read(size),numpy.uint16) - def dump_array(self,flo,indent,flags): - super(MSH_MAT_GROUP,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def read_array(self, fbuf, flags): + size = 2 * self.mfaces + self.array = string_to_array(fbuf.read(size), numpy.uint16) + + def dump_array(self, flo, indent, flags): + super(MSH_MAT_GROUP, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = self.mfaces else: - n = min(flags['arraylines'],self.mfaces) + n = min(flags["arraylines"], self.mfaces) for i in range(n): - flo.write("%s %10d\n" % (indent,self.array[i])) + flo.write("%s %10d\n" % (indent, self.array[i])) if n < self.mfaces: flo.write("%s …\n" % indent) + def write_array(self): s = numpy.array(self.array).astype(numpy.uint16) return array_to_string_destructive(s) + swallow = True + class TEX_VERTS(ArrayChunk): tag = 0x4140 struct = "short npoints" - def read_array(self,fbuf,flags): - size = 8*self.npoints - a = string_to_array(fbuf.read(size),numpy.float32) - a = numpy.reshape(a,(self.npoints,2)) + + def read_array(self, fbuf, flags): + size = 8 * self.npoints + a = string_to_array(fbuf.read(size), numpy.float32) + a = numpy.reshape(a, (self.npoints, 2)) self.array = numpy.array(a) - def dump_array(self,flo,indent,flags): - super(TEX_VERTS,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def dump_array(self, flo, indent, flags): + super(TEX_VERTS, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = self.npoints else: - n = min(flags['arraylines'],self.npoints) + n = min(flags["arraylines"], self.npoints) for i in range(n): - flo.write("%s %12.4g%12.4g\n" - % (indent,self.array[i,0],self.array[i,1])) + flo.write("%s %12.4g%12.4g\n" % (indent, self.array[i, 0], self.array[i, 1])) if n < self.npoints: flo.write("%s …\n" % indent) + def write_array(self): s = numpy.array(self.array).astype(numpy.float32) return array_to_string_destructive(s) + swallow = True + class SMOOTH_GROUP(ArrayChunk): tag = 0x4150 - def read_array(self,fbuf,flags): - self.array = string_to_array(fbuf.read_rest(),numpy.uint32) - def dump_array(self,flo,indent,flags): - super(SMOOTH_GROUP,self).dump_array(flo,indent,flags) - if flags['arraylines'] == 0: + + def read_array(self, fbuf, flags): + self.array = string_to_array(fbuf.read_rest(), numpy.uint32) + + def dump_array(self, flo, indent, flags): + super(SMOOTH_GROUP, self).dump_array(flo, indent, flags) + if flags["arraylines"] == 0: return - if flags['arraylines'] < 0: + if flags["arraylines"] < 0: n = len(self.array) else: - n = min(flags['arraylines'],len(self.array)) + n = min(flags["arraylines"], len(self.array)) for i in range(n): - flo.write("%s %10d\n" % (indent,self.array[i])) + flo.write("%s %10d\n" % (indent, self.array[i])) if n < len(self.array): flo.write("%s …\n" % indent) + def write_array(self): s = numpy.array(self.array).astype(numpy.uint32) return array_to_string_destructive(s) + swallow = True + class MESH_MATRIX(MatrixChunk): tag = 0x4160 + class MESH_COLOR(ChunkBase): tag = 0x4165 struct = "byte color_index" + class MESH_TEXTURE_INFO(MatrixChunk): tag = 0x4170 - __slots__ = [ 'icon_width', 'icon_height', 'cyl_height' ] - struct = ("float x_tiling, float y_tiling, float icon_x," - "float icon_y, float icon_z, float scaling") - def read(self,fbuf,flags): - self.read_struct(fbuf,flags) - self.read_array(fbuf,flags) - self.icon_width = self.get_float(fbuf,flags) - self.icon_height = self.get_float(fbuf,flags) - self.cyl_height = self.get_float(fbuf,flags) - self.read_chunks(fbuf,flags) - def dump(self,flo,indent,flags): - indent += ' ' - self.dump_header(flo,flags) - self.dump_struct(flo,indent,flags) - self.dump_array(flo,indent,flags) - self.out_attr('icon_width',flo,indent,flags) - self.out_attr('icon_height',flo,indent,flags) - self.out_attr('cyl_height',flo,indent,flags) - self.dump_chunks(flo,indent,flags) + __slots__ = ["icon_width", "icon_height", "cyl_height"] + struct = ( + "float x_tiling, float y_tiling, float icon_x," "float icon_y, float icon_z, float scaling" + ) + + def read(self, fbuf, flags): + self.read_struct(fbuf, flags) + self.read_array(fbuf, flags) + self.icon_width = self.get_float(fbuf, flags) + self.icon_height = self.get_float(fbuf, flags) + self.cyl_height = self.get_float(fbuf, flags) + self.read_chunks(fbuf, flags) + + def dump(self, flo, indent, flags): + indent += " " + self.dump_header(flo, flags) + self.dump_struct(flo, indent, flags) + self.dump_array(flo, indent, flags) + self.out_attr("icon_width", flo, indent, flags) + self.out_attr("icon_height", flo, indent, flags) + self.out_attr("cyl_height", flo, indent, flags) + self.dump_chunks(flo, indent, flags) + def write(self): s = [] s.append(self.write_struct()) @@ -1159,577 +1311,765 @@ class MESH_TEXTURE_INFO(MatrixChunk): s.append(self.out_float(self.icon_height)) s.append(self.out_float(self.cyl_height)) s.append(self.write_chunks()) - return ''.join(s) + return "".join(s) + class PROC_NAME(ChunkBase): tag = 0x4181 struct = "string value" swallow = True + class PROC_DATA(UndefinedChunk): tag = 0x4181 struct = "string value" swallow = True + class MSH_BOXMAP(ChunkBase): tag = 0x4190 - struct = ("string front, string back, string left," - "string right, string top, string bottom") + struct = "string front, string back, string left," "string right, string top, string bottom" swallow = True class N_DIRECT_LIGHT(ChunkBase): tag = 0x4600 struct = "float light_x, float light_y, float light_z" - single = ("COLOR_F color, COLOR_24 color," - "DL_OFF switch," - "DL_OUTER_RANGE outer_range," - "DL_INNER_RANGE inner_range," - "DL_MULTIPLIER multiplier," - "DL_SPOTLIGHT spotlight," - "DL_ATTENUATE attenuate") + single = ( + "COLOR_F color, COLOR_24 color," + "DL_OFF switch," + "DL_OUTER_RANGE outer_range," + "DL_INNER_RANGE inner_range," + "DL_MULTIPLIER multiplier," + "DL_SPOTLIGHT spotlight," + "DL_ATTENUATE attenuate" + ) multiple = "DL_EXCLUDE excludes" + class DL_SPOTLIGHT(ChunkBase): tag = 0x4610 - struct = ("float spot_x, float spot_y, float spot_z," - "float hotspot_angle, float falloff_angle") - single = ("DL_SPOT_ROLL roll_angle," - "DL_SHADOWED shadowed," - "DL_LOCAL_SHADOW2 local_shadow," - "DL_SEE_CONE see_cone," - "DL_SPOT_RECTANGULAR rectangular," - "DL_SPOT_ASPECT aspect," - "DL_SPOT_PROJECTOR projector," - "DL_SPOT_OVERSHOOT overshoot," - "DL_RAY_BIAS bias," - "DL_RAYSHADE rayshade") + struct = "float spot_x, float spot_y, float spot_z," "float hotspot_angle, float falloff_angle" + single = ( + "DL_SPOT_ROLL roll_angle," + "DL_SHADOWED shadowed," + "DL_LOCAL_SHADOW2 local_shadow," + "DL_SEE_CONE see_cone," + "DL_SPOT_RECTANGULAR rectangular," + "DL_SPOT_ASPECT aspect," + "DL_SPOT_PROJECTOR projector," + "DL_SPOT_OVERSHOOT overshoot," + "DL_RAY_BIAS bias," + "DL_RAYSHADE rayshade" + ) + class DL_OFF(ChunkBase): tag = 0x4620 + class DL_ATTENUATE(ChunkBase): tag = 0x4625 + class DL_RAYSHADE(ChunkBase): tag = 0x4627 + class DL_SHADOWED(ChunkBase): tag = 0x4630 + class DL_LOCAL_SHADOW2(ChunkBase): tag = 0x4630 struct = "float low_bias, float filter, short mapsize" + class DL_SEE_CONE(ChunkBase): tag = 0x4650 + class DL_SPOT_RECTANGULAR(ChunkBase): tag = 0x4651 + class DL_SPOT_OVERSHOOT(ChunkBase): tag = 0x4652 + class DL_SPOT_PROJECTOR(ChunkBase): tag = 0x4653 struct = "string filename" swallow = True + class DL_EXCLUDE(ChunkBase): tag = 0x4654 struct = "string value" swallow = True + class DL_SPOT_ROLL(OneFloatValueChunk): tag = 0x4656 + class DL_SPOT_ASPECT(OneFloatValueChunk): tag = 0x4657 + class DL_RAY_BIAS(OneFloatValueChunk): tag = 0x4658 + class DL_INNER_RANGE(OneFloatValueChunk): tag = 0x4659 + class DL_OUTER_RANGE(OneFloatValueChunk): tag = 0x465A + class DL_MULTIPLIER(OneFloatValueChunk): tag = 0x465B class N_CAMERA(ChunkBase): tag = 0x4700 - struct = ("float camera_x, float camera_y, float camera_z," - "float target_x, float target_y, float target_z," - "float bank_angle, float focal_length") + struct = ( + "float camera_x, float camera_y, float camera_z," + "float target_x, float target_y, float target_z," + "float bank_angle, float focal_length" + ) single = "CAM_SEE_CONE see_cone, CAM_RANGES ranges" + class CAM_SEE_CONE(ChunkBase): tag = 0x4710 + class CAM_RANGES(ChunkBase): tag = 0x4720 struct = "float near, float far" + # # Viewport Data # + class VIEWPORT_LAYOUT(ChunkBase): tag = 0x7001 - struct = ('short form, short top, short ready,' - 'short wstate, short swapws, short swapport,' - 'short swapcur') - single = 'VIEWPORT_SIZE size' - multiple = 'VIEWPORT_DATA data, VIEWPORT_DATA_3 data' + struct = ( + "short form, short top, short ready," + "short wstate, short swapws, short swapport," + "short swapcur" + ) + single = "VIEWPORT_SIZE size" + multiple = "VIEWPORT_DATA data, VIEWPORT_DATA_3 data" + class ViewportDataChunk(ChunkBase): - struct = ('short flags, short axis_lockout, short win_x,' - 'short win_y, short win_w, short win_h,' - 'short win_view, float zoom, float worldcenter_x,' - 'float worldcenter_y, float worldcenter_z,' - 'float horiz_ang, float vert_ang,' - 'string cameraname') + struct = ( + "short flags, short axis_lockout, short win_x," + "short win_y, short win_w, short win_h," + "short win_view, float zoom, float worldcenter_x," + "float worldcenter_y, float worldcenter_z," + "float horiz_ang, float vert_ang," + "string cameraname" + ) swallow = True + class VIEWPORT_DATA(ViewportDataChunk): tag = 0x7011 + class VIEWPORT_DATA_3(ViewportDataChunk): tag = 0x7012 + class VIEWPORT_SIZE(ChunkBase): tag = 0x7020 - struct = 'short x, short y, short w, short h' + struct = "short x, short y, short w, short h" + class NETWORK_VIEW(UndefinedChunk): tag = 0x7030 + # # Material Data # + class MAT_NAME(ChunkBase): tag = 0xA000 - struct = 'string value' + struct = "string value" swallow = True + class MAT_AMBIENT(OneColorChunk): tag = 0xA010 + class MAT_DIFFUSE(OneColorChunk): tag = 0xA020 + class MAT_SPECULAR(OneColorChunk): tag = 0xA030 + class MAT_SHININESS(OnePercentageChunk): tag = 0xA040 + class MAT_SHIN2PCT(OnePercentageChunk): tag = 0xA041 + class MAT_SHIN3PCT(OnePercentageChunk): tag = 0xA042 + class MAT_TRANSPARENCY(OnePercentageChunk): tag = 0xA050 + class MAT_XPFALL(OnePercentageChunk): tag = 0xA052 + class MAT_REFBLUR(OnePercentageChunk): tag = 0xA053 + class MAT_SELF_ILLUM(ChunkBase): tag = 0xA080 + class MAT_TWO_SIDE(ChunkBase): tag = 0xA081 + class MAT_DECAL(ChunkBase): tag = 0xA082 + class MAT_ADDITIVE(ChunkBase): tag = 0xA083 + class MAT_SELF_ILPCT(OnePercentageChunk): tag = 0xA084 + class MAT_WIRE(ChunkBase): tag = 0xA085 + class MAT_SUPERSMP(UndefinedChunk): tag = 0xA086 + class MAT_WIRESIZE(OneFloatValueChunk): tag = 0xA087 + class MAT_FACEMAP(ChunkBase): tag = 0xA088 + class MAT_XPFALLIN(UndefinedChunk): tag = 0xA08A + class MAT_PHONGSOFT(ChunkBase): tag = 0xA08C + class MAT_WIREABS(ChunkBase): tag = 0xA08E + class MAT_SHADING(OneShortValueChunk): tag = 0xA100 + class MAT_TEXMAP(TextureChunk): tag = 0xA200 + class MAT_SPECMAP(TextureChunk): tag = 0xA204 + class MAT_OPACMAP(TextureMaskChunk): tag = 0xA210 + class MAT_REFLMAP(ChunkBase): tag = 0xA220 single = "INT_PERCENTAGE pct, MAT_MAPNAME filename" + class MAT_BUMPMAP(TextureMaskChunk): tag = 0xA230 + class MAT_USE_XPFALL(ChunkBase): tag = 0xA240 + class MAT_USE_REFBLUR(ChunkBase): tag = 0xA250 + class MAT_BUMP_PERCENT(ChunkBase): tag = 0xA252 struct = "short value" + class MAT_MAPNAME(ChunkBase): tag = 0xA300 struct = "string value" swallow = True + class MAT_ACUBIC(ChunkBase): tag = 0xA310 - struct = ("byte shade, byte antialias, short rflags," - "long mapsize, long frame") + struct = "byte shade, byte antialias, short rflags," "long mapsize, long frame" + class MAT_TEX2MAP(TextureChunk): tag = 0xA33A + class MAT_SHINMAP(TextureMaskChunk): tag = 0xA33C + class MAT_SELFIMAP(TextureChunk): tag = 0xA33D + class MAT_TEXMASK(TextureMaskChunk): tag = 0xA33E + class MAT_TEXT2MASK(TextureMaskChunk): tag = 0xA340 + class MAT_OPACMASK(TextureMaskChunk): tag = 0xA342 + class MAT_BUMPMASK(TextureMaskChunk): tag = 0xA344 + class MAT_SHINMASK(TextureMaskChunk): tag = 0xA346 + class MAT_SPECMASK(TextureMaskChunk): tag = 0xA348 + class MAT_SELFIMASK(TextureChunk): tag = 0xA34A + class MAT_REFLMASK(TextureChunk): tag = 0xA34C + class MAT_MAP_TILING(OneShortValueChunk): tag = 0xA351 + class MAT_MAP_TEXBLUR(OneFloatValueChunk): tag = 0xA353 + class MAT_MAP_USCALE(OneFloatValueChunk): tag = 0xA354 + class MAT_MAP_VSCALE(OneFloatValueChunk): tag = 0xA356 + class MAT_MAP_UOFFSET(OneFloatValueChunk): tag = 0xA358 + class MAT_MAP_VOFFSET(OneFloatValueChunk): tag = 0xA35A + class MAT_MAP_ANG(OneFloatValueChunk): tag = 0xA35C + class MAT_MAP_COL1(Color24Chunk): tag = 0xA360 + class MAT_MAP_COL2(Color24Chunk): tag = 0xA362 + class MAT_MAP_RCOL(Color24Chunk): tag = 0xA364 + class MAT_MAP_GCOL(Color24Chunk): tag = 0xA366 + class MAT_MAP_BCOL(Color24Chunk): tag = 0xA368 + class MAT_ENTRY(ChunkBase): tag = 0xAFFF - single = ("MAT_NAME name," - "MAT_AMBIENT ambient," - "MAT_DIFFUSE diffuse," - "MAT_SPECULAR specular," - "MAT_SHININESS shininess," - "MAT_SHIN2PCT shin2pct," - "MAT_TRANSPARENCY transparency," - "MAT_XPFALL xpfall," - "MAT_REFBLUR refblur," - "MAT_SELF_ILLUM self_illum," - "MAT_SHADING shading," - "MAT_SELF_ILPCT self_ilpct," - "MAT_USE_XPFALL use_xpfall," - "MAT_USE_REFBLUR use_refblur," - "MAT_TWO_SIDE two_side," - "MAT_ADDITIVE additive," - "MAT_WIRE wire," - "MAT_FACEMAP facemap," - "MAT_PHONGSOFT phongsoft," - "MAT_WIRESIZE wiresize," - "MAT_DECAL decal," - "MAT_TEXMAP texmap," - "MAT_SXP_TEXT_DATA sxp_text_data," - "MAT_TEXMASK texmask," - "MAT_SXP_TEXT_MASKDATA sxp_text_maskdata," - "MAT_TEX2MAP tex2map," - "MAT_SXP_TEXT2_DATA sxp_text2_data," - "MAT_TEXT2MASK text2mask," - "MAT_SXP_TEXT2_MASKDATA sxp_text2_maskdata," - "MAT_OPACMAP opacmap," - "MAT_SXP_OPAC_DATA sxp_opac_data," - "MAT_OPACMASK opac_mask," - "MAT_SXP_OPAC_MASKDATA sxp_opac_maskdata," - "MAT_BUMPMAP bumpmap," - "MAT_SXP_BUMP_DATA sxp_bump_data," - "MAT_BUMPMASK bumpmask," - "MAT_SXP_BUMP_MASKDATA sxp_bump_maskdata," - "MAT_SPECMAP specmap," - "MAT_SXP_SPEC_DATA sxp_spec_data," - "MAT_SPECMASK specmask," - "MAT_SXP_SPEC_MASKDATA sxp_spec_maskdata," - "MAT_SHINMAP shinmap," - "MAT_SXP_SHIN_DATA sxp_shin_data," - "MAT_SHINMASK shinmask," - "MAT_SXP_SHIN_MASKDATA sxp_shin_maskdata," - "MAT_SELFIMAP selfimap," - "MAT_SXP_SELFI_DATA sxp_selfi_data," - "MAT_SELFIMASK selfimask," - "MAT_SXP_SELFI_MASKDATA sxp_selfi_maskdata," - "MAT_REFLMAP reflmap," - "MAT_REFLMASK reflmask," - "MAT_SXP_REFL_MASKDATA sxp_refl_maskdata," - "MAT_ACUBIC acubic") + single = ( + "MAT_NAME name," + "MAT_AMBIENT ambient," + "MAT_DIFFUSE diffuse," + "MAT_SPECULAR specular," + "MAT_SHININESS shininess," + "MAT_SHIN2PCT shin2pct," + "MAT_TRANSPARENCY transparency," + "MAT_XPFALL xpfall," + "MAT_REFBLUR refblur," + "MAT_SELF_ILLUM self_illum," + "MAT_SHADING shading," + "MAT_SELF_ILPCT self_ilpct," + "MAT_USE_XPFALL use_xpfall," + "MAT_USE_REFBLUR use_refblur," + "MAT_TWO_SIDE two_side," + "MAT_ADDITIVE additive," + "MAT_WIRE wire," + "MAT_FACEMAP facemap," + "MAT_PHONGSOFT phongsoft," + "MAT_WIRESIZE wiresize," + "MAT_DECAL decal," + "MAT_TEXMAP texmap," + "MAT_SXP_TEXT_DATA sxp_text_data," + "MAT_TEXMASK texmask," + "MAT_SXP_TEXT_MASKDATA sxp_text_maskdata," + "MAT_TEX2MAP tex2map," + "MAT_SXP_TEXT2_DATA sxp_text2_data," + "MAT_TEXT2MASK text2mask," + "MAT_SXP_TEXT2_MASKDATA sxp_text2_maskdata," + "MAT_OPACMAP opacmap," + "MAT_SXP_OPAC_DATA sxp_opac_data," + "MAT_OPACMASK opac_mask," + "MAT_SXP_OPAC_MASKDATA sxp_opac_maskdata," + "MAT_BUMPMAP bumpmap," + "MAT_SXP_BUMP_DATA sxp_bump_data," + "MAT_BUMPMASK bumpmask," + "MAT_SXP_BUMP_MASKDATA sxp_bump_maskdata," + "MAT_SPECMAP specmap," + "MAT_SXP_SPEC_DATA sxp_spec_data," + "MAT_SPECMASK specmask," + "MAT_SXP_SPEC_MASKDATA sxp_spec_maskdata," + "MAT_SHINMAP shinmap," + "MAT_SXP_SHIN_DATA sxp_shin_data," + "MAT_SHINMASK shinmask," + "MAT_SXP_SHIN_MASKDATA sxp_shin_maskdata," + "MAT_SELFIMAP selfimap," + "MAT_SXP_SELFI_DATA sxp_selfi_data," + "MAT_SELFIMASK selfimask," + "MAT_SXP_SELFI_MASKDATA sxp_selfi_maskdata," + "MAT_REFLMAP reflmap," + "MAT_REFLMASK reflmask," + "MAT_SXP_REFL_MASKDATA sxp_refl_maskdata," + "MAT_ACUBIC acubic" + ) + + +class MAT_SXP_TEXT_DATA(UndefinedChunk): + tag = 0xA320 + + +class MAT_SXP_TEXT2_DATA(UndefinedChunk): + tag = 0xA321 + + +class MAT_SXP_OPAC_DATA(UndefinedChunk): + tag = 0xA322 + + +class MAT_SXP_BUMP_DATA(UndefinedChunk): + tag = 0xA324 + + +class MAT_SXP_SPEC_DATA(UndefinedChunk): + tag = 0xA325 + + +class MAT_SXP_SHIN_DATA(UndefinedChunk): + tag = 0xA326 + + +class MAT_SXP_SELFI_DATA(UndefinedChunk): + tag = 0xA328 + + +class MAT_SXP_TEXT_MASKDATA(UndefinedChunk): + tag = 0xA32A + + +class MAT_SXP_TEXT2_MASKDATA(UndefinedChunk): + tag = 0xA32C + + +class MAT_SXP_OPAC_MASKDATA(UndefinedChunk): + tag = 0xA32E + + +class MAT_SXP_BUMP_MASKDATA(UndefinedChunk): + tag = 0xA330 + + +class MAT_SXP_SPEC_MASKDATA(UndefinedChunk): + tag = 0xA332 + + +class MAT_SXP_SHIN_MASKDATA(UndefinedChunk): + tag = 0xA334 + + +class MAT_SXP_SELFI_MASKDATA(UndefinedChunk): + tag = 0xA336 + + +class MAT_SXP_REFL_MASKDATA(UndefinedChunk): + tag = 0xA338 -class MAT_SXP_TEXT_DATA(UndefinedChunk): tag = 0xA320 -class MAT_SXP_TEXT2_DATA(UndefinedChunk): tag = 0xA321 -class MAT_SXP_OPAC_DATA(UndefinedChunk): tag = 0xA322 -class MAT_SXP_BUMP_DATA(UndefinedChunk): tag = 0xA324 -class MAT_SXP_SPEC_DATA(UndefinedChunk): tag = 0xA325 -class MAT_SXP_SHIN_DATA(UndefinedChunk): tag = 0xA326 -class MAT_SXP_SELFI_DATA(UndefinedChunk): tag = 0xA328 -class MAT_SXP_TEXT_MASKDATA(UndefinedChunk): tag = 0xA32A -class MAT_SXP_TEXT2_MASKDATA(UndefinedChunk): tag = 0xA32C -class MAT_SXP_OPAC_MASKDATA(UndefinedChunk): tag = 0xA32E -class MAT_SXP_BUMP_MASKDATA(UndefinedChunk): tag = 0xA330 -class MAT_SXP_SPEC_MASKDATA(UndefinedChunk): tag = 0xA332 -class MAT_SXP_SHIN_MASKDATA(UndefinedChunk): tag = 0xA334 -class MAT_SXP_SELFI_MASKDATA(UndefinedChunk): tag = 0xA336 -class MAT_SXP_REFL_MASKDATA(UndefinedChunk): tag = 0xA338 # # Keyframe Section # + class KFDATA(ChunkBase): tag = 0xB000 single = "KFHDR kfhdr, KFSEG kfseg, KFCURTIME kfcurtime" - multiple = ("OBJECT_NODE_TAG object_nodes," - "CAMERA_NODE_TAG camera_nodes," - "TARGET_NODE_TAG target_nodes," - "LIGHT_NODE_TAG light_nodes," - "SPOTLIGHT_NODE_TAG spotlight_nodes," - "L_TARGET_NODE_TAG l_target_nodes," - "AMBIENT_NODE_TAG ambient_nodes") + multiple = ( + "OBJECT_NODE_TAG object_nodes," + "CAMERA_NODE_TAG camera_nodes," + "TARGET_NODE_TAG target_nodes," + "LIGHT_NODE_TAG light_nodes," + "SPOTLIGHT_NODE_TAG spotlight_nodes," + "L_TARGET_NODE_TAG l_target_nodes," + "AMBIENT_NODE_TAG ambient_nodes" + ) + class KFHDR(ChunkBase): tag = 0xB00A struct = "short revision, string filename, long anim_length" + class KFSEG(ChunkBase): tag = 0xB008 struct = "long first_frame, long last_frame" + class KFCURTIME(ChunkBase): tag = 0xB009 struct = "long current_frame" + class AMBIENT_NODE_TAG(ChunkBase): tag = 0xB001 single = "NODE_ID node_id, NODE_HDR node_hdr, COL_TRACK_TAG col_track" + class OBJECT_NODE_TAG(ChunkBase): tag = 0xB002 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "PIVOT pivot," - "INSTANCE_NAME instance_name," - "BOUNDBOX bounding_box," - "POS_TRACK_TAG pos_track," - "ROT_TRACK_TAG rot_track," - "SCL_TRACK_TAG scl_track," - "MORPH_TRACK_TAG morph_track," - "HIDE_TRACK_TAG hide_track," - "MORPH_SMOOTH morph_smooth") + single = ( + "NODE_ID node_id," + "NODE_HDR node_hdr," + "PIVOT pivot," + "INSTANCE_NAME instance_name," + "BOUNDBOX bounding_box," + "POS_TRACK_TAG pos_track," + "ROT_TRACK_TAG rot_track," + "SCL_TRACK_TAG scl_track," + "MORPH_TRACK_TAG morph_track," + "HIDE_TRACK_TAG hide_track," + "MORPH_SMOOTH morph_smooth" + ) class CAMERA_NODE_TAG(ChunkBase): tag = 0xB003 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "POS_TRACK_TAG pos_track," - "FOV_TRACK_TAG fov_track," - "ROLL_TRACK_TAG roll_track") + single = ( + "NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "FOV_TRACK_TAG fov_track," + "ROLL_TRACK_TAG roll_track" + ) + class TARGET_NODE_TAG(ChunkBase): tag = 0xB004 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "POS_TRACK_TAG pos_track") + single = "NODE_ID node_id," "NODE_HDR node_hdr," "POS_TRACK_TAG pos_track" class LIGHT_NODE_TAG(ChunkBase): tag = 0xB005 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "POS_TRACK_TAG pos_track," - "COL_TRACK_TAG col_track") + single = ( + "NODE_ID node_id," "NODE_HDR node_hdr," "POS_TRACK_TAG pos_track," "COL_TRACK_TAG col_track" + ) + class SPOTLIGHT_NODE_TAG(ChunkBase): tag = 0xB007 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "POS_TRACK_TAG pos_track," - "COL_TRACK_TAG col_track," - "HOT_TRACK_TAG hot_track," - "FALL_TRACK_TAG fall_track," - "ROLL_TRACK_TAG roll_track") + single = ( + "NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "COL_TRACK_TAG col_track," + "HOT_TRACK_TAG hot_track," + "FALL_TRACK_TAG fall_track," + "ROLL_TRACK_TAG roll_track" + ) + class L_TARGET_NODE_TAG(ChunkBase): tag = 0xB006 - single = ("NODE_ID node_id," - "NODE_HDR node_hdr," - "POS_TRACK_TAG pos_track," - "COL_TRACK_TAG col_track") + single = ( + "NODE_ID node_id," "NODE_HDR node_hdr," "POS_TRACK_TAG pos_track," "COL_TRACK_TAG col_track" + ) + class NODE_ID(ChunkBase): tag = 0xB030 struct = "short id" + class NODE_HDR(ChunkBase): tag = 0xB010 struct = "string name, short flags1, short flags2, short parent" + class PIVOT(ChunkBase): tag = 0xB013 struct = "float pivot_x, float pivot_y, float pivot_z" + class INSTANCE_NAME(ChunkBase): tag = 0xB011 struct = "string name" + class MORPH_SMOOTH(ChunkBase): tag = 0xB015 struct = "float smooth_angle" + class BOUNDBOX(ChunkBase): tag = 0xB014 - struct = ("float min_x, float min_y, float min_z," - "float max_x, float max_y, float max_z") + struct = "float min_x, float min_y, float min_z," "float max_x, float max_y, float max_z" + class POS_TRACK_TAG(TrackChunk): tag = 0xB020 keyframe = "float pos_x, float pos_y, float pos_z" + class COL_TRACK_TAG(TrackChunk): tag = 0xB025 keyframe = "float red, float green, float blue" + class ROT_TRACK_TAG(TrackChunk): tag = 0xB021 keyframe = "float angle, float axis_x, float axis_y, float axis_z" + class SCL_TRACK_TAG(TrackChunk): tag = 0xB022 keyframe = "float scl_x, float scl_y, float scl_z" + class MORPH_TRACK_TAG(TrackChunk): tag = 0xB026 keyframe = "string name" + class FOV_TRACK_TAG(TrackChunk): tag = 0xB023 keyframe = "float angle" + class ROLL_TRACK_TAG(TrackChunk): tag = 0xB024 keyframe = "float angle" + class HOT_TRACK_TAG(TrackChunk): tag = 0xB027 keyframe = "float angle" + class FALL_TRACK_TAG(TrackChunk): tag = 0xB028 keyframe = "float angle" + class HIDE_TRACK_TAG(UndefinedChunk): tag = 0xB029 + # # Misc # -class DUMMY(ChunkBase): tag = 0xFFFF + +class DUMMY(ChunkBase): + tag = 0xFFFF + # # Obsolete Chunks # -class VIEWPORT_LAYOUT_OLD(UndefinedChunk): tag = 0x7000 -class VIEWPORT_DATA_OLD(UndefinedChunk): tag = 0x7010 -class OLD_MAT_GROUP(UndefinedChunk): tag = 0x4131 -class MAT_MAP_TILING_OLD(UndefinedChunk): tag = 0xA350 -class MAT_MAP_TEXBLUR_OLD(UndefinedChunk): tag = 0xA352 + +class VIEWPORT_LAYOUT_OLD(UndefinedChunk): + tag = 0x7000 + + +class VIEWPORT_DATA_OLD(UndefinedChunk): + tag = 0x7010 + + +class OLD_MAT_GROUP(UndefinedChunk): + tag = 0x4131 + + +class MAT_MAP_TILING_OLD(UndefinedChunk): + tag = 0xA350 + + +class MAT_MAP_TEXBLUR_OLD(UndefinedChunk): + tag = 0xA352 # # Functions to operate on chunks # -def read_3ds_mem(membuf,check_magic=True,tight=False,recover=True): + +def read_3ds_mem(membuf, check_magic=True, tight=False, recover=True): """Create a 3DS DOM from a memory buffer. dom = read_3ds_mem(buffer,check_magic=True,tight=False, @@ -1751,15 +2091,15 @@ def read_3ds_mem(membuf,check_magic=True,tight=False,recover=True): """ if check_magic: - tag,length = struct.unpack(" sinB2 + sinC2, numpy.pi - angA, angA) angB = numpy.where(sinB2 > sinA2 + sinC2, numpy.pi - angB, angB) angC = numpy.where(sinC2 > sinA2 + sinB2, numpy.pi - angC, angC) - rnorms[0::3][ndg] = p*(angA/lp)[:,numpy.newaxis] - rnorms[1::3][ndg] = p*(angB/lp)[:,numpy.newaxis] - rnorms[2::3][ndg] = p*(angC/lp)[:,numpy.newaxis] + rnorms[0::3][ndg] = p * (angA / lp)[:, numpy.newaxis] + rnorms[1::3][ndg] = p * (angB / lp)[:, numpy.newaxis] + rnorms[2::3][ndg] = p * (angC / lp)[:, numpy.newaxis] # normalize vectors according to passed in smoothing group lex = numpy.lexsort(numpy.transpose(points)) - brs = numpy.nonzero( - numpy.any(points[lex[1:],:]-points[lex[:-1],:],axis=1))[0]+1 - lslice = numpy.empty((len(brs)+1,),numpy.int) + brs = numpy.nonzero(numpy.any(points[lex[1:], :] - points[lex[:-1], :], axis=1))[0] + 1 + lslice = numpy.empty((len(brs) + 1,), numpy.int) lslice[0] = 0 lslice[1:] = brs - rslice = numpy.empty((len(brs)+1,),numpy.int) + rslice = numpy.empty((len(brs) + 1,), numpy.int) rslice[:-1] = brs - rslice[-1] = 3*m - for i in range(len(brs)+1): - rgroup = lex[lslice[i]:rslice[i]] + rslice[-1] = 3 * m + for i in range(len(brs) + 1): + rgroup = lex[lslice[i] : rslice[i]] xgroup = exarray[rgroup] - normpat = numpy.logical_or( - numpy.bitwise_and.outer(xgroup,xgroup), - numpy.eye(len(xgroup))) - fnorms[rgroup,:] = numpy.dot(normpat,rnorms[rgroup,:]) - q = numpy.sum(fnorms*fnorms,axis=1) + normpat = numpy.logical_or(numpy.bitwise_and.outer(xgroup, xgroup), numpy.eye(len(xgroup))) + fnorms[rgroup, :] = numpy.dot(normpat, rnorms[rgroup, :]) + q = numpy.sum(fnorms * fnorms, axis=1) qnz = numpy.nonzero(q)[0] lq = 1.0 / numpy.sqrt(q[qnz]) fnt = numpy.transpose(fnorms) - fnt[:,qnz] *= lq + fnt[:, qnz] *= lq # we're done - return points, numpy.asarray(fnorms,numpy.float32) + return points, numpy.asarray(fnorms, numpy.float32) diff --git a/src/Mod/BIM/Init.py b/src/Mod/BIM/Init.py index 5abea05b69..82f90777b3 100644 --- a/src/Mod/BIM/Init.py +++ b/src/Mod/BIM/Init.py @@ -24,18 +24,18 @@ # add import/export types -FreeCAD.addExportType("Industry Foundation Classes (*.ifc)","importers.exportIFC") +FreeCAD.addExportType("Industry Foundation Classes (*.ifc)", "importers.exportIFC") # FreeCAD.addImportType("Industry Foundation Classes (*.ifc)","importIFC") FreeCAD.addImportType("Industry Foundation Classes (*.ifc)", "nativeifc.ifc_import") -FreeCAD.addExportType("Industry Foundation Classes - IFCJSON (*.ifcJSON)","importers.exportIFC") -FreeCAD.addImportType("Wavefront OBJ - Arch module (*.obj *.OBJ)","importers.importOBJ") -FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importers.importOBJ") -FreeCAD.addExportType("WebGL file (*.html)","importers.importWebGL") -FreeCAD.addExportType("JavaScript Object Notation (*.json)","importers.importJSON") -FreeCAD.addImportType("Collada (*.dae *.DAE)","importers.importDAE") -FreeCAD.addExportType("Collada (*.dae)","importers.importDAE") -FreeCAD.addImportType("3D Studio mesh (*.3ds *.3DS)","importers.import3DS") -FreeCAD.addImportType("SweetHome3D (*.sh3d)","importers.importSH3D") -FreeCAD.addImportType("Shapefile (*.shp *.SHP)","importers.importSHP") +FreeCAD.addExportType("Industry Foundation Classes - IFCJSON (*.ifcJSON)", "importers.exportIFC") +FreeCAD.addImportType("Wavefront OBJ - Arch module (*.obj *.OBJ)", "importers.importOBJ") +FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)", "importers.importOBJ") +FreeCAD.addExportType("WebGL file (*.html)", "importers.importWebGL") +FreeCAD.addExportType("JavaScript Object Notation (*.json)", "importers.importJSON") +FreeCAD.addImportType("Collada (*.dae *.DAE)", "importers.importDAE") +FreeCAD.addExportType("Collada (*.dae)", "importers.importDAE") +FreeCAD.addImportType("3D Studio mesh (*.3ds *.3DS)", "importers.import3DS") +FreeCAD.addImportType("SweetHome3D (*.sh3d)", "importers.importSH3D") +FreeCAD.addImportType("Shapefile (*.shp *.SHP)", "importers.importSHP") -FreeCAD.__unit_test__ += ["TestArch"] \ No newline at end of file +FreeCAD.__unit_test__ += ["TestArch"] diff --git a/src/Mod/BIM/InitGui.py b/src/Mod/BIM/InitGui.py index 368523ad73..9173b6bde5 100644 --- a/src/Mod/BIM/InitGui.py +++ b/src/Mod/BIM/InitGui.py @@ -30,6 +30,7 @@ import FreeCAD import FreeCADGui import Arch_rc + class BIMWorkbench(Workbench): def __init__(self): @@ -38,11 +39,10 @@ class BIMWorkbench(Workbench): return text bdir = os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM") - tt = QT_TRANSLATE_NOOP("BIM","The BIM workbench is used to model buildings") + tt = QT_TRANSLATE_NOOP("BIM", "The BIM workbench is used to model buildings") self.__class__.MenuText = QT_TRANSLATE_NOOP("BIM", "BIM") - self.__class__.ToolTip = tt - self.__class__.Icon = os.path.join(bdir,"Resources", "icons", - "BIMWorkbench.svg") + self.__class__.ToolTip = tt + self.__class__.Icon = os.path.join(bdir, "Resources", "icons", "BIMWorkbench.svg") def Initialize(self): @@ -59,9 +59,7 @@ class BIMWorkbench(Workbench): Log("Loading BIM module… done\n") FreeCADGui.updateLocale() - def createTools(self): - "Create tolbars and menus" def QT_TRANSLATE_NOOP(context, text): @@ -108,9 +106,9 @@ class BIMWorkbench(Workbench): ] self.create_2dviews = [ - "BIM_DrawingView", - "BIM_Shape2DView", - "BIM_Shape2DCut", + "BIM_DrawingView", + "BIM_Shape2DView", + "BIM_Shape2DCut", ] self.bimtools = [ @@ -187,7 +185,9 @@ class BIMWorkbench(Workbench): ] sep = ["Separator"] - self.modify = self.modify_gen + sep + self.modify_2d + sep + self.modify_obj + sep + self.modify_3d + self.modify = ( + self.modify_gen + sep + self.modify_2d + sep + self.modify_obj + sep + self.modify_3d + ) self.manage = [ "BIM_Setup", @@ -250,6 +250,7 @@ class BIMWorkbench(Workbench): # append BIM snaps from draftutils import init_tools + self.snapbar = init_tools.get_draft_snap_commands() self.snapmenu = self.snapbar + [ "BIM_SetWPFront", @@ -262,14 +263,18 @@ class BIMWorkbench(Workbench): class BIM_GenericTools: def __init__(self, tools): self.tools = tools + def GetCommands(self): return self.tools + def GetResources(self): t = QT_TRANSLATE_NOOP("BIM_GenericTools", "Generic 3D Tools") - return { "MenuText": t, "ToolTip": t, "Icon": "BIM_Box"} + return {"MenuText": t, "ToolTip": t, "Icon": "BIM_Box"} + def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v + FreeCADGui.addCommand("BIM_GenericTools", BIM_GenericTools(self.generictools)) self.bimtools.append("BIM_GenericTools") @@ -277,14 +282,18 @@ class BIMWorkbench(Workbench): class BIM_Create2DViews: def __init__(self, tools): self.tools = tools + def GetCommands(self): return self.tools + def GetResources(self): t = QT_TRANSLATE_NOOP("BIM_Create2DViews", "Create 2D Views") - return { "MenuText": t, "ToolTip": t, "Icon": "BIM_DrawingView"} + return {"MenuText": t, "ToolTip": t, "Icon": "BIM_DrawingView"} + def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v + FreeCADGui.addCommand("BIM_Create2DViews", BIM_Create2DViews(self.create_2dviews)) insert_at_index = self.annotationtools.index("BIM_TDPage") self.annotationtools.insert(insert_at_index, "BIM_Create2DViews") @@ -303,12 +312,8 @@ class BIMWorkbench(Workbench): def GetResources(self): return { - "MenuText": QT_TRANSLATE_NOOP( - "Arch_RebarTools", "Reinforcement Tools" - ), - "ToolTip": QT_TRANSLATE_NOOP( - "Arch_RebarTools", "Reinforcement tools" - ), + "MenuText": QT_TRANSLATE_NOOP("Arch_RebarTools", "Reinforcement Tools"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_RebarTools", "Reinforcement tools"), "Icon": "Arch_Rebar", } @@ -322,7 +327,7 @@ class BIMWorkbench(Workbench): Log("Load Reinforcement Module… done\n") if hasattr(RebarTools, "updateLocale"): RebarTools.updateLocale() - #self.rebar = RebarTools.RebarCommands + ["Arch_Rebar"] + # self.rebar = RebarTools.RebarCommands + ["Arch_Rebar"] # load Reporting @@ -401,9 +406,7 @@ class BIMWorkbench(Workbench): fasteners = None else: fasteners = [ - c - for c in FastenerBase.FSGetCommands("screws") - if not isinstance(c, tuple) + c for c in FastenerBase.FSGetCommands("screws") if not isinstance(c, tuple) ] # load nativeifc tools @@ -433,31 +436,31 @@ class BIMWorkbench(Workbench): # create menus - t1 = QT_TRANSLATE_NOOP("Workbench", "&2D Drafting") - t2 = QT_TRANSLATE_NOOP("Workbench", "&3D/BIM") - t3 = QT_TRANSLATE_NOOP("Workbench", "Reinforcement Tools") - t4 = QT_TRANSLATE_NOOP("Workbench", "&Annotation") - t5 = QT_TRANSLATE_NOOP("Workbench", "&Snapping") - t6 = QT_TRANSLATE_NOOP("Workbench", "&Modify") - t7 = QT_TRANSLATE_NOOP("Workbench", "&Manage") - #t8 = QT_TRANSLATE_NOOP("Workbench", "&IFC") - t9 = QT_TRANSLATE_NOOP("Workbench", "&Flamingo") + t1 = QT_TRANSLATE_NOOP("Workbench", "&2D Drafting") + t2 = QT_TRANSLATE_NOOP("Workbench", "&3D/BIM") + t3 = QT_TRANSLATE_NOOP("Workbench", "Reinforcement Tools") + t4 = QT_TRANSLATE_NOOP("Workbench", "&Annotation") + t5 = QT_TRANSLATE_NOOP("Workbench", "&Snapping") + t6 = QT_TRANSLATE_NOOP("Workbench", "&Modify") + t7 = QT_TRANSLATE_NOOP("Workbench", "&Manage") + # t8 = QT_TRANSLATE_NOOP("Workbench", "&IFC") + t9 = QT_TRANSLATE_NOOP("Workbench", "&Flamingo") t10 = QT_TRANSLATE_NOOP("Workbench", "&Fasteners") t11 = QT_TRANSLATE_NOOP("Workbench", "&Utils") t12 = QT_TRANSLATE_NOOP("Workbench", "Nudge") - #self.bimtools_menu = list(self.bimtools) - #if "Arch_RebarTools" in self.bimtools_menu: + # self.bimtools_menu = list(self.bimtools) + # if "Arch_RebarTools" in self.bimtools_menu: # self.bimtools_menu.remove("Arch_RebarTools") self.appendMenu(t1, self.draftingtools) self.appendMenu(t2, self.bimtools) - #if self.rebar: + # if self.rebar: # self.appendMenu([t2, t3], self.rebar) self.appendMenu(t4, self.annotationtools) self.appendMenu(t5, self.snapmenu) self.appendMenu(t6, self.modify) self.appendMenu(t7, self.manage) - #if ifctools: + # if ifctools: # self.appendMenu(t8, ifctools) if flamingo: self.appendMenu(t9, flamingo) @@ -467,7 +470,6 @@ class BIMWorkbench(Workbench): self.appendMenu([t11, t12], nudge) def loadPreferences(self): - """Set up preferences pages""" def QT_TRANSLATE_NOOP(context, text): @@ -482,6 +484,7 @@ class BIMWorkbench(Workbench): if hasattr(FreeCADGui.draftToolBar, "loadedPreferences"): return from draftutils import params + params._param_observer_start() FreeCADGui.addPreferencePage(":/ui/preferences-draft.ui", t2) FreeCADGui.addPreferencePage(":/ui/preferences-draftinterface.ui", t2) @@ -494,9 +497,7 @@ class BIMWorkbench(Workbench): import BimSelect - if hasattr(FreeCADGui, "addDocumentObserver") and not hasattr( - self, "BimSelectObserver" - ): + if hasattr(FreeCADGui, "addDocumentObserver") and not hasattr(self, "BimSelectObserver"): self.BimSelectObserver = BimSelect.Setup() FreeCADGui.addDocumentObserver(self.BimSelectObserver) @@ -572,6 +573,7 @@ class BIMWorkbench(Workbench): {"insert": "BIM_Help", "menuItem": "Std_ReportBug", "after": ""}, {"insert": "BIM_Welcome", "menuItem": "Std_ReportBug", "after": ""}, ] + reload = hasattr(Gui, "BIM_WBManipulator") # BIM WB has previously been loaded. if not getattr(Gui, "BIM_WBManipulator", None): Gui.BIM_WBManipulator = BIM_WBManipulator() @@ -621,6 +623,7 @@ class BIMWorkbench(Workbench): # Ifc stuff try: from nativeifc import ifc_status + ifc_status.toggle_lock(False) except: pass @@ -633,10 +636,10 @@ class BIMWorkbench(Workbench): Log("BIM workbench deactivated\n") - def ContextMenu(self, recipient): import DraftTools + translate = FreeCAD.Qt.translate if recipient == "Tree": @@ -652,9 +655,9 @@ class BIMWorkbench(Workbench): break for o in FreeCADGui.Selection.getSelection(): for parent in o.InList: - if parent.isDerivedFrom( - "App::DocumentObjectGroup" - ) or parent.hasExtension("App::GroupExtension"): + if parent.isDerivedFrom("App::DocumentObjectGroup") or parent.hasExtension( + "App::GroupExtension" + ): if o in parent.Group: ungroupable = True else: @@ -700,8 +703,11 @@ FreeCADGui.addWorkbench(BIMWorkbench) # Preference pages for importing and exporting various file formats # are independent of the loading of the workbench and can be loaded at startup + def QT_TRANSLATE_NOOP(context, text): return text + + t = QT_TRANSLATE_NOOP("QObject", "Import-Export") FreeCADGui.addPreferencePage(":/ui/preferences-ifc.ui", t) FreeCADGui.addPreferencePage(":/ui/preferences-ifc-export.ui", t) diff --git a/src/Mod/BIM/OfflineRenderingUtils.py b/src/Mod/BIM/OfflineRenderingUtils.py index 5bede0d08c..bda1f588e8 100755 --- a/src/Mod/BIM/OfflineRenderingUtils.py +++ b/src/Mod/BIM/OfflineRenderingUtils.py @@ -113,9 +113,7 @@ import xml.sax import zipfile - class FreeCADGuiHandler(xml.sax.ContentHandler): - "A XML handler to process the FreeCAD GUI xml data, used by getGuiData()" # this creates a dictionary where each key is a FC object name, @@ -148,10 +146,10 @@ class FreeCADGuiHandler(xml.sax.ContentHandler): self.currentval = False elif tag == "PropertyColor": c = int(attributes["value"]) - r = float((c>>24)&0xFF)/255.0 - g = float((c>>16)&0xFF)/255.0 - b = float((c>>8)&0xFF)/255.0 - self.currentval = (r,g,b) + r = float((c >> 24) & 0xFF) / 255.0 + g = float((c >> 16) & 0xFF) / 255.0 + b = float((c >> 8) & 0xFF) / 255.0 + self.currentval = (r, g, b) elif tag == "Integer": self.currentval = int(attributes["value"]) elif tag == "String": @@ -167,15 +165,20 @@ class FreeCADGuiHandler(xml.sax.ContentHandler): e = int(attributes["emissiveColor"]) i = float(attributes["shininess"]) t = float(attributes["transparency"]) - self.currentval = (a,d,s,e,i,t) + self.currentval = (a, d, s, e, i, t) elif tag == "Enum": - if isinstance(self.currentval,int): - self.currentval = [self.currentval,attributes["value"]] - elif isinstance(self.currentval,list): + if isinstance(self.currentval, int): + self.currentval = [self.currentval, attributes["value"]] + elif isinstance(self.currentval, list): self.currentval.append(attributes["value"]) elif tag == "Python": if "module" in attributes: - self.currentval = (attributes["value"],attributes["encoded"],attributes["module"],attributes["class"]) + self.currentval = ( + attributes["value"], + attributes["encoded"], + attributes["module"], + attributes["class"], + ) elif tag == "Camera": self.guidata["GuiCameraSettings"] = attributes["settings"] @@ -190,15 +193,16 @@ class FreeCADGuiHandler(xml.sax.ContentHandler): self.properties = {} elif tag == "Property": if self.currentprop and (self.currentval is not None): - self.properties[self.currentprop] = {"type":self.currenttype,"value":self.currentval} + self.properties[self.currentprop] = { + "type": self.currenttype, + "value": self.currentval, + } self.currentprop = None self.currentval = None self.currenttype = None - def getGuiData(filename): - """getGuiData(filename): Extract visual data from a saved FreeCAD file. Returns a dictionary ["objectName:dict] where dict contains properties keys like ShapeAppearaance, Transparency, DiffuseColor or Visibility. If found, @@ -214,11 +218,11 @@ def getGuiData(filename): Handler = FreeCADGuiHandler() xml.sax.parseString(guidata, Handler) guidata = Handler.guidata - for key,properties in guidata.items(): + for key, properties in guidata.items(): # open each diffusecolor files and retrieve values # first 4 bytes are the array length, then each group of 4 bytes is abgr # https://forum.freecad.org/viewtopic.php?t=29382 - if isinstance(properties,dict): + if isinstance(properties, dict): for propname in properties.keys(): if properties[propname]["type"] == "App::PropertyColorList": if not guidata[key][propname]["value"]: @@ -227,28 +231,34 @@ def getGuiData(filename): buf = df.read() df.close() cols = [] - for i in range(1,int(len(buf)/4)): - cols.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0)) + for i in range(1, int(len(buf) / 4)): + cols.append( + ( + buf[i * 4 + 3] / 255.0, + buf[i * 4 + 2] / 255.0, + buf[i * 4 + 1] / 255.0, + buf[i * 4] / 255.0, + ) + ) guidata[key][propname]["value"] = cols zdoc.close() - #print ("guidata:",guidata) + # print ("guidata:",guidata) return guidata - def saveDiffuseColor(colorlist): - """saveDiffuseColor(colorlist): Saves the given list or tuple of color tuples to a temp file, suitable to include in a DiffuseColor property. Returns the path to the created temp file""" def tochr(i): return bytes((i,)) + # if too many colors, bail out and use only the first one for now... if len(colorlist) > 254: colorlist = colorlist[:1] print("debug: too many colors, reducing") - output = tochr(len(colorlist))+3*tochr(0) + output = tochr(len(colorlist)) + 3 * tochr(0) allfloats = True for color in colorlist: for d in color: @@ -260,19 +270,17 @@ def saveDiffuseColor(colorlist): output += tochr(0) for d in reversed(color): if allfloats: - output += tochr(int(d*255)) + output += tochr(int(d * 255)) else: output += tochr(int(d)) colfile = tempfile.mkstemp(prefix="DiffuseColor")[-1] - f = open(colfile,"wb") + f = open(colfile, "wb") f.write(output) f.close() return colfile - -def getColors(filename,nodiffuse=False): - +def getColors(filename, nodiffuse=False): """getColors(filename,nodiffuse): Extracts the colors saved in a FreeCAD file Returns a dictionary containing ["objectName":colors] pairs. colrs can be either a 3-element tuple representing an RGB color, if @@ -284,7 +292,7 @@ def getColors(filename,nodiffuse=False): guidata = getGuiData(filename) colors = {} - for k,v in guidata.items(): + for k, v in guidata.items(): if ("DiffuseColor" in v) and (not nodiffuse): if len(v["DiffuseColor"]["value"]) == 1: # only one color in DiffuseColor: used for the whole object @@ -296,9 +304,7 @@ def getColors(filename,nodiffuse=False): return colors - -def getStepData(objects,colors): - +def getStepData(objects, colors): """getStepData(objects,colors): transforms the given list of objects and colors dictionary into a list of tuples acceptable by the STEP exporter of FreeCAD's Import module""" @@ -312,21 +318,28 @@ def getStepData(objects,colors): for obj in objects: if obj.Name in colors: color = colors[obj.Name] - if isinstance(color,tuple): + if isinstance(color, tuple): # this is a ShapeAppeaaraance. We reformat as a list so it works as a DiffuseColor, # which is what the exporter expects. DiffuseColor can have either one color, # or the same number of colors as the number of faces color = [color] - data.append((obj,color)) + data.append((obj, color)) else: # no color information. This object will be exported without colors data.append(obj) return data - -def render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,background=(1.0,1.0,1.0),lightdir=None): - +def render( + outputfile, + scene=None, + camera=None, + zoom=False, + width=400, + height=300, + background=(1.0, 1.0, 1.0), + lightdir=None, +): """render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,background=(1.0,1.0,1.0),lightdir=None): Renders a PNG image of given width and height and background color from the given coin scene, using the given coin camera (ortho or perspective). If zoom is True the camera will be resized to fit all @@ -347,7 +360,7 @@ def render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,bac from pivy import coin - if isinstance(camera,str): + if isinstance(camera, str): camera = getCoinCamera(camera) print("Starting offline renderer") @@ -361,8 +374,8 @@ def render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,bac # create a default camera if none was given camera = coin.SoPerspectiveCamera() cameraRotation = coin.SbRotation.identity() - cameraRotation *= coin.SbRotation(coin.SbVec3f(1,0,0),1.0) - cameraRotation *= coin.SbRotation(coin.SbVec3f(0,0,1),0.4) + cameraRotation *= coin.SbRotation(coin.SbVec3f(1, 0, 0), 1.0) + cameraRotation *= coin.SbRotation(coin.SbVec3f(0, 0, 1), 0.4) camera.orientation = cameraRotation # make sure all objects get in the view later zoom = True @@ -374,28 +387,26 @@ def render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,bac cube = coin.SoCube() root.addChild(cube) if lightdir: - root = embedLight(root,lightdir) - vpRegion = coin.SbViewportRegion(width,height) + root = embedLight(root, lightdir) + vpRegion = coin.SbViewportRegion(width, height) if zoom: - camera.viewAll(root,vpRegion) + camera.viewAll(root, vpRegion) print("Creating viewport") offscreenRenderer = coin.SoOffscreenRenderer(vpRegion) - offscreenRenderer.setBackgroundColor(coin.SbColor(background[0],background[1],background[2])) + offscreenRenderer.setBackgroundColor(coin.SbColor(background[0], background[1], background[2])) print("Ready to render") # ref ensures that the node will not be garbage-collected during rendering root.ref() ok = offscreenRenderer.render(root) root.unref() if ok: - offscreenRenderer.writeToFile(outputfile,"PNG") - print("Rendering",outputfile,"done") + offscreenRenderer.writeToFile(outputfile, "PNG") + print("Rendering", outputfile, "done") else: print("Error rendering image") - -def buildScene(objects,colors=None): - +def buildScene(objects, colors=None): """buildScene(objects,colors=None): builds a coin node from a given list of FreeCAD objects. Optional colors argument can be a dictionary of objName:ShapeColorTuple or obj:DiffuseColorList pairs.""" @@ -405,9 +416,9 @@ def buildScene(objects,colors=None): root = coin.SoSeparator() for o in objects: buf = None - if hasattr(o,'Shape') and o.Shape and (not o.Shape.isNull()): + if hasattr(o, "Shape") and o.Shape and (not o.Shape.isNull()): # writeInventor of shapes needs tessellation values - buf = o.Shape.writeInventor(2,0.01) + buf = o.Shape.writeInventor(2, 0.01) elif o.isDerivedFrom("Mesh::Feature"): buf = o.Mesh.writeInventor() if buf: @@ -420,20 +431,18 @@ def buildScene(objects,colors=None): if o.Name in colors: # insert a material node at 1st position, before the geometry color = colors[o.Name] - if isinstance(color,list): + if isinstance(color, list): # DiffuseColor, not supported here color = color[0] color = color[:3] mat = coin.SoMaterial() mat.diffuseColor = color - node.insertChild(mat,0) + node.insertChild(mat, 0) root.addChild(node) return root - def getCamera(filepath): - """getCamera(filepath): Returns a string representing a coin camera node from a given FreeCAD file, or None if none was found inside""" @@ -444,9 +453,7 @@ def getCamera(filepath): return None - def getCoinCamera(camerastring): - """getCoinCamera(camerastring): Returns a coin camera node from a string""" from pivy import coin @@ -459,13 +466,11 @@ def getCoinCamera(camerastring): for child in node.getChildren(): if ("SoOrthographicCamera" in str(child)) or ("SoPerspectiveCamera" in str(child)): return child - print("unable to build a camera node from string:",camerastring) + print("unable to build a camera node from string:", camerastring) return None - -def viewer(scene=None,background=(1.0,1.0,1.0),lightdir=None): - +def viewer(scene=None, background=(1.0, 1.0, 1.0), lightdir=None): """viewer(scene=None,background=(1.0,1.0,1.0),lightdir=None): starts a standalone coin viewer with the contents of the given scene. You can give a background color, and optionally a light direction as a (x,y,z) @@ -485,7 +490,7 @@ def viewer(scene=None,background=(1.0,1.0,1.0),lightdir=None): print("Unable to create a Quarter window") return - win.setBackgroundColor(coin.SbColor(background[0],background[1],background[2])) + win.setBackgroundColor(coin.SbColor(background[0], background[1], background[2])) if not scene: # build a quick default scene @@ -497,7 +502,7 @@ def viewer(scene=None,background=(1.0,1.0,1.0),lightdir=None): scene.addChild(coin.SoCone()) if lightdir: - scene = embedLight(scene,lightdir) + scene = embedLight(scene, lightdir) # ref the scene so it doesn't get garbage-collected scene.ref() @@ -512,8 +517,7 @@ def viewer(scene=None,background=(1.0,1.0,1.0),lightdir=None): app.exec_() -def embedLight(scene,lightdir): - +def embedLight(scene, lightdir): """embedLight(scene,lightdir): embeds a given coin node inside a shadow group with directional light with the given direction (x,y,z) tuple. Returns the final coin node""" @@ -521,25 +525,33 @@ def embedLight(scene,lightdir): from pivy import coin # buggy - no SoShadowGroup in pivy? - #sgroup = coin.SoShadowGroup() - #sgroup.quality = 1 - #sgroup.precision = 1 - #slight = SoShadowDirectionalLight() - #slight.direction = lightdir - #slight.intensity = 20.0 + # sgroup = coin.SoShadowGroup() + # sgroup.quality = 1 + # sgroup.precision = 1 + # slight = SoShadowDirectionalLight() + # slight.direction = lightdir + # slight.intensity = 20.0 - buf = """ + buf = ( + """ #Inventor V2.1 ascii ShadowGroup { quality 1 precision 1 ShadowDirectionalLight { - direction """+str(lightdir[0])+" "+str(lightdir[1])+" "+str(lightdir[2])+""" + direction """ + + str(lightdir[0]) + + " " + + str(lightdir[1]) + + " " + + str(lightdir[2]) + + """ intensity 200.0 # enable this to reduce the shadow view distance # maxShadowDistance 200 } }""" + ) inp = coin.SoInput() inp.setBuffer(buf) @@ -549,33 +561,31 @@ ShadowGroup { return sgroup - -def save(document,filename=None,guidata=None,colors=None,camera=None): - +def save(document, filename=None, guidata=None, colors=None, camera=None): """save(document,filename=None,guidata=None,colors=None,camera=None): Saves the current document. If no filename - is given, the filename stored in the document (document.FileName) is used. + is given, the filename stored in the document (document.FileName) is used. - You can provide a guidata dictionary, which can be obtained by the getGuiData() function, and has the form: + You can provide a guidata dictionary, which can be obtained by the getGuiData() function, and has the form: - { "objectName" : - { "propertyName" : - { "type" : "App::PropertyString", - "value" : "My Value" - } - } - } + { "objectName" : + { "propertyName" : + { "type" : "App::PropertyString", + "value" : "My Value" + } + } + } - The type of the "value" contents depends on the type (int, string, float,tuple...) see inside the FreeCADGuiHandler - class to get an idea. + The type of the "value" contents depends on the type (int, string, float,tuple...) see inside the FreeCADGuiHandler + class to get an idea. - If guidata is provided, colors and camera attributes are discarded. + If guidata is provided, colors and camera attributes are discarded. - Alternatively, a color dictionary of objName:ShapeColorTuple or obj:DiffuseColorList pairs.can be provided, - in that case the objects will keep their colors when opened in the FreeCAD GUI. If given, camera is a string - representing a coin camera node.""" + Alternatively, a color dictionary of objName:ShapeColorTuple or obj:DiffuseColorList pairs.can be provided, + in that case the objects will keep their colors when opened in the FreeCAD GUI. If given, camera is a string + representing a coin camera node.""" if filename: - print("Saving as",filename) + print("Saving as", filename) document.saveAs(filename) else: if document.FileName: @@ -586,47 +596,43 @@ def save(document,filename=None,guidata=None,colors=None,camera=None): return if guidata: - guidocs = buildGuiDocumentFromGuiData(document,guidata) + guidocs = buildGuiDocumentFromGuiData(document, guidata) if guidocs: - zf = zipfile.ZipFile(filename, mode='a') + zf = zipfile.ZipFile(filename, mode="a") for guidoc in guidocs: - zf.write(guidoc[0],guidoc[1]) + zf.write(guidoc[0], guidoc[1]) zf.close() # delete the temp files for guidoc in guidocs: os.remove(guidoc[0]) elif colors: - guidoc = buildGuiDocumentFromColors(document,colors,camera) + guidoc = buildGuiDocumentFromColors(document, colors, camera) if guidoc: - zf = zipfile.ZipFile(filename, mode='a') - zf.write(guidoc[0],'GuiDocument.xml') + zf = zipfile.ZipFile(filename, mode="a") + zf.write(guidoc[0], "GuiDocument.xml") for colorfile in guidoc[1:]: - zf.write(colorfile,os.path.basename(colorfile)) + zf.write(colorfile, os.path.basename(colorfile)) zf.close() # delete the temp files for tfile in guidoc: os.remove(tfile) - def getUnsigned(color): - """getUnsigned(color): returns an unsigned int from a (r,g,b) color tuple""" if (color[0] <= 1) and (color[1] <= 1) and (color[2] <= 1): # 0->1 float colors, convert to 0->255 - color = (color[0]*255.0,color[1]*255.0,color[2]*255.0) + color = (color[0] * 255.0, color[1] * 255.0, color[2] * 255.0) # ensure everything is int otherwise bit ops below don't work - color = (int(color[0]),int(color[1]),int(color[2])) + color = (int(color[0]), int(color[1]), int(color[2])) # https://forum.freecad.org/viewtopic.php?t=19074 return str(color[0] << 24 | color[1] << 16 | color[2] << 8) - -def buildGuiDocumentFromColors(document,colors,camera=None): - +def buildGuiDocumentFromColors(document, colors, camera=None): """buildGuiDocumentFromColors(document,colors,camera=None): Returns the path to a temporary GuiDocument.xml for the given document. Colors is a color dictionary of objName:ShapeColorTuple or obj:DiffuseColorList. Camera, if given, is a string representing a coin camera. You must delete the temporary file after using it.""" @@ -634,68 +640,76 @@ def buildGuiDocumentFromColors(document,colors,camera=None): if not camera: camera = "OrthographicCamera { viewportMapping ADJUST_CAMERA position 0 -0 20000 orientation 0.0, 0.8939966636005564, 0.0, -0.44807361612917324 nearDistance 7561.228 farDistance 63175.688 aspectRatio 1 focalDistance 35368.102 height 2883.365 }" - guidoc = "\n" + guidoc = "\n" guidoc += "\n" - guidoc += "\n" + guidoc += '\n' colfiles = [] vps = [obj for obj in document.Objects if obj.Name in colors] if not vps: return None - guidoc += " \n" + guidoc += ' \n' for vp in vps: - guidoc += " \n" - guidoc += " \n" + guidoc += ' \n' + guidoc += ' \n' vpcol = colors[vp.Name] - if isinstance(vpcol[0],tuple): + if isinstance(vpcol[0], tuple): # distinct diffuse colors colfile = saveDiffuseColor(vpcol) name = os.path.basename(colfile) colfiles.append(colfile) - guidoc += " \n" - guidoc += " \n" + guidoc += ( + ' \n' + ) + guidoc += ' \n' guidoc += " \n" else: - guidoc += " \n" - guidoc += " \n" + guidoc += ' \n' + guidoc += ' \n' guidoc += " \n" - guidoc += " \n" - guidoc += " \n" + guidoc += ' \n' + guidoc += ' \n' guidoc += " \n" - if hasattr(vp,"Proxy"): + if hasattr(vp, "Proxy"): # if this is a python feature, store the view provider class if possible m = getViewProviderClass(vp) if m: - guidoc += " \n" - guidoc += " \n" + guidoc += ( + ' \n' + ) + guidoc += ( + ' \n' + ) guidoc += " \n" guidoc += " \n" guidoc += " \n" - guidoc +=" \n" - guidoc +=" \n" + guidoc += " \n" + guidoc += ' \n' guidoc += "" # although the zipfile module has a writestr() function that should allow us to write the # string above directly to the zip file, I couldn't manage to make it work.. So we rather # use a temp file here, which works. - #print(guidoc) + # print(guidoc) tempxml = tempfile.mkstemp(suffix=".xml")[-1] - f = open(tempxml,"w") + f = open(tempxml, "w") f.write(guidoc) f.close() - return [tempxml]+colfiles + return [tempxml] + colfiles - -def buildGuiDocumentFromGuiData(document,guidata): - +def buildGuiDocumentFromGuiData(document, guidata): """buildGuiDocumentFromColors(document,guidata): Returns the path to a temporary GuiDocument.xml for the given document. GuiData is a dictionary, which can be obtained by the getGuiData() function, and has the form: @@ -713,38 +727,52 @@ def buildGuiDocumentFromGuiData(document,guidata): files = [] colorindex = 1 - guidoc = "\n" + guidoc = "\n" guidoc += "\n" - guidoc += "\n" + guidoc += '\n' vps = [obj for obj in document.Objects if obj.Name in guidata] if not vps: return None - guidoc += " \n" + guidoc += ' \n' for vp in vps: properties = guidata[vp.Name] - guidoc += " \n" - guidoc += " \n" - for name,prop in properties.items(): - guidoc += " \n" - if prop["type"] in ["App::PropertyString","PropertyFont"]: - guidoc += " \n" - elif prop["type"] in ["App::PropertyAngle","App::PropertyFloat","App::PropertyFloatConstraint","App::PropertyDistance","App::PropertyLength"]: - guidoc += " \n" - elif prop["type"] in ["App::PropertyInteger","App::PropertyPercent"]: - guidoc += " \n" + guidoc += ' \n' + guidoc += ' \n' + for name, prop in properties.items(): + guidoc += ' \n' + if prop["type"] in ["App::PropertyString", "PropertyFont"]: + guidoc += ' \n' + elif prop["type"] in [ + "App::PropertyAngle", + "App::PropertyFloat", + "App::PropertyFloatConstraint", + "App::PropertyDistance", + "App::PropertyLength", + ]: + guidoc += ' \n' + elif prop["type"] in ["App::PropertyInteger", "App::PropertyPercent"]: + guidoc += ' \n' elif prop["type"] in ["App::PropertyBool"]: - guidoc += " \n" + guidoc += ' \n' elif prop["type"] in ["App::PropertyEnumeration"]: - if isinstance(prop["value"],int): - guidoc += " \n" - elif isinstance(prop["value"],list): - guidoc += " \n" - guidoc += " \n" + if isinstance(prop["value"], int): + guidoc += ' \n' + elif isinstance(prop["value"], list): + guidoc += ( + ' \n' + ) + guidoc += ( + ' \n' + ) for v in prop["value"][1:]: - guidoc += " \n" + guidoc += ' \n' guidoc += " \n" elif prop["type"] in ["App::PropertyColorList"]: # DiffuseColor: first 4 bytes of file tells number of Colors @@ -780,60 +808,76 @@ def buildGuiDocumentFromGuiData(document,guidata): # fill colors in abgr order for color in prop["value"]: if len(color) >= 4: - buf += binascii.unhexlify(hex(int(color[3]*255))[2:].zfill(2)) + buf += binascii.unhexlify(hex(int(color[3] * 255))[2:].zfill(2)) else: buf += binascii.unhexlify(hex(0)[2:].zfill(2)) - buf += binascii.unhexlify(hex(int(color[2]*255))[2:].zfill(2)) - buf += binascii.unhexlify(hex(int(color[1]*255))[2:].zfill(2)) - buf += binascii.unhexlify(hex(int(color[0]*255))[2:].zfill(2)) + buf += binascii.unhexlify(hex(int(color[2] * 255))[2:].zfill(2)) + buf += binascii.unhexlify(hex(int(color[1] * 255))[2:].zfill(2)) + buf += binascii.unhexlify(hex(int(color[0] * 255))[2:].zfill(2)) tempcolorfile = tempfile.mkstemp(suffix=".xml")[-1] - f = open(tempcolorfile,"wb") + f = open(tempcolorfile, "wb") f.write(buf) f.close() tempcolorname = "ColorFile" + str(colorindex) colorindex += 1 - guidoc += " \n" - files.append((tempcolorfile,tempcolorname)) + guidoc += ' \n' + files.append((tempcolorfile, tempcolorname)) elif prop["type"] in ["App::PropertyMaterial"]: - guidoc += " \n" + guidoc += ' \n' elif prop["type"] in ["App::PropertyPythonObject"]: - guidoc += " \n" + guidoc += ( + ' \n' elif prop["type"] in ["App::PropertyColor"]: - guidoc += " \n" + guidoc += ( + ' \n' + ) guidoc += " \n" guidoc += " \n" guidoc += " \n" - guidoc +=" \n" + guidoc += " \n" if "GuiCameraSettings" in guidata: - guidoc +=" \n" + guidoc += ' \n' guidoc += "\n" # although the zipfile module has a writestr() function that should allow us to write the # string above directly to the zip file, I couldn't manage to make it work.. So we rather # use a temp file here, which works. - #print(guidoc) + # print(guidoc) tempxml = tempfile.mkstemp(suffix=".xml")[-1] - f = open(tempxml,"w") + f = open(tempxml, "w") f.write(guidoc) f.close() - files.insert(0,(tempxml,"GuiDocument.xml")) + files.insert(0, (tempxml, "GuiDocument.xml")) return files - def getViewProviderClass(obj): - """getViewProviderClass(obj): tries to identify the associated view provider for a - given python object. Returns a (modulename,classname) tuple if found, or None""" + given python object. Returns a (modulename,classname) tuple if found, or None""" - if not hasattr(obj,"Proxy"): + if not hasattr(obj, "Proxy"): return None if not obj.Proxy: return None @@ -845,22 +889,23 @@ def getViewProviderClass(obj): classes.append(mem.__name__) # try to find a matching ViewProvider class if objclass.startswith("_"): - wantedname = "_ViewProvider"+objclass[1:] + wantedname = "_ViewProvider" + objclass[1:] else: - wantedname = "ViewProvider"+objclass + wantedname = "ViewProvider" + objclass if wantedname in classes: - #print("Found matching view provider for",mod,objclass,wantedname) - return (mod,wantedname,) + # print("Found matching view provider for",mod,objclass,wantedname) + return ( + mod, + wantedname, + ) # use the default Draft VP if this is a Draft object if mod == "Draft": - return(mod,"_ViewProviderDraft") - print("Found no matching view provider for",mod,objclass) + return (mod, "_ViewProviderDraft") + print("Found no matching view provider for", mod, objclass) return None - -def extract(filename,inputpath,outputpath=None): - +def extract(filename, inputpath, outputpath=None): """extract(filename,inputpath,outputpath=None): extracts 'inputpath' which is a filename stored in filename (a FreeCAD or zip file). If outputpath is given, the file is saved as outputpath and nothing is returned. If not, the contents of the inputfile are returned and nothing is saved.""" @@ -873,24 +918,22 @@ def extract(filename,inputpath,outputpath=None): gf.close() if data: if outputpath: - if isinstance(data,str): - of = open(outputpath,"w") + if isinstance(data, str): + of = open(outputpath, "w") else: - of = open(outputpath,"wb") + of = open(outputpath, "wb") of.write(data) of.close() else: return data - def openiv(filename): - """openiv(filename): opens an .iv file and returns a coin node from it""" from pivy import coin - f = open(filename,"r") + f = open(filename, "r") buf = f.read() f.close() inp = coin.SoInput() @@ -899,19 +942,16 @@ def openiv(filename): return node - -def saveiv(scene,filename): - +def saveiv(scene, filename): """saveiv(scene,filename): saves an .iv file with the contents of the given coin node""" from pivy import coin - wa=coin.SoWriteAction() + wa = coin.SoWriteAction() wa.getOutput().openFile(filename) wa.getOutput().setBinary(False) wa.apply(scene) wa.getOutput().closeFile() - ## @} diff --git a/src/Mod/BIM/Presets/ifc_contexts_IFC4.json b/src/Mod/BIM/Presets/ifc_contexts_IFC4.json index 3585bb57a3..478bcc880d 100644 --- a/src/Mod/BIM/Presets/ifc_contexts_IFC4.json +++ b/src/Mod/BIM/Presets/ifc_contexts_IFC4.json @@ -143,4 +143,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/Mod/BIM/Resources/create_qrc.py b/src/Mod/BIM/Resources/create_qrc.py index 3d5dc4d39f..829ab6fb6b 100755 --- a/src/Mod/BIM/Resources/create_qrc.py +++ b/src/Mod/BIM/Resources/create_qrc.py @@ -25,6 +25,7 @@ #!/usr/bin/python import os + txt = "\n \n" cdir = os.path.dirname(__file__) for subdir in ["geometry", "icons", "icons/IFC", "translations", "ui"]: diff --git a/src/Mod/BIM/Resources/templates/webgl_export_template.html b/src/Mod/BIM/Resources/templates/webgl_export_template.html index c3498f9205..1c04b69ce6 100644 --- a/src/Mod/BIM/Resources/templates/webgl_export_template.html +++ b/src/Mod/BIM/Resources/templates/webgl_export_template.html @@ -649,4 +649,4 @@ } - \ No newline at end of file + diff --git a/src/Mod/BIM/arch.dox b/src/Mod/BIM/arch.dox index 9d5a537fe9..8508f695ee 100644 --- a/src/Mod/BIM/arch.dox +++ b/src/Mod/BIM/arch.dox @@ -1,3 +1,2 @@ /** \defgroup ARCH Arch * \ingroup PYTHONWORKBENCHES */ - diff --git a/src/Mod/BIM/bimcommands/BimArchUtils.py b/src/Mod/BIM/bimcommands/BimArchUtils.py index 5bf0e416e6..dd97e2d21e 100644 --- a/src/Mod/BIM/bimcommands/BimArchUtils.py +++ b/src/Mod/BIM/bimcommands/BimArchUtils.py @@ -37,9 +37,13 @@ class Arch_Add: "the Arch Add command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Add', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Add","Add Component"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Add","Adds the selected components to the active object")} + return { + "Pixmap": "Arch_Add", + "MenuText": QT_TRANSLATE_NOOP("Arch_Add", "Add Component"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Add", "Adds the selected components to the active object" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -48,27 +52,40 @@ class Arch_Add: def Activated(self): import Draft import Arch + sel = FreeCADGui.Selection.getSelection() if Draft.getType(sel[-1]) == "Space": - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Add space boundary")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Add space boundary")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.addSpaceBoundaries( FreeCAD.ActiveDocument."+sel[-1].Name+", FreeCADGui.Selection.getSelectionEx() )") + FreeCADGui.doCommand( + "Arch.addSpaceBoundaries( FreeCAD.ActiveDocument." + + sel[-1].Name + + ", FreeCADGui.Selection.getSelectionEx() )" + ) elif Draft.getType(sel[-1]).startswith("Ifc"): FreeCADGui.addModule("nativeifc.ifc_tools") for s in sel[:-1]: - FreeCADGui.doCommand("nativeifc.ifc_tools.aggregate(FreeCAD.ActiveDocument."+s.Name+",FreeCAD.ActiveDocument."+sel[-1].Name+")") + FreeCADGui.doCommand( + "nativeifc.ifc_tools.aggregate(FreeCAD.ActiveDocument." + + s.Name + + ",FreeCAD.ActiveDocument." + + sel[-1].Name + + ")" + ) else: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Grouping")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Grouping")) if not Arch.mergeCells(sel): host = sel.pop() ss = "[" for o in sel: if len(ss) > 1: ss += "," - ss += "FreeCAD.ActiveDocument."+o.Name + ss += "FreeCAD.ActiveDocument." + o.Name ss += "]" FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.addComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") + FreeCADGui.doCommand( + "Arch.addComponents(" + ss + ",FreeCAD.ActiveDocument." + host.Name + ")" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -77,9 +94,14 @@ class Arch_Remove: "the Arch Add command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Remove', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Remove","Remove Component"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Remove","Removes the selected components from their parents, or creates a hole in a component")} + return { + "Pixmap": "Arch_Remove", + "MenuText": QT_TRANSLATE_NOOP("Arch_Remove", "Remove Component"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Remove", + "Removes the selected components from their parents, or creates a hole in a component", + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -87,30 +109,45 @@ class Arch_Remove: def Activated(self): import Draft + sel = FreeCADGui.Selection.getSelection() if Draft.getType(sel[-1]) == "Space": - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Remove space boundary")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Remove space boundary")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.removeSpaceBoundaries( FreeCAD.ActiveDocument."+sel[-1].Name+", FreeCADGui.Selection.getSelection() )") + FreeCADGui.doCommand( + "Arch.removeSpaceBoundaries( FreeCAD.ActiveDocument." + + sel[-1].Name + + ", FreeCADGui.Selection.getSelection() )" + ) elif Draft.getType(sel[-1]).startswith("Ifc"): FreeCADGui.addModule("nativeifc.ifc_tools") for s in sel[:-1]: - FreeCADGui.doCommand("nativeifc.ifc_tools.aggregate(FreeCAD.ActiveDocument."+s.Name+",FreeCAD.ActiveDocument."+sel[-1].Name+",mode='opening')") + FreeCADGui.doCommand( + "nativeifc.ifc_tools.aggregate(FreeCAD.ActiveDocument." + + s.Name + + ",FreeCAD.ActiveDocument." + + sel[-1].Name + + ",mode='opening')" + ) else: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Ungrouping")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Ungrouping")) if len(sel) > 1: host = sel.pop() ss = "[" for o in sel: if len(ss) > 1: ss += "," - ss += "FreeCAD.ActiveDocument."+o.Name + ss += "FreeCAD.ActiveDocument." + o.Name ss += "]" FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.removeComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") + FreeCADGui.doCommand( + "Arch.removeComponents(" + ss + ",FreeCAD.ActiveDocument." + host.Name + ")" + ) else: FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.removeComponents(FreeCAD.ActiveDocument."+sel[0].Name+")") + FreeCADGui.doCommand( + "Arch.removeComponents(FreeCAD.ActiveDocument." + sel[0].Name + ")" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -119,9 +156,13 @@ class Arch_SplitMesh: "the Arch SplitMesh command definition" def GetResources(self): - return {'Pixmap' : 'Arch_SplitMesh', - 'MenuText': QT_TRANSLATE_NOOP("Arch_SplitMesh","Split Mesh"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_SplitMesh","Splits selected meshes into independent components")} + return { + "Pixmap": "Arch_SplitMesh", + "MenuText": QT_TRANSLATE_NOOP("Arch_SplitMesh", "Split Mesh"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_SplitMesh", "Splits selected meshes into independent components" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -129,14 +170,15 @@ class Arch_SplitMesh: def Activated(self): import Arch + if FreeCADGui.Selection.getSelection(): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Split Mesh")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Split Mesh")) for obj in sel: n = obj.Name nobjs = Arch.splitMesh(obj) if len(nobjs) > 1: - g = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",n) + g = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", n) for o in nobjs: g.addObject(o) FreeCAD.ActiveDocument.commitTransaction() @@ -147,9 +189,13 @@ class Arch_MeshToShape: "the Arch MeshToShape command definition" def GetResources(self): - return {'Pixmap' : 'Arch_MeshToShape', - 'MenuText': QT_TRANSLATE_NOOP("Arch_MeshToShape","Mesh to Shape"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_MeshToShape","Turns selected meshes into Part shape objects")} + return { + "Pixmap": "Arch_MeshToShape", + "MenuText": QT_TRANSLATE_NOOP("Arch_MeshToShape", "Mesh to Shape"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_MeshToShape", "Turns selected meshes into Part shape objects" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -158,6 +204,7 @@ class Arch_MeshToShape: def Activated(self): import Arch from draftutils import params + if FreeCADGui.Selection.getSelection(): f = FreeCADGui.Selection.getSelection()[0] g = None @@ -174,20 +221,28 @@ class Arch_MeshToShape: tol = params.get_param_arch("ConversionTolerance") flat = params.get_param_arch("ConversionFlat") cut = params.get_param_arch("ConversionCut") - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Mesh to shape")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Mesh to shape")) for obj in FreeCADGui.Selection.getSelection(): - newobj = Arch.meshToShape(obj,True,fast,tol,flat,cut) + newobj = Arch.meshToShape(obj, True, fast, tol, flat, cut) if g and newobj: g.addObject(newobj) FreeCAD.ActiveDocument.commitTransaction() + class Arch_SelectNonSolidMeshes: "the Arch SelectNonSolidMeshes command definition" def GetResources(self): - return {'Pixmap': 'Arch_SelectNonManifold.svg', - 'MenuText': QT_TRANSLATE_NOOP("Arch_SelectNonSolidMeshes","Select Non-Manifold Meshes"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_SelectNonSolidMeshes","Selects all non-manifold meshes from the document or from the selected groups")} + return { + "Pixmap": "Arch_SelectNonManifold.svg", + "MenuText": QT_TRANSLATE_NOOP( + "Arch_SelectNonSolidMeshes", "Select Non-Manifold Meshes" + ), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_SelectNonSolidMeshes", + "Selects all non-manifold meshes from the document or from the selected groups", + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -214,10 +269,15 @@ class Arch_SelectNonSolidMeshes: class Arch_RemoveShape: "the Arch RemoveShape command definition" + def GetResources(self): - return {'Pixmap' : 'Arch_RemoveShape', - 'MenuText': QT_TRANSLATE_NOOP("Arch_RemoveShape","Remove Shape From BIM"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_RemoveShape","Removes cubic shapes from BIM components")} + return { + "Pixmap": "Arch_RemoveShape", + "MenuText": QT_TRANSLATE_NOOP("Arch_RemoveShape", "Remove Shape From BIM"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_RemoveShape", "Removes cubic shapes from BIM components" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -225,6 +285,7 @@ class Arch_RemoveShape: def Activated(self): import Arch + sel = FreeCADGui.Selection.getSelection() Arch.removeShape(sel) @@ -233,9 +294,13 @@ class Arch_CloseHoles: "the Arch CloseHoles command definition" def GetResources(self): - return {'Pixmap' : 'Arch_CloseHoles', - 'MenuText': QT_TRANSLATE_NOOP("Arch_CloseHoles","Close Holes"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_CloseHoles","Closes holes in open shapes, turning them into solids")} + return { + "Pixmap": "Arch_CloseHoles", + "MenuText": QT_TRANSLATE_NOOP("Arch_CloseHoles", "Close Holes"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_CloseHoles", "Closes holes in open shapes, turning them into solids" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -243,6 +308,7 @@ class Arch_CloseHoles: def Activated(self): import Arch + for o in FreeCADGui.Selection.getSelection(): s = Arch.closeHole(o.Shape) if s: @@ -251,10 +317,13 @@ class Arch_CloseHoles: class Arch_Check: "the Arch Check command definition" + def GetResources(self): - return {'Pixmap' : 'Arch_Check', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Check","Check"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Check","Checks the selected objects for problems")} + return { + "Pixmap": "Arch_Check", + "MenuText": QT_TRANSLATE_NOOP("Arch_Check", "Check"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_Check", "Checks the selected objects for problems"), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -262,13 +331,16 @@ class Arch_Check: def Activated(self): import Arch + result = Arch.check(FreeCADGui.Selection.getSelection()) if not result: - FreeCAD.Console.PrintMessage(str(translate("Arch","No problems found!"))) + FreeCAD.Console.PrintMessage(str(translate("Arch", "No problems found!"))) else: FreeCADGui.Selection.clearSelection() for i in result: - FreeCAD.Console.PrintWarning("Object "+i[0].Name+" ("+i[0].Label+") "+i[1]) + FreeCAD.Console.PrintWarning( + "Object " + i[0].Name + " (" + i[0].Label + ") " + i[1] + ) FreeCADGui.Selection.addSelection(i[0]) @@ -276,9 +348,11 @@ class Arch_Survey: "the Arch Survey command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Survey', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Survey","Survey"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Survey","Starts survey")} + return { + "Pixmap": "Arch_Survey", + "MenuText": QT_TRANSLATE_NOOP("Arch_Survey", "Survey"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_Survey", "Starts survey"), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -293,9 +367,13 @@ class Arch_ToggleIfcBrepFlag: "the Toggle IFC B-rep flag command definition" def GetResources(self): - return {'Pixmap' : 'Arch_ToggleIfcBrepFlag', - 'MenuText': QT_TRANSLATE_NOOP("Arch_ToggleIfcBrepFlag","Toggle IFC B-Rep Flag"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_ToggleIfcBrepFlag","Forces an object to be exported as B-rep or not")} + return { + "Pixmap": "Arch_ToggleIfcBrepFlag", + "MenuText": QT_TRANSLATE_NOOP("Arch_ToggleIfcBrepFlag", "Toggle IFC B-Rep Flag"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_ToggleIfcBrepFlag", "Forces an object to be exported as B-rep or not" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -303,6 +381,7 @@ class Arch_ToggleIfcBrepFlag: def Activated(self): import Arch + for o in FreeCADGui.Selection.getSelection(): Arch.toggleIfcBrepFlag(o) @@ -311,10 +390,14 @@ class Arch_Component: "the Arch Component command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Component', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Component","Component"), - 'Accel': "C, M", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Component","Creates an undefined architectural component")} + return { + "Pixmap": "Arch_Component", + "MenuText": QT_TRANSLATE_NOOP("Arch_Component", "Component"), + "Accel": "C, M", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Component", "Creates an undefined architectural component" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -323,12 +406,14 @@ class Arch_Component: def Activated(self): sel = FreeCADGui.Selection.getSelection() if sel: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Component")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Component")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.Control.closeDialog() for o in sel: - FreeCADGui.doCommand("obj = Arch.makeComponent(FreeCAD.ActiveDocument."+o.Name+")") + FreeCADGui.doCommand( + "obj = Arch.makeComponent(FreeCAD.ActiveDocument." + o.Name + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -338,10 +423,14 @@ class Arch_CloneComponent: "the Arch Clone Component command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Component_Clone', - 'MenuText': QT_TRANSLATE_NOOP("Arch_CloneComponent","Clone Component"), - 'Accel': "C, C", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_CloneComponent","Clones an object as an undefined architectural component")} + return { + "Pixmap": "Arch_Component_Clone", + "MenuText": QT_TRANSLATE_NOOP("Arch_CloneComponent", "Clone Component"), + "Accel": "C, C", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_CloneComponent", "Clones an object as an undefined architectural component" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -350,12 +439,14 @@ class Arch_CloneComponent: def Activated(self): sel = FreeCADGui.Selection.getSelection() if sel: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Component")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Component")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.Control.closeDialog() for o in sel: - FreeCADGui.doCommand("obj = Arch.cloneComponent(FreeCAD.ActiveDocument."+o.Name+")") + FreeCADGui.doCommand( + "obj = Arch.cloneComponent(FreeCAD.ActiveDocument." + o.Name + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -365,10 +456,14 @@ class Arch_IfcSpreadsheet: "the Arch Schedule command definition" def GetResources(self): - return {'Pixmap': 'Arch_Schedule', - 'MenuText': QT_TRANSLATE_NOOP("Arch_IfcSpreadsheet","New IFC Spreadsheet"), - 'Accel': "I, P", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_IfcSpreadsheet","Creates a spreadsheet to store IFC properties of an object")} + return { + "Pixmap": "Arch_Schedule", + "MenuText": QT_TRANSLATE_NOOP("Arch_IfcSpreadsheet", "New IFC Spreadsheet"), + "Accel": "I, P", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_IfcSpreadsheet", "Creates a spreadsheet to store IFC properties of an object" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -376,13 +471,17 @@ class Arch_IfcSpreadsheet: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create IFC properties spreadsheet")) + FreeCAD.ActiveDocument.openTransaction( + translate("Arch", "Create IFC properties spreadsheet") + ) FreeCADGui.addModule("Arch") FreeCADGui.Control.closeDialog() if sel: for o in sel: - FreeCADGui.doCommand("Arch.makeIfcSpreadsheet(FreeCAD.ActiveDocument."+o.Name+")") - else : + FreeCADGui.doCommand( + "Arch.makeIfcSpreadsheet(FreeCAD.ActiveDocument." + o.Name + ")" + ) + else: FreeCADGui.doCommand("Arch.makeIfcSpreadsheet()") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -392,10 +491,14 @@ class Arch_ToggleSubs: "the ToggleSubs command definition" def GetResources(self): - return {'Pixmap' : 'Arch_ToggleSubs', - 'Accel' : 'Ctrl+Space', - 'MenuText': QT_TRANSLATE_NOOP("Arch_ToggleSubs","Toggle Subcomponents"), - 'ToolTip' : QT_TRANSLATE_NOOP("Arch_ToggleSubs","Shows or hides the subcomponents of this object")} + return { + "Pixmap": "Arch_ToggleSubs", + "Accel": "Ctrl+Space", + "MenuText": QT_TRANSLATE_NOOP("Arch_ToggleSubs", "Toggle Subcomponents"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_ToggleSubs", "Shows or hides the subcomponents of this object" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -403,11 +506,12 @@ class Arch_ToggleSubs: def Activated(self): import Draft + mode = None for obj in FreeCADGui.Selection.getSelection(): if hasattr(obj, "Subtractions"): for sub in obj.Subtractions: - if not (Draft.getType(sub) in ["Window","Roof"]): + if not (Draft.getType(sub) in ["Window", "Roof"]): if mode is None: # take the first sub as base mode = sub.ViewObject.isVisible() @@ -431,9 +535,13 @@ class Arch_MergeWalls: def GetResources(self): """Returns a dictionary with the visual aspects of the Arch MergeWalls tool.""" - return {'Pixmap' : 'Arch_MergeWalls', - 'MenuText': QT_TRANSLATE_NOOP("Arch_MergeWalls","Merge Walls"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_MergeWalls","Merges the selected walls, if possible")} + return { + "Pixmap": "Arch_MergeWalls", + "MenuText": QT_TRANSLATE_NOOP("Arch_MergeWalls", "Merge Walls"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_MergeWalls", "Merges the selected walls, if possible" + ), + } def IsActive(self): """Determines whether or not the Arch MergeWalls tool is active. @@ -456,54 +564,60 @@ class Arch_MergeWalls: import Draft import ArchWall + walls = FreeCADGui.Selection.getSelection() if len(walls) == 1: if Draft.getType(walls[0]) == "Wall": - ostr = "FreeCAD.ActiveDocument."+ walls[0].Name + ostr = "FreeCAD.ActiveDocument." + walls[0].Name ok = False for o in walls[0].Additions: if Draft.getType(o) == "Wall": ostr += ",FreeCAD.ActiveDocument." + o.Name ok = True if ok: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Merge Walls")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Merge Walls")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.joinWalls(["+ostr+"],delete=True)") + FreeCADGui.doCommand("Arch.joinWalls([" + ostr + "],delete=True)") FreeCAD.ActiveDocument.commitTransaction() return else: - FreeCAD.Console.PrintWarning(translate("Arch","The selected wall contains no subwalls to merge")) + FreeCAD.Console.PrintWarning( + translate("Arch", "The selected wall contains no subwalls to merge") + ) return else: - FreeCAD.Console.PrintWarning(translate("Arch","Select only wall objects")) + FreeCAD.Console.PrintWarning(translate("Arch", "Select only wall objects")) return for w in walls: if Draft.getType(w) != "Wall": - FreeCAD.Console.PrintMessage(translate("Arch","Select only wall objects")) + FreeCAD.Console.PrintMessage(translate("Arch", "Select only wall objects")) return if not ArchWall.areSameWallTypes(walls): FreeCAD.Console.PrintMessage( - translate("Arch","Walls with different 'Width', 'Height' and 'Align' properties cannot be merged") + translate( + "Arch", + "Walls with different 'Width', 'Height' and 'Align' properties cannot be merged", + ) ) return - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Merge Walls")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Merge Walls")) FreeCADGui.addModule("Arch") FreeCADGui.doCommand("Arch.joinWalls(FreeCADGui.Selection.getSelection(),delete=True)") FreeCAD.ActiveDocument.commitTransaction() -FreeCADGui.addCommand('Arch_Add',Arch_Add()) -FreeCADGui.addCommand('Arch_Remove',Arch_Remove()) -FreeCADGui.addCommand('Arch_SplitMesh',Arch_SplitMesh()) -FreeCADGui.addCommand('Arch_MeshToShape',Arch_MeshToShape()) -FreeCADGui.addCommand('Arch_SelectNonSolidMeshes',Arch_SelectNonSolidMeshes()) -FreeCADGui.addCommand('Arch_RemoveShape',Arch_RemoveShape()) -FreeCADGui.addCommand('Arch_CloseHoles',Arch_CloseHoles()) -FreeCADGui.addCommand('Arch_Check',Arch_Check()) -FreeCADGui.addCommand('Arch_Survey',Arch_Survey()) -FreeCADGui.addCommand('Arch_ToggleIfcBrepFlag',Arch_ToggleIfcBrepFlag()) -FreeCADGui.addCommand('Arch_Component',Arch_Component()) -FreeCADGui.addCommand('Arch_CloneComponent',Arch_CloneComponent()) -FreeCADGui.addCommand('Arch_IfcSpreadsheet',Arch_IfcSpreadsheet()) -FreeCADGui.addCommand('Arch_ToggleSubs',Arch_ToggleSubs()) -FreeCADGui.addCommand('Arch_MergeWalls',Arch_MergeWalls()) +FreeCADGui.addCommand("Arch_Add", Arch_Add()) +FreeCADGui.addCommand("Arch_Remove", Arch_Remove()) +FreeCADGui.addCommand("Arch_SplitMesh", Arch_SplitMesh()) +FreeCADGui.addCommand("Arch_MeshToShape", Arch_MeshToShape()) +FreeCADGui.addCommand("Arch_SelectNonSolidMeshes", Arch_SelectNonSolidMeshes()) +FreeCADGui.addCommand("Arch_RemoveShape", Arch_RemoveShape()) +FreeCADGui.addCommand("Arch_CloseHoles", Arch_CloseHoles()) +FreeCADGui.addCommand("Arch_Check", Arch_Check()) +FreeCADGui.addCommand("Arch_Survey", Arch_Survey()) +FreeCADGui.addCommand("Arch_ToggleIfcBrepFlag", Arch_ToggleIfcBrepFlag()) +FreeCADGui.addCommand("Arch_Component", Arch_Component()) +FreeCADGui.addCommand("Arch_CloneComponent", Arch_CloneComponent()) +FreeCADGui.addCommand("Arch_IfcSpreadsheet", Arch_IfcSpreadsheet()) +FreeCADGui.addCommand("Arch_ToggleSubs", Arch_ToggleSubs()) +FreeCADGui.addCommand("Arch_MergeWalls", Arch_MergeWalls()) diff --git a/src/Mod/BIM/bimcommands/BimAxis.py b/src/Mod/BIM/bimcommands/BimAxis.py index 878cd422f3..f02b0c0716 100644 --- a/src/Mod/BIM/bimcommands/BimAxis.py +++ b/src/Mod/BIM/bimcommands/BimAxis.py @@ -33,21 +33,21 @@ translate = FreeCAD.Qt.translate PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") - class Arch_Axis: - "the Arch Axis command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Axis', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Axis","Axis"), - 'Accel': "A, X", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Axis","Creates a set of axes")} + return { + "Pixmap": "Arch_Axis", + "MenuText": QT_TRANSLATE_NOOP("Arch_Axis", "Axis"), + "Accel": "A, X", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Axis", "Creates a set of axes"), + } def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Axis")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Axis")) FreeCADGui.addModule("Arch") FreeCADGui.doCommand("Arch.makeAxis()") @@ -59,35 +59,40 @@ class Arch_Axis: return v - class Arch_AxisSystem: - "the Arch Axis System command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Axis_System', - 'MenuText': QT_TRANSLATE_NOOP("Arch_AxisSystem","Axis System"), - 'Accel': "X, S", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_AxisSystem","Creates an axis system from a set of axes")} + return { + "Pixmap": "Arch_Axis_System", + "MenuText": QT_TRANSLATE_NOOP("Arch_AxisSystem", "Axis System"), + "Accel": "X, S", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_AxisSystem", "Creates an axis system from a set of axes" + ), + } def Activated(self): import Draft + if FreeCADGui.Selection.getSelection(): s = "[" for o in FreeCADGui.Selection.getSelection(): if Draft.getType(o) != "Axis": - FreeCAD.Console.PrintError(translate("Arch","Only axes must be selected")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Only axes must be selected") + "\n" + ) return - s += "FreeCAD.ActiveDocument."+o.Name+"," + s += "FreeCAD.ActiveDocument." + o.Name + "," s += "]" - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Axis System")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Axis System")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.makeAxisSystem("+s+")") + FreeCADGui.doCommand("Arch.makeAxisSystem(" + s + ")") FreeCAD.ActiveDocument.commitTransaction() else: - FreeCAD.Console.PrintError(translate("Arch","Select at least one axis")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Select at least one axis") + "\n") def IsActive(self): @@ -96,19 +101,20 @@ class Arch_AxisSystem: class Arch_Grid: - "the Arch Grid command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Grid', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Grid","Grid"), - 'Accel': "A, X", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Grid","Creates a customizable grid object")} + return { + "Pixmap": "Arch_Grid", + "MenuText": QT_TRANSLATE_NOOP("Arch_Grid", "Grid"), + "Accel": "A, X", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Grid", "Creates a customizable grid object"), + } def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Grid")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Grid")) FreeCADGui.addModule("Arch") FreeCADGui.doCommand("Arch.makeGrid()") @@ -121,24 +127,23 @@ class Arch_Grid: class Arch_AxisTools: - """The Axis tools group command""" def GetCommands(self): - return tuple(['Arch_Axis','Arch_AxisSystem','Arch_Grid']) + return tuple(["Arch_Axis", "Arch_AxisSystem", "Arch_Grid"]) def GetResources(self): - return { 'MenuText': QT_TRANSLATE_NOOP("Arch_AxisTools",'Axis Tools'), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_AxisTools",'Axis tools') - } + return { + "MenuText": QT_TRANSLATE_NOOP("Arch_AxisTools", "Axis Tools"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_AxisTools", "Axis tools"), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v - -FreeCADGui.addCommand('Arch_Axis', Arch_Axis()) -FreeCADGui.addCommand('Arch_AxisSystem', Arch_AxisSystem()) -FreeCADGui.addCommand('Arch_Grid', Arch_Grid()) -FreeCADGui.addCommand('Arch_AxisTools', Arch_AxisTools()) +FreeCADGui.addCommand("Arch_Axis", Arch_Axis()) +FreeCADGui.addCommand("Arch_AxisSystem", Arch_AxisSystem()) +FreeCADGui.addCommand("Arch_Grid", Arch_Grid()) +FreeCADGui.addCommand("Arch_AxisTools", Arch_AxisTools()) diff --git a/src/Mod/BIM/bimcommands/BimBeam.py b/src/Mod/BIM/bimcommands/BimBeam.py index 59a0ad4c05..964d3be3aa 100644 --- a/src/Mod/BIM/bimcommands/BimBeam.py +++ b/src/Mod/BIM/bimcommands/BimBeam.py @@ -46,9 +46,7 @@ class BIM_Beam(ArchStructure._CommandStructure): return { "Pixmap": "BIM_Beam", "MenuText": QT_TRANSLATE_NOOP("BIM_Beam", "Beam"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Beam", "Creates a beam between two points" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Beam", "Creates a beam between two points"), "Accel": "B,M", } diff --git a/src/Mod/BIM/bimcommands/BimBox.py b/src/Mod/BIM/bimcommands/BimBox.py index 6c8ea7d928..cf1d168641 100644 --- a/src/Mod/BIM/bimcommands/BimBox.py +++ b/src/Mod/BIM/bimcommands/BimBox.py @@ -79,11 +79,11 @@ class BIM_Box: # we have the base point already self.Length.setText( FreeCAD.Units.Quantity( - self.points[-1].sub(point).Length, FreeCAD.Units.Length + self.points[-1].sub(point).Length, FreeCAD.Units.Length ).UserString ) self.Length.setFocus() - self.Length.setSelection(0,FreeCADGui.draftToolBar.number_length(self.Length.text())) + self.Length.setSelection(0, FreeCADGui.draftToolBar.number_length(self.Length.text())) elif len(self.points) == 2: # now we already have our base line, we update the 1st rectangle p = point @@ -101,7 +101,7 @@ class BIM_Box: ).UserString ) self.Width.setFocus() - self.Width.setSelection(0,FreeCADGui.draftToolBar.number_length(self.Width.text())) + self.Width.setSelection(0, FreeCADGui.draftToolBar.number_length(self.Width.text())) elif len(self.points) == 3: h = DraftGeomUtils.distance_to_plane(point, self.cubetracker[0].p3(), self.normal) w = self.normal * h @@ -112,7 +112,7 @@ class BIM_Box: self.cubetracker[3].p3((self.cubetracker[0].p3()).add(w)) self.Height.setText(FreeCAD.Units.Quantity(h, FreeCAD.Units.Length).UserString) self.Height.setFocus() - self.Height.setSelection(0,FreeCADGui.draftToolBar.number_length(self.Height.text())) + self.Height.setSelection(0, FreeCADGui.draftToolBar.number_length(self.Height.text())) def PointCallback(self, point, snapinfo): if not point: @@ -281,7 +281,7 @@ class BIM_Box: if self.HeightValue > 0.0: pla = DraftGeomUtils.placement_from_points(p1, p3, p2) self.LengthValue, self.WidthValue = self.WidthValue, self.LengthValue - self.doc.openTransaction(translate("Arch","Create Box")) + self.doc.openTransaction(translate("Arch", "Create Box")) cube = self.doc.addObject("Part::Box", "Box") cube.Placement = pla cube.Length = self.LengthValue diff --git a/src/Mod/BIM/bimcommands/BimBuilder.py b/src/Mod/BIM/bimcommands/BimBuilder.py index 6f9b2b7d25..86cfc3fb0d 100644 --- a/src/Mod/BIM/bimcommands/BimBuilder.py +++ b/src/Mod/BIM/bimcommands/BimBuilder.py @@ -36,9 +36,7 @@ class BIM_Builder: return { "Pixmap": "Part_Shapebuilder", "MenuText": QT_TRANSLATE_NOOP("Part_Builder", "Shape Builder"), - "ToolTip": QT_TRANSLATE_NOOP( - "Part_Builder", "Advanced utility to create shapes" - ), + "ToolTip": QT_TRANSLATE_NOOP("Part_Builder", "Advanced utility to create shapes"), } def Activated(self): diff --git a/src/Mod/BIM/bimcommands/BimBuildingPart.py b/src/Mod/BIM/bimcommands/BimBuildingPart.py index a096179232..5aa123a9e5 100644 --- a/src/Mod/BIM/bimcommands/BimBuildingPart.py +++ b/src/Mod/BIM/bimcommands/BimBuildingPart.py @@ -37,15 +37,18 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Level: - """The command definition for the Arch workbench's gui tool, Arch Floor""" def GetResources(self): - return {'Pixmap' : 'Arch_Floor', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Level","Level"), - 'Accel': "L, V", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Level","Creates a building part object that represents a level")} + return { + "Pixmap": "Arch_Floor", + "MenuText": QT_TRANSLATE_NOOP("Arch_Level", "Level"), + "Accel": "L, V", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Level", "Creates a building part object that represents a level" + ), + } def IsActive(self): @@ -54,7 +57,7 @@ class Arch_Level: def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Level")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Level")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.addModule("WorkingPlane") @@ -66,15 +69,16 @@ class Arch_Level: class Arch_Building: - "the Arch Building command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Building', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Building","Building"), - 'Accel': "B, U", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Building","Creates a building object")} + return { + "Pixmap": "Arch_Building", + "MenuText": QT_TRANSLATE_NOOP("Arch_Building", "Building"), + "Accel": "B, U", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Building", "Creates a building object"), + } def IsActive(self): @@ -83,7 +87,7 @@ class Arch_Building: def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Building")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Building")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("obj = Arch.makeBuilding()") @@ -92,5 +96,5 @@ class Arch_Building: FreeCAD.ActiveDocument.recompute() -FreeCADGui.addCommand('Arch_Building', Arch_Building()) -FreeCADGui.addCommand('Arch_Level', Arch_Level()) +FreeCADGui.addCommand("Arch_Building", Arch_Building()) +FreeCADGui.addCommand("Arch_Level", Arch_Level()) diff --git a/src/Mod/BIM/bimcommands/BimClassification.py b/src/Mod/BIM/bimcommands/BimClassification.py index b614850300..04be60432d 100644 --- a/src/Mod/BIM/bimcommands/BimClassification.py +++ b/src/Mod/BIM/bimcommands/BimClassification.py @@ -40,9 +40,7 @@ class BIM_Classification: def GetResources(self): return { "Pixmap": "BIM_Classification", - "MenuText": QT_TRANSLATE_NOOP( - "BIM_Classification", "Manage Classification" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_Classification", "Manage Classification"), "ToolTip": QT_TRANSLATE_NOOP( "BIM_Classification", "Manages classification systems and apply classification to objects", @@ -73,10 +71,12 @@ class BIM_Classification: # load the form and set the tree model up self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogClassification.ui") self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_Classification.svg")) - self.form.groupMode.setItemIcon(0, QtGui.QIcon(":/icons/Arch_SectionPlane_Tree.svg")) # Alphabetical - self.form.groupMode.setItemIcon(1, QtGui.QIcon(":/icons/IFC.svg")) # Type - self.form.groupMode.setItemIcon(2, QtGui.QIcon(":/icons/Arch_Material.svg")) # Material - self.form.groupMode.setItemIcon(3, QtGui.QIcon(":/icons/Document.svg")) # Model structure + self.form.groupMode.setItemIcon( + 0, QtGui.QIcon(":/icons/Arch_SectionPlane_Tree.svg") + ) # Alphabetical + self.form.groupMode.setItemIcon(1, QtGui.QIcon(":/icons/IFC.svg")) # Type + self.form.groupMode.setItemIcon(2, QtGui.QIcon(":/icons/Arch_Material.svg")) # Material + self.form.groupMode.setItemIcon(3, QtGui.QIcon(":/icons/Document.svg")) # Model structure # restore saved values self.form.onlyVisible.setChecked(PARAMS.GetInt("BimClassificationVisibleState", 0)) @@ -107,12 +107,11 @@ class BIM_Classification: self.form.groupMaterials.hide() self.form.buttonApply.hide() self.form.buttonRename.hide() - self.form.setWindowTitle( - translate("BIM", "Editing") + " " + self.isEditing.Label - ) + self.form.setWindowTitle(translate("BIM", "Editing") + " " + self.isEditing.Label) if "IfcClass" in pl: # load existing class if needed from nativeifc import ifc_classification + ifc_classification.show_classification(self.isEditing) if "StandardCode" in pl: current = self.isEditing.StandardCode @@ -152,9 +151,7 @@ class BIM_Classification: presets.append(n) self.form.comboSystem.addItem(n) if n == p: - self.form.comboSystem.setCurrentIndex( - self.form.comboSystem.count() - 1 - ) + self.form.comboSystem.setCurrentIndex(self.form.comboSystem.count() - 1) # connect signals self.form.comboSystem.currentIndexChanged.connect(self.updateClasses) @@ -167,18 +164,16 @@ class BIM_Classification: self.form.treeClass.itemDoubleClicked.connect(self.apply) self.form.search.up.connect(self.onUpArrow) self.form.search.down.connect(self.onDownArrow) - if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 + if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 self.form.onlyVisible.checkStateChanged.connect(self.onVisible) self.form.checkPrefix.checkStateChanged.connect(self.onPrefix) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 self.form.onlyVisible.stateChanged.connect(self.onVisible) self.form.checkPrefix.stateChanged.connect(self.onPrefix) # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) self.updateClasses() @@ -186,13 +181,11 @@ class BIM_Classification: # select current classification if current: system, classification = current.split(" ", 1) - print("searching for",classification) + print("searching for", classification) if system in self.Classes: self.form.comboSystem.setCurrentText(system) res = self.form.treeClass.findItems( - classification, - QtCore.Qt.MatchExactly|QtCore.Qt.MatchRecursive, - 0 + classification, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0 ) if res: self.form.treeClass.setCurrentItem(res[0]) @@ -284,11 +277,7 @@ class BIM_Classification: for name in self.matlist.keys(): mat = FreeCAD.ActiveDocument.getObject(name) if mat: - children = [ - par.Name - for par in mat.InList - if par.Name in self.objectslist.keys() - ] + children = [par.Name for par in mat.InList if par.Name in self.objectslist.keys()] groups[name] = children claimed.extend(children) groups["Undefined"] = [o for o in self.objectslist.keys() if not o in claimed] @@ -296,9 +285,7 @@ class BIM_Classification: for group in groups.keys(): matobj = FreeCAD.ActiveDocument.getObject(group) if matobj: - mit = QtGui.QTreeWidgetItem( - [self.labellist[group], self.matlist[group]] - ) + mit = QtGui.QTreeWidgetItem([self.labellist[group], self.matlist[group]]) mit.setIcon(0, self.getIcon(matobj)) mit.setToolTip(0, group) else: @@ -307,12 +294,8 @@ class BIM_Classification: for name in groups[group]: obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): - it = QtGui.QTreeWidgetItem( - [self.labellist[name], self.objectslist[name]] - ) + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): + it = QtGui.QTreeWidgetItem([self.labellist[name], self.objectslist[name]]) it.setIcon(0, self.getIcon(obj)) it.setToolTip(0, name) mit.addChild(it) @@ -473,9 +456,7 @@ class BIM_Classification: def build(self, system): # try to load the IFC first - preset = os.path.join( - FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".ifc" - ) + preset = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".ifc") if os.path.exists(preset): return self.build_ifc(system) else: @@ -485,9 +466,7 @@ class BIM_Classification: if os.path.exists(preset): return self.build_xml(system) else: - FreeCAD.Console.PrintError( - "Unable to find classification file:" + system + "\n" - ) + FreeCAD.Console.PrintError("Unable to find classification file:" + system + "\n") return [] def build_ifc(self, system): @@ -500,9 +479,7 @@ class BIM_Classification: self.Name = None self.children = [] - preset = os.path.join( - FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".ifc" - ) + preset = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".ifc") if not os.path.exists(preset): return None import ifcopenshell @@ -536,9 +513,7 @@ class BIM_Classification: self.Name = None self.children = [] - preset = os.path.join( - FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".xml" - ) + preset = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Classification", system + ".xml") if not os.path.exists(preset): return None import codecs @@ -562,19 +537,14 @@ class BIM_Classification: and re.findall(r"(.*?)", l) and not currentItem.Name ): - currentItem.Name = re.findall( - "(.*?)", l - )[0] + currentItem.Name = re.findall("(.*?)", l)[0] return [self.listize(c) for c in d.children] def listize(self, item): return [item.ID, item.Name, [self.listize(it) for it in item.children]] def apply(self, item=None, col=None): - if ( - self.form.treeObjects.selectedItems() - and len(self.form.treeClass.selectedItems()) == 1 - ): + if self.form.treeObjects.selectedItems() and len(self.form.treeClass.selectedItems()) == 1: c = self.form.treeClass.selectedItems()[0].text(0) if self.form.checkPrefix.isChecked(): c = self.form.comboSystem.currentText() + " " + c @@ -583,10 +553,7 @@ class BIM_Classification: m.setText(1, c) def rename(self): - if ( - self.form.treeObjects.selectedItems() - and len(self.form.treeClass.selectedItems()) == 1 - ): + if self.form.treeObjects.selectedItems() and len(self.form.treeClass.selectedItems()) == 1: c = self.form.treeClass.selectedItems()[0].toolTip(0) for m in self.form.treeObjects.selectedItems(): if m.toolTip(0): @@ -598,9 +565,7 @@ class BIM_Classification: for row in range(self.form.treeObjects.topLevelItemCount()): child = self.form.treeObjects.topLevelItem(row) items = [child] - items.extend( - [child.child(childrow) for childrow in range(child.childCount())] - ) + items.extend([child.child(childrow) for childrow in range(child.childCount())]) for item in items: code = item.text(1) label = item.text(0) @@ -617,7 +582,9 @@ class BIM_Classification: obj.StandardCode = code elif hasattr(obj, "IfcClass"): if not "Classification" in obj.PropertiesList: - obj.addProperty("App::PropertyString", "Classification", "IFC", locked=True) + obj.addProperty( + "App::PropertyString", "Classification", "IFC", locked=True + ) if code != obj.Classification: if not changed: FreeCAD.ActiveDocument.openTransaction( @@ -627,9 +594,7 @@ class BIM_Classification: obj.Classification = code if label != obj.Label: if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change standard codes" - ) + FreeCAD.ActiveDocument.openTransaction("Change standard codes") changed = True obj.Label = label if changed: @@ -652,7 +617,9 @@ class BIM_Classification: self.isEditing.StandardCode = code else: if not "Classification" in self.isEditing.PropertiesList: - self.isEditing.addProperty("App::PropertyString", "Classification", "IFC", locked=True) + self.isEditing.addProperty( + "App::PropertyString", "Classification", "IFC", locked=True + ) self.isEditing.Classification = code if hasattr(self.isEditing.ViewObject, "Proxy") and hasattr( self.isEditing.ViewObject.Proxy, "setTaskValue" @@ -690,7 +657,7 @@ class BIM_Classification: def onPrefix(self, index): PARAMS.SetInt("BimClassificationSystemNamePrefix", getattr(index, "value", index)) - def getIcon(self,obj): + def getIcon(self, obj): """returns a QIcon for an object""" from PySide import QtGui diff --git a/src/Mod/BIM/bimcommands/BimColumn.py b/src/Mod/BIM/bimcommands/BimColumn.py index 6174f1988a..54837aa179 100644 --- a/src/Mod/BIM/bimcommands/BimColumn.py +++ b/src/Mod/BIM/bimcommands/BimColumn.py @@ -46,9 +46,7 @@ class BIM_Column(ArchStructure._CommandStructure): return { "Pixmap": "BIM_Column", "MenuText": QT_TRANSLATE_NOOP("BIM_Column", "Column"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Column", "Creates a column at a specified location" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Column", "Creates a column at a specified location"), "Accel": "C,O", } diff --git a/src/Mod/BIM/bimcommands/BimCommon.py b/src/Mod/BIM/bimcommands/BimCommon.py index f5d69e2043..47655e9a58 100644 --- a/src/Mod/BIM/bimcommands/BimCommon.py +++ b/src/Mod/BIM/bimcommands/BimCommon.py @@ -36,9 +36,7 @@ class BIM_Common: return { "Pixmap": "Part_Common", "MenuText": QT_TRANSLATE_NOOP("Part_Common", "Intersection"), - "ToolTip": QT_TRANSLATE_NOOP( - "Part_Common", "Creates an intersection of two shapes" - ), + "ToolTip": QT_TRANSLATE_NOOP("Part_Common", "Creates an intersection of two shapes"), } def IsActive(self): diff --git a/src/Mod/BIM/bimcommands/BimCompound.py b/src/Mod/BIM/bimcommands/BimCompound.py index 33d9e605b8..eb08f527d8 100644 --- a/src/Mod/BIM/bimcommands/BimCompound.py +++ b/src/Mod/BIM/bimcommands/BimCompound.py @@ -36,9 +36,7 @@ class BIM_Compound: return { "Pixmap": "Part_Compound", "MenuText": QT_TRANSLATE_NOOP("BIM_Compound", "Create Compound"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Compound", "Create a compound of several shapes" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Compound", "Create a compound of several shapes"), } def IsActive(self): diff --git a/src/Mod/BIM/bimcommands/BimConvert.py b/src/Mod/BIM/bimcommands/BimConvert.py index 354733c77a..b2e82fcadb 100644 --- a/src/Mod/BIM/bimcommands/BimConvert.py +++ b/src/Mod/BIM/bimcommands/BimConvert.py @@ -36,9 +36,7 @@ class BIM_Convert: return { "Pixmap": "Arch_Component", "MenuText": QT_TRANSLATE_NOOP("BIM_Convert", "Convert to BIM"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Convert", "Converts any object to a BIM component" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Convert", "Converts any object to a BIM component"), } def IsActive(self): @@ -55,6 +53,7 @@ class BIM_Convert_TaskPanel: def __init__(self, objs): from PySide import QtGui + self.types = [ "Wall", "Structure", diff --git a/src/Mod/BIM/bimcommands/BimCopy.py b/src/Mod/BIM/bimcommands/BimCopy.py index 237fa4e901..d3165519f8 100644 --- a/src/Mod/BIM/bimcommands/BimCopy.py +++ b/src/Mod/BIM/bimcommands/BimCopy.py @@ -41,20 +41,21 @@ class BIM_Copy(DraftTools.Move): return { "Pixmap": "BIM_Copy", "MenuText": QT_TRANSLATE_NOOP("BIM_Copy", "Copy"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Copy", "Copies selected objects to another location" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Copy", "Copies selected objects to another location"), "Accel": "C,P", } def Activated(self): from draftutils import params + self.cmode = params.get_param("CopyMode") DraftTools.Move.Activated(self) def finish(self, cont=False): from draftutils import params + DraftTools.Move.finish(self, cont) params.set_param("CopyMode", self.cmode) + FreeCADGui.addCommand("BIM_Copy", BIM_Copy()) diff --git a/src/Mod/BIM/bimcommands/BimCurtainwall.py b/src/Mod/BIM/bimcommands/BimCurtainwall.py index 6cfd5a2f6a..3e0944afe9 100644 --- a/src/Mod/BIM/bimcommands/BimCurtainwall.py +++ b/src/Mod/BIM/bimcommands/BimCurtainwall.py @@ -33,17 +33,20 @@ translate = FreeCAD.Qt.translate PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") - class Arch_CurtainWall: - "the Arch Curtain Wall command definition" def GetResources(self): - return {'Pixmap' : 'Arch_CurtainWall', - 'MenuText': QT_TRANSLATE_NOOP("Arch_CurtainWall","Curtain Wall"), - 'Accel': "C, W", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_CurtainWall","Creates a curtain wall object from selected line or from scratch")} + return { + "Pixmap": "Arch_CurtainWall", + "MenuText": QT_TRANSLATE_NOOP("Arch_CurtainWall", "Curtain Wall"), + "Accel": "C, W", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_CurtainWall", + "Creates a curtain wall object from selected line or from scratch", + ), + } def IsActive(self): @@ -55,14 +58,20 @@ class Arch_CurtainWall: self.doc = FreeCAD.ActiveDocument sel = FreeCADGui.Selection.getSelection() if len(sel) > 1: - FreeCAD.Console.PrintError(translate("Arch","Select only one base object or none")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Select only one base object or none") + "\n" + ) elif len(sel) == 1: # build on selection FreeCADGui.Control.closeDialog() - self.doc.openTransaction(translate("Arch","Create Curtain Wall")) + self.doc.openTransaction(translate("Arch", "Create Curtain Wall")) FreeCADGui.addModule("Draft") FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("obj = Arch.makeCurtainWall(FreeCAD.ActiveDocument."+FreeCADGui.Selection.getSelection()[0].Name+")") + FreeCADGui.doCommand( + "obj = Arch.makeCurtainWall(FreeCAD.ActiveDocument." + + FreeCADGui.Selection.getSelection()[0].Name + + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() self.doc.recompute() @@ -71,12 +80,12 @@ class Arch_CurtainWall: FreeCAD.activeDraftCommand = self # register as a Draft command for auto grid on/off self.points = [] import WorkingPlane + WorkingPlane.get_working_plane() - if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui, "Snapper"): FreeCADGui.Snapper.getPoint(callback=self.getPoint) - def getPoint(self,point=None,obj=None): - + def getPoint(self, point=None, obj=None): """Callback for clicks during interactive mode""" if point is None: @@ -86,20 +95,25 @@ class Arch_CurtainWall: return self.points.append(point) if len(self.points) == 1: - FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint) + FreeCADGui.Snapper.getPoint(last=self.points[0], callback=self.getPoint) elif len(self.points) == 2: FreeCAD.activeDraftCommand = None FreeCADGui.Snapper.off() FreeCADGui.Control.closeDialog() - self.doc.openTransaction(translate("Arch","Create Curtain Wall")) + self.doc.openTransaction(translate("Arch", "Create Curtain Wall")) FreeCADGui.addModule("Draft") FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("base = Draft.makeLine(FreeCAD."+str(self.points[0])+",FreeCAD."+str(self.points[1])+")") + FreeCADGui.doCommand( + "base = Draft.makeLine(FreeCAD." + + str(self.points[0]) + + ",FreeCAD." + + str(self.points[1]) + + ")" + ) FreeCADGui.doCommand("obj = Arch.makeCurtainWall(base)") FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() self.doc.recompute() - -FreeCADGui.addCommand('Arch_CurtainWall', Arch_CurtainWall()) +FreeCADGui.addCommand("Arch_CurtainWall", Arch_CurtainWall()) diff --git a/src/Mod/BIM/bimcommands/BimCut.py b/src/Mod/BIM/bimcommands/BimCut.py index 44d5ed8bf7..8ee67af9ec 100644 --- a/src/Mod/BIM/bimcommands/BimCut.py +++ b/src/Mod/BIM/bimcommands/BimCut.py @@ -35,9 +35,7 @@ class BIM_Cut: return { "Pixmap": "Part_Cut", "MenuText": QT_TRANSLATE_NOOP("BIM_Cut", "Difference"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Cut", "Creates a difference between two shapes" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Cut", "Creates a difference between two shapes"), } def IsActive(self): diff --git a/src/Mod/BIM/bimcommands/BimCutPlane.py b/src/Mod/BIM/bimcommands/BimCutPlane.py index 6b6a76c1ac..37c407bb37 100644 --- a/src/Mod/BIM/bimcommands/BimCutPlane.py +++ b/src/Mod/BIM/bimcommands/BimCutPlane.py @@ -35,40 +35,57 @@ translate = FreeCAD.Qt.translate PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") - class Arch_CutPlane: "the Arch CutPlane command definition" def GetResources(self): - return {"Pixmap": "Arch_CutPlane", - "MenuText": QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut With Plane"), - "ToolTip": QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut an object with a plane")} + return { + "Pixmap": "Arch_CutPlane", + "MenuText": QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut With Plane"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut an object with a plane"), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v and len(FreeCADGui.Selection.getSelection()) > 1 def Activated(self): - import ArchCutPlane - baseObj, baseShp, cutterShp = ArchCutPlane._getShapes(FreeCADGui.Selection.getSelectionEx("", 0)) + import ArchCutPlane + + baseObj, baseShp, cutterShp = ArchCutPlane._getShapes( + FreeCADGui.Selection.getSelectionEx("", 0) + ) if baseObj is None: FreeCAD.Console.PrintError( - translate("Arch", "Select two objects, an object to be cut and an object defining a cutting plane, in that order")+"\n") + translate( + "Arch", + "Select two objects, an object to be cut and an object defining a cutting plane, in that order", + ) + + "\n" + ) return if baseShp is None: - FreeCAD.Console.PrintError(translate("Arch", "The first object does not have a shape")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "The first object does not have a shape") + "\n" + ) return if cutterShp is None: - FreeCAD.Console.PrintError(translate("Arch", "The second object does not define a plane")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "The second object does not define a plane") + "\n" + ) return - panel= CutPlaneTaskPanel() + panel = CutPlaneTaskPanel() FreeCADGui.Control.showDialog(panel) + class CutPlaneTaskPanel: def __init__(self): import ArchCutPlane from PySide import QtCore, QtGui - _, self.base, self.cutter = ArchCutPlane._getShapes(FreeCADGui.Selection.getSelectionEx("", 0)) + + _, self.base, self.cutter = ArchCutPlane._getShapes( + FreeCADGui.Selection.getSelectionEx("", 0) + ) self.previewObj = FreeCAD.ActiveDocument.addObject("Part::Feature", "PreviewCutVolume") self.previewObj.ViewObject.ShapeColor = (1.00, 0.00, 0.00) @@ -80,12 +97,14 @@ class CutPlaneTaskPanel: self.grid.setObjectName("grid") self.title = QtGui.QLabel(self.form) self.grid.addWidget(self.title, 1, 0) - self.infoText = QtGui.QLabel(self.form) + self.infoText = QtGui.QLabel(self.form) self.grid.addWidget(self.infoText, 2, 0) self.combobox = QtGui.QComboBox() self.combobox.setCurrentIndex(0) self.grid.addWidget(self.combobox, 2, 1) - QtCore.QObject.connect(self.combobox,QtCore.SIGNAL("currentIndexChanged(int)"), self.previewCutVolume) + QtCore.QObject.connect( + self.combobox, QtCore.SIGNAL("currentIndexChanged(int)"), self.previewCutVolume + ) self.retranslateUi(self.form) self.previewCutVolume(self.combobox.currentIndex()) @@ -110,10 +129,12 @@ class CutPlaneTaskPanel: def getStandardButtons(self): from PySide import QtGui + return QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel def previewCutVolume(self, i): import Arch + cutVolume = Arch.getCutVolume(self.cutter, self.base) if i == 1: cutVolume = cutVolume[1] diff --git a/src/Mod/BIM/bimcommands/BimDiff.py b/src/Mod/BIM/bimcommands/BimDiff.py index 18020150a4..93b2f63540 100644 --- a/src/Mod/BIM/bimcommands/BimDiff.py +++ b/src/Mod/BIM/bimcommands/BimDiff.py @@ -86,9 +86,7 @@ class BIM_Diff: if hasattr(obj, "IfcData") and obj.ViewObject.Visibility: if "IfcUID" in obj.IfcData: activedocids[obj.IfcData["IfcUID"]] = obj - elif obj.isDerivedFrom( - "Part::Feature" - ): # discard BuildingParts + elif obj.isDerivedFrom("Part::Feature"): # discard BuildingParts objswithoutid.append(obj) otherdocids = {} # other doc to be merged to the main one @@ -116,9 +114,7 @@ class BIM_Diff: if obj.Label != mainobj.Label: # object has a different name renamed[mainobj.Name] = obj.Label - if obj.IfcProperties and ( - obj.IfcProperties != mainobj.IfcProperties - ): + if obj.IfcProperties and (obj.IfcProperties != mainobj.IfcProperties): # properties have changed propertieschanged[id] = obj.IfcProperties if hasattr(obj, "Shape") and hasattr(mainobj, "Shape"): @@ -126,26 +122,19 @@ class BIM_Diff: if v < VOL_TOLERANCE: # identical volume l = ( - obj.Shape.BoundBox.Center.sub( - mainobj.Shape.BoundBox.Center - ) + obj.Shape.BoundBox.Center.sub(mainobj.Shape.BoundBox.Center) ).Length if l < MOVE_TOLERANCE: # identical position if ( - abs( - obj.Shape.BoundBox.XMin - - mainobj.Shape.BoundBox.XMin + abs(obj.Shape.BoundBox.XMin - mainobj.Shape.BoundBox.XMin) + < MOVE_TOLERANCE + and abs( + obj.Shape.BoundBox.YMin - mainobj.Shape.BoundBox.YMin ) < MOVE_TOLERANCE and abs( - obj.Shape.BoundBox.YMin - - mainobj.Shape.BoundBox.YMin - ) - < MOVE_TOLERANCE - and abs( - obj.Shape.BoundBox.YMin - - mainobj.Shape.BoundBox.YMin + obj.Shape.BoundBox.YMin - mainobj.Shape.BoundBox.YMin ) < MOVE_TOLERANCE ): @@ -156,10 +145,7 @@ class BIM_Diff: and ( obj.Material and mainobj.Material - and ( - obj.Material.Label - == mainobj.Material.Label - ) + and (obj.Material.Label == mainobj.Material.Label) ) or (obj.Material == mainobj.Material) ): @@ -216,12 +202,8 @@ class BIM_Diff: for id, obj in activedocids.items(): if not id in otherdocids: - if obj.isDerivedFrom( - "Part::Feature" - ): # don't count building parts - print( - "Object", obj.Label, "does not exist anymore in new document" - ) + if obj.isDerivedFrom("Part::Feature"): # don't count building parts + print("Object", obj.Label, "does not exist anymore in new document") subtractions.append(obj) # try to find our objects without ID @@ -229,29 +211,19 @@ class BIM_Diff: for obj in objswithoutid: for id, otherobj in otherdocids.items(): if not id in activedocids: - if ( - abs(otherobj.Shape.Volume - obj.Shape.Volume) - < VOL_TOLERANCE - ): + if abs(otherobj.Shape.Volume - obj.Shape.Volume) < VOL_TOLERANCE: if ( - otherobj.Shape.BoundBox.Center.sub( - obj.Shape.BoundBox.Center - ) + otherobj.Shape.BoundBox.Center.sub(obj.Shape.BoundBox.Center) ).Length < MOVE_TOLERANCE: if ( - abs( - obj.Shape.BoundBox.XMin - - otherobj.Shape.BoundBox.XMin + abs(obj.Shape.BoundBox.XMin - otherobj.Shape.BoundBox.XMin) + < MOVE_TOLERANCE + and abs( + obj.Shape.BoundBox.YMin - otherobj.Shape.BoundBox.YMin ) < MOVE_TOLERANCE and abs( - obj.Shape.BoundBox.YMin - - otherobj.Shape.BoundBox.YMin - ) - < MOVE_TOLERANCE - and abs( - obj.Shape.BoundBox.YMin - - otherobj.Shape.BoundBox.YMin + obj.Shape.BoundBox.YMin - otherobj.Shape.BoundBox.YMin ) < MOVE_TOLERANCE ): @@ -280,9 +252,7 @@ class BIM_Diff: newmats[obj.Label] = obj if newmats: - group = otherdoc.addObject( - "App::DocumentObjectGroup", "New_materials" - ) + group = otherdoc.addObject("App::DocumentObjectGroup", "New_materials") for newmat in newmats.values(): group.addObject(newmat) @@ -381,9 +351,7 @@ class BIM_Diff: import ArchMaterial ArchMaterial._ArchMaterial(newmat) - ArchMaterial._ViewProviderArchMaterial( - newmat.ViewObject - ) + ArchMaterial._ViewProviderArchMaterial(newmat.ViewObject) newmat.Material = mat.Material print( "Changing material of", @@ -424,9 +392,7 @@ class BIM_Diff: "", str(len(renamed)) + " " - + translate( - "BIM", "objects had their name changed. Rename them?" - ), + + translate("BIM", "objects had their name changed. Rename them?"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No, ) @@ -443,9 +409,7 @@ class BIM_Diff: "", str(len(propertieschanged)) + " " - + translate( - "BIM", "objects had their properties changed. Update?" - ), + + translate("BIM", "objects had their properties changed. Update?"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No, ) @@ -532,9 +496,7 @@ class BIM_Diff: QtGui.QMessageBox.No, ) if reply == QtGui.QMessageBox.Yes: - group = activedoc.addObject( - "App::DocumentObjectGroup", "ToDelete" - ) + group = activedoc.addObject("App::DocumentObjectGroup", "ToDelete") group.Label = "To Delete" for obj in subtractions: group.addObject(obj) diff --git a/src/Mod/BIM/bimcommands/BimDimensions.py b/src/Mod/BIM/bimcommands/BimDimensions.py index fae43083e6..6a7a7ce357 100644 --- a/src/Mod/BIM/bimcommands/BimDimensions.py +++ b/src/Mod/BIM/bimcommands/BimDimensions.py @@ -41,9 +41,7 @@ class BIM_DimensionAligned(gui_dimensions.Dimension): return { "Pixmap": "BIM_DimensionAligned", "MenuText": QT_TRANSLATE_NOOP("BIM_DimensionAligned", "Aligned Dimension"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_DimensionAligned", "Creates an aligned dimension" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_DimensionAligned", "Creates an aligned dimension"), "Accel": "D, I", } @@ -56,9 +54,7 @@ class BIM_DimensionHorizontal(gui_dimensions.Dimension): def GetResources(self): return { "Pixmap": "BIM_DimensionHorizontal.svg", - "MenuText": QT_TRANSLATE_NOOP( - "BIM_DimensionHorizontal", "Horizontal Dimension" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_DimensionHorizontal", "Horizontal Dimension"), "ToolTip": QT_TRANSLATE_NOOP( "BIM_DimensionHorizontal", "Creates an horizontal dimension" ), @@ -80,12 +76,8 @@ class BIM_DimensionVertical(gui_dimensions.Dimension): def GetResources(self): return { "Pixmap": "BIM_DimensionVertical", - "MenuText": QT_TRANSLATE_NOOP( - "BIM_DimensionVertical", "Vertical Dimension" - ), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_DimensionVertical", "Creates a vertical dimension" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_DimensionVertical", "Vertical Dimension"), + "ToolTip": QT_TRANSLATE_NOOP("BIM_DimensionVertical", "Creates a vertical dimension"), "Accel": "D, V", } diff --git a/src/Mod/BIM/bimcommands/BimDoor.py b/src/Mod/BIM/bimcommands/BimDoor.py index 040684d9e1..213cb7e27c 100644 --- a/src/Mod/BIM/bimcommands/BimDoor.py +++ b/src/Mod/BIM/bimcommands/BimDoor.py @@ -41,9 +41,7 @@ class BIM_Door(BimWindow.Arch_Window): return { "Pixmap": "BIM_Door", "MenuText": QT_TRANSLATE_NOOP("BIM_Door", "Door"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Door", "Places a door at a given location" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Door", "Places a door at a given location"), "Accel": "D,O", } diff --git a/src/Mod/BIM/bimcommands/BimDrawingView.py b/src/Mod/BIM/bimcommands/BimDrawingView.py index 9e2704550e..d75496f869 100644 --- a/src/Mod/BIM/bimcommands/BimDrawingView.py +++ b/src/Mod/BIM/bimcommands/BimDrawingView.py @@ -34,15 +34,18 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class BIM_DrawingView: - """The command definition for the Drawing View command""" def GetResources(self): - return {'Pixmap' : 'BIM_ArchView', - 'MenuText': QT_TRANSLATE_NOOP("BIM_DrawingView","2D Drawing"), - 'Accel': "V, D", - 'ToolTip': QT_TRANSLATE_NOOP("BIM_DrawingView","Creates a drawing container to contain elements of a 2D view")} + return { + "Pixmap": "BIM_ArchView", + "MenuText": QT_TRANSLATE_NOOP("BIM_DrawingView", "2D Drawing"), + "Accel": "V, D", + "ToolTip": QT_TRANSLATE_NOOP( + "BIM_DrawingView", "Creates a drawing container to contain elements of a 2D view" + ), + } def IsActive(self): @@ -52,7 +55,8 @@ class BIM_DrawingView: def Activated(self): import Draft - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create 2D View")) + + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create 2D View")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.addModule("WorkingPlane") @@ -62,8 +66,10 @@ class BIM_DrawingView: if len(s) == 1: s = s[0] if Draft.getType(s) == "SectionPlane": - FreeCADGui.doCommand("vobj = Draft.make_shape2dview(FreeCAD.ActiveDocument."+s.Name+")") - FreeCADGui.doCommand("vobj.Label = \""+translate("BIM","Viewed lines")+"\"") + FreeCADGui.doCommand( + "vobj = Draft.make_shape2dview(FreeCAD.ActiveDocument." + s.Name + ")" + ) + FreeCADGui.doCommand('vobj.Label = "' + translate("BIM", "Viewed lines") + '"') FreeCADGui.doCommand("vobj.InPlace = False") FreeCADGui.doCommand("obj.addObject(vobj)") bb = FreeCAD.BoundBox() @@ -71,13 +77,15 @@ class BIM_DrawingView: if hasattr(so, "Shape"): bb.add(so.Shape.BoundBox) if bb.isInside(s.Shape.CenterOfMass): - FreeCADGui.doCommand("cobj = Draft.make_shape2dview(FreeCAD.ActiveDocument."+s.Name+")") - FreeCADGui.doCommand("cobj.Label = \""+translate("BIM","Cut lines")+"\"") + FreeCADGui.doCommand( + "cobj = Draft.make_shape2dview(FreeCAD.ActiveDocument." + s.Name + ")" + ) + FreeCADGui.doCommand('cobj.Label = "' + translate("BIM", "Cut lines") + '"') FreeCADGui.doCommand("cobj.InPlace = False") - FreeCADGui.doCommand("cobj.ProjectionMode = \"Cutfaces\"") + FreeCADGui.doCommand('cobj.ProjectionMode = "Cutfaces"') FreeCADGui.doCommand("obj.addObject(cobj)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() -FreeCADGui.addCommand('BIM_DrawingView', BIM_DrawingView()) +FreeCADGui.addCommand("BIM_DrawingView", BIM_DrawingView()) diff --git a/src/Mod/BIM/bimcommands/BimEquipment.py b/src/Mod/BIM/bimcommands/BimEquipment.py index ccf2eeaa99..f53227621b 100644 --- a/src/Mod/BIM/bimcommands/BimEquipment.py +++ b/src/Mod/BIM/bimcommands/BimEquipment.py @@ -33,17 +33,19 @@ translate = FreeCAD.Qt.translate PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") - class Arch_Equipment: - "the Arch Equipment command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Equipment', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Equipment","Equipment"), - 'Accel': "E, Q", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Equipment","Creates an equipment from a selected object (Part or Mesh)")} + return { + "Pixmap": "Arch_Equipment", + "MenuText": QT_TRANSLATE_NOOP("Arch_Equipment", "Equipment"), + "Accel": "E, Q", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Equipment", "Creates an equipment from a selected object (Part or Mesh)" + ), + } def IsActive(self): @@ -54,27 +56,29 @@ class Arch_Equipment: s = FreeCADGui.Selection.getSelection() if not s: - FreeCAD.Console.PrintError(translate("Arch","Select a base shape object and optionally a mesh object")) + FreeCAD.Console.PrintError( + translate("Arch", "Select a base shape object and optionally a mesh object") + ) else: base = "" mesh = "" if len(s) == 2: - if hasattr(s[0],'Shape'): + if hasattr(s[0], "Shape"): base = s[0].Name elif s[0].isDerivedFrom("Mesh::Feature"): mesh = s[0].Name - if hasattr(s[1],'Shape'): + if hasattr(s[1], "Shape"): if mesh: base = s[1].Name elif s[1].isDerivedFrom("Mesh::Feature"): if base: mesh = s[1].Name else: - if hasattr(s[0],'Shape'): + if hasattr(s[0], "Shape"): base = s[0].Name elif s[0].isDerivedFrom("Mesh::Feature"): mesh = s[0].Name - FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Equipment"))) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch", "Create Equipment"))) FreeCADGui.addModule("Arch") if base: base = "FreeCAD.ActiveDocument." + base @@ -86,9 +90,13 @@ class Arch_Equipment: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() # get diffuse color info from base object - if base and hasattr(s[0].ViewObject,"DiffuseColor"): - FreeCADGui.doCommand("FreeCAD.ActiveDocument.Objects[-1].ViewObject.DiffuseColor = " + base + ".ViewObject.DiffuseColor") + if base and hasattr(s[0].ViewObject, "DiffuseColor"): + FreeCADGui.doCommand( + "FreeCAD.ActiveDocument.Objects[-1].ViewObject.DiffuseColor = " + + base + + ".ViewObject.DiffuseColor" + ) return -FreeCADGui.addCommand('Arch_Equipment',Arch_Equipment()) +FreeCADGui.addCommand("Arch_Equipment", Arch_Equipment()) diff --git a/src/Mod/BIM/bimcommands/BimExamples.py b/src/Mod/BIM/bimcommands/BimExamples.py index 82cceafe39..2430130480 100644 --- a/src/Mod/BIM/bimcommands/BimExamples.py +++ b/src/Mod/BIM/bimcommands/BimExamples.py @@ -44,6 +44,7 @@ class BIM_Examples: def Activated(self): from PySide import QtGui + QtGui.QDesktopServices.openUrl("https://github.com/yorikvanhavre/FreeCAD-BIM-examples") diff --git a/src/Mod/BIM/bimcommands/BimFence.py b/src/Mod/BIM/bimcommands/BimFence.py index fb4ba38c65..3ea3d514f5 100644 --- a/src/Mod/BIM/bimcommands/BimFence.py +++ b/src/Mod/BIM/bimcommands/BimFence.py @@ -37,9 +37,13 @@ class Arch_Fence: "the Arch Fence command definition" def GetResources(self): - return {'Pixmap': 'Arch_Fence', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Fence", "Fence"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Fence", "Creates a fence object from a selected section, post and path")} + return { + "Pixmap": "Arch_Fence", + "MenuText": QT_TRANSLATE_NOOP("Arch_Fence", "Fence"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Fence", "Creates a fence object from a selected section, post and path" + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -47,10 +51,16 @@ class Arch_Fence: def Activated(self): if len(FreeCADGui.Selection.getSelection()) != 3: - FreeCAD.Console.PrintError(translate('Arch Fence selection','Select a section, post and path in exactly this order to build a fence.')+"\n") + FreeCAD.Console.PrintError( + translate( + "Arch Fence selection", + "Select a section, post and path in exactly this order to build a fence.", + ) + + "\n" + ) return doc = FreeCAD.ActiveDocument - doc.openTransaction(translate("Arch","Create Fence")) + doc.openTransaction(translate("Arch", "Create Fence")) FreeCADGui.addModule("Arch") FreeCADGui.doCommand("section = FreeCADGui.Selection.getSelection()[0]") FreeCADGui.doCommand("post = FreeCADGui.Selection.getSelection()[1]") @@ -59,4 +69,5 @@ class Arch_Fence: doc.commitTransaction() doc.recompute() -FreeCADGui.addCommand('Arch_Fence', Arch_Fence()) + +FreeCADGui.addCommand("Arch_Fence", Arch_Fence()) diff --git a/src/Mod/BIM/bimcommands/BimFrame.py b/src/Mod/BIM/bimcommands/BimFrame.py index e9a2df88a1..e3e4ea5836 100644 --- a/src/Mod/BIM/bimcommands/BimFrame.py +++ b/src/Mod/BIM/bimcommands/BimFrame.py @@ -34,15 +34,19 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Frame: - "the Arch Frame command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Frame', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Frame","Frame"), - 'Accel': "F, R", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Frame","Creates a frame object from a planar 2D object (the extrusion path(s)) and a profile. Make sure objects are selected in that order.")} + return { + "Pixmap": "Arch_Frame", + "MenuText": QT_TRANSLATE_NOOP("Arch_Frame", "Frame"), + "Accel": "F, R", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Frame", + "Creates a frame object from a planar 2D object (the extrusion path(s)) and a profile. Make sure objects are selected in that order.", + ), + } def IsActive(self): @@ -53,13 +57,19 @@ class Arch_Frame: s = FreeCADGui.Selection.getSelection() if len(s) == 2: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Frame")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Frame")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") - FreeCADGui.doCommand("obj = Arch.makeFrame(FreeCAD.ActiveDocument."+s[0].Name+",FreeCAD.ActiveDocument."+s[1].Name+")") + FreeCADGui.doCommand( + "obj = Arch.makeFrame(FreeCAD.ActiveDocument." + + s[0].Name + + ",FreeCAD.ActiveDocument." + + s[1].Name + + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() -FreeCADGui.addCommand('Arch_Frame',Arch_Frame()) +FreeCADGui.addCommand("Arch_Frame", Arch_Frame()) diff --git a/src/Mod/BIM/bimcommands/BimGlue.py b/src/Mod/BIM/bimcommands/BimGlue.py index 767d84f4d3..b310decd45 100644 --- a/src/Mod/BIM/bimcommands/BimGlue.py +++ b/src/Mod/BIM/bimcommands/BimGlue.py @@ -47,6 +47,7 @@ class BIM_Glue: def Activated(self): import Part + sel = FreeCADGui.Selection.getSelection() if sel: rem = [] diff --git a/src/Mod/BIM/bimcommands/BimHelp.py b/src/Mod/BIM/bimcommands/BimHelp.py index c206c98322..3e140c0487 100644 --- a/src/Mod/BIM/bimcommands/BimHelp.py +++ b/src/Mod/BIM/bimcommands/BimHelp.py @@ -44,6 +44,7 @@ class BIM_Help: def Activated(self): from PySide import QtGui + QtGui.QDesktopServices.openUrl("https://www.freecadweb.org/wiki/BIM_Workbench") diff --git a/src/Mod/BIM/bimcommands/BimIfcElements.py b/src/Mod/BIM/bimcommands/BimIfcElements.py index 32916c0c68..18e1a13c00 100644 --- a/src/Mod/BIM/bimcommands/BimIfcElements.py +++ b/src/Mod/BIM/bimcommands/BimIfcElements.py @@ -92,18 +92,14 @@ class BIM_IfcElements: self.form.globalMaterial.addItem(translate("BIM", "Create new multi-material")) self.materials = [] for o in FreeCAD.ActiveDocument.Objects: - if o.isDerivedFrom("App::MaterialObject") or ( - Draft.getType(o) == "MultiMaterial" - ): + if o.isDerivedFrom("App::MaterialObject") or (Draft.getType(o) == "MultiMaterial"): self.materials.append(o.Name) - self.form.globalMaterial.addItem( - o.Label, QtGui.QIcon(":/icons/Arch_Material.svg") - ) + self.form.globalMaterial.addItem(o.Label, QtGui.QIcon(":/icons/Arch_Material.svg")) self.form.groupMode.currentIndexChanged.connect(self.update) self.form.tree.clicked.connect(self.onClickTree) - if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 + if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 self.form.onlyVisible.checkStateChanged.connect(self.update) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 self.form.onlyVisible.stateChanged.connect(self.update) self.form.buttonBox.accepted.connect(self.accept) self.form.buttonBox.rejected.connect(self.reject) @@ -113,9 +109,7 @@ class BIM_IfcElements: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) self.update() @@ -171,9 +165,7 @@ class BIM_IfcElements: mat = rolemat[1] obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): groups.setdefault(role, []).append([name, mat]) for group in groups.keys(): s1 = group + " (" + str(len(groups[group])) + ")" @@ -214,9 +206,7 @@ class BIM_IfcElements: mat = "Undefined" obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): groups.setdefault(mat, []).append([name, role]) for group in groups.keys(): @@ -333,9 +323,7 @@ class BIM_IfcElements: mat = rolemat[1] obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): it1 = QtGui.QStandardItem(obj.Label) it1.setIcon(getIcon(obj)) it1.setToolTip(obj.Name) @@ -380,9 +368,7 @@ class BIM_IfcElements: mat = None for index in sel: if index.column() == 0: - obj = FreeCAD.ActiveDocument.getObject( - self.model.itemFromIndex(index).toolTip() - ) + obj = FreeCAD.ActiveDocument.getObject(self.model.itemFromIndex(index).toolTip()) if obj: FreeCADGui.Selection.addSelection(obj) @@ -462,22 +448,15 @@ class BIM_IfcElements: mats = [ o.Name for o in FreeCAD.ActiveDocument.Objects - if ( - o.isDerivedFrom("App::MaterialObject") - or (Draft.getType(o) == "MultiMaterial") - ) + if (o.isDerivedFrom("App::MaterialObject") or (Draft.getType(o) == "MultiMaterial")) ] if len(mats) != len(self.materials): newmats = [m for m in mats if not m in self.materials] self.materials = mats self.form.globalMaterial.clear() self.form.globalMaterial.addItem(" ") - self.form.globalMaterial.addItem( - translate("BIM", "Create new material") - ) - self.form.globalMaterial.addItem( - translate("BIM", "Create new multi-material") - ) + self.form.globalMaterial.addItem(translate("BIM", "Create new material")) + self.form.globalMaterial.addItem(translate("BIM", "Create new multi-material")) for m in self.materials: o = FreeCAD.ActiveDocument.getObject(m) if o: @@ -539,16 +518,12 @@ class BIM_IfcElements: if obj.Material: if obj.Material.Name != mat: if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change material" - ) + FreeCAD.ActiveDocument.openTransaction("Change material") changed = True obj.Material = mobj else: if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change material" - ) + FreeCAD.ActiveDocument.openTransaction("Change material") changed = True obj.Material = mobj if changed: diff --git a/src/Mod/BIM/bimcommands/BimIfcExplorer.py b/src/Mod/BIM/bimcommands/BimIfcExplorer.py index 9f80eba0a7..7dcc7b31e4 100644 --- a/src/Mod/BIM/bimcommands/BimIfcExplorer.py +++ b/src/Mod/BIM/bimcommands/BimIfcExplorer.py @@ -163,9 +163,7 @@ class BIM_IfcExplorer: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.dialog.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.dialog.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.dialog.rect().center() ) # open a file and show the dialog @@ -180,9 +178,9 @@ class BIM_IfcExplorer: from PySide import QtGui self.filename = "" - lastfolder = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/BIM" - ).GetString("lastIfcExplorerFolder", "") + lastfolder = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM").GetString( + "lastIfcExplorerFolder", "" + ) filename = QtGui.QFileDialog.getOpenFileName( None, translate("BIM", "Select an IFC file"), @@ -227,7 +225,8 @@ class BIM_IfcExplorer: "BIM", "IfcSite element was not found in %s. Unable to explore.", ) - % self.filename + "\n" + % self.filename + + "\n" ) return @@ -309,10 +308,7 @@ class BIM_IfcExplorer: g = m.geometry v = g.verts f = g.faces - verts = [ - FreeCAD.Vector(v[i : i + 3]) - for i in range(0, len(v), 3) - ] + verts = [FreeCAD.Vector(v[i : i + 3]) for i in range(0, len(v), 3)] faces = [tuple(f[i : i + 3]) for i in range(0, len(f), 3)] omesh = Mesh.Mesh((verts, faces)) if trf: @@ -321,9 +317,7 @@ class BIM_IfcExplorer: basemesh.addMesh(omesh) except: pass - self.mesh = FreeCAD.ActiveDocument.addObject( - "Mesh::Feature", "IFCMesh" - ) + self.mesh = FreeCAD.ActiveDocument.addObject("Mesh::Feature", "IFCMesh") self.mesh.Mesh = basemesh self.mesh.ViewObject.Transparency = 85 FreeCAD.ActiveDocument.recompute() @@ -414,9 +408,7 @@ class BIM_IfcExplorer: item.setFont(0, self.bold) if isinstance(parent, QtGui.QTreeWidgetItem): parent.setExpanded(True) - item.setText( - 0, "#" + self.tostr(eid) + " : " + self.tostr(entity.is_a()) + name - ) + item.setText(0, "#" + self.tostr(eid) + " : " + self.tostr(entity.is_a()) + name) if entity.is_a() in ["IfcWall", "IfcWallStandardCase"]: item.setIcon(0, QtGui.QIcon(":icons/Arch_Wall_Tree.svg")) elif entity.is_a() in ["IfcBuildingElementProxy"]: @@ -490,10 +482,7 @@ class BIM_IfcExplorer: argvalue = getattr(entity, argname) except AttributeError: FreeCAD.Console.PrintError( - translate("BIM", "Error in entity") - + " " - + self.tostr(entity) - + "\n" + translate("BIM", "Error in entity") + " " + self.tostr(entity) + "\n" ) break else: @@ -568,8 +557,7 @@ class BIM_IfcExplorer: item = QtGui.QTreeWidgetItem(parent) item.setText( 0, - "PropertySet: " - + self.tostr(rel.RelatingPropertyDefinition.Name), + "PropertySet: " + self.tostr(rel.RelatingPropertyDefinition.Name), ) item.setFont(0, self.bold) item.setFirstColumnSpanned(True) diff --git a/src/Mod/BIM/bimcommands/BimIfcProperties.py b/src/Mod/BIM/bimcommands/BimIfcProperties.py index 5dd830f7f4..9d814578ee 100644 --- a/src/Mod/BIM/bimcommands/BimIfcProperties.py +++ b/src/Mod/BIM/bimcommands/BimIfcProperties.py @@ -41,9 +41,7 @@ class BIM_IfcProperties: def GetResources(self): return { "Pixmap": "BIM_IfcProperties", - "MenuText": QT_TRANSLATE_NOOP( - "BIM_IfcProperties", "Manage IFC Properties" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_IfcProperties", "Manage IFC Properties"), "ToolTip": QT_TRANSLATE_NOOP( "BIM_IfcProperties", "Manages the different IFC properties of the BIM objects", @@ -99,12 +97,9 @@ class BIM_IfcProperties: except (ImportError, AttributeError): import ArchComponent - self.ptypes = ( - ArchComponent.SimplePropertyTypes + ArchComponent.MeasurePropertyTypes - ) + self.ptypes = ArchComponent.SimplePropertyTypes + ArchComponent.MeasurePropertyTypes self.plabels = [ - "".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:] - for t in self.ptypes + "".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:] for t in self.ptypes ] self.psetdefs = {} psetpath = os.path.join( @@ -134,22 +129,19 @@ class BIM_IfcProperties: ) # set combos - self.form.comboProperty.addItems( - [translate("BIM", "Add property")] + self.plabels - ) + self.form.comboProperty.addItems([translate("BIM", "Add property")] + self.plabels) self.form.comboPset.addItems( - [translate("BIM", "Add property set"), translate("BIM", "New")] - + self.psetkeys + [translate("BIM", "Add property set"), translate("BIM", "New")] + self.psetkeys ) # connect signals self.form.tree.selectionModel().selectionChanged.connect(self.updateProperties) self.form.groupMode.currentIndexChanged.connect(self.update) - if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 + if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 self.form.onlyVisible.checkStateChanged.connect(self.update) self.form.onlySelected.checkStateChanged.connect(self.onSelected) self.form.onlyMatches.checkStateChanged.connect(self.update) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 self.form.onlyVisible.stateChanged.connect(self.update) self.form.onlySelected.stateChanged.connect(self.onSelected) self.form.onlyMatches.stateChanged.connect(self.update) @@ -165,9 +157,7 @@ class BIM_IfcProperties: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) self.update() @@ -185,9 +175,7 @@ class BIM_IfcProperties: for obj in objects: role = self.getRole(obj) if role: - if hasattr(obj, "IfcProperties") and isinstance( - obj.IfcProperties, dict - ): + if hasattr(obj, "IfcProperties") and isinstance(obj.IfcProperties, dict): props = obj.IfcProperties elif hasattr(obj, "IfcClass"): props = self.getNativeIfcProperties(obj) @@ -265,9 +253,7 @@ class BIM_IfcProperties: role = role[0] obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): groups.setdefault(role, []).append(name) for group in groups.keys(): @@ -356,9 +342,7 @@ class BIM_IfcProperties: role = role[0] obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): it1 = QtGui.QStandardItem(obj.Label) icon = obj.ViewObject.Icon it1.setIcon(icon) @@ -406,16 +390,17 @@ class BIM_IfcProperties: if not changed: FreeCAD.ActiveDocument.openTransaction("Change properties") changed = True - if hasattr(obj,"IfcClass"): - print("props:",props) - for key,value in values[1].items(): + if hasattr(obj, "IfcClass"): + print("props:", props) + for key, value in values[1].items(): if ";;" in key and ";;" in value: pname, pset = key.split(";;") ptype, pvalue = value.split(";;") from nativeifc import ifc_psets # lazy loading + fctype = ifc_psets.get_freecad_type(ptype) if not pname in obj.PropertiesList: - obj.addProperty(fctype, pname, pset, ptype+":"+pname) + obj.addProperty(fctype, pname, pset, ptype + ":" + pname) ifc_psets.edit_pset(obj, pname, force=True) if pvalue: setattr(obj, pname, pvalue) @@ -424,9 +409,7 @@ class BIM_IfcProperties: "App::PropertyMap", "IfcPRoperties", "IFC", - QT_TRANSLATE_NOOP( - "App::Property", "IFC properties of this object" - ), + QT_TRANSLATE_NOOP("App::Property", "IFC properties of this object"), locked=True, ) if hasattr(obj, "IfcProperties"): @@ -449,7 +432,7 @@ class BIM_IfcProperties: if ":" in ttip: ptype, pname = ttip.split(":") if pset not in ["Base", "IFC", "Geometry"]: - props[pname+";;"+pset] = ptype+";;"+str(getattr(obj,p)) + props[pname + ";;" + pset] = ptype + ";;" + str(getattr(obj, p)) return props def getSearchResults(self, obj): @@ -565,9 +548,7 @@ class BIM_IfcProperties: top = QtGui.QStandardItem(pset) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.propmodel.appendRow( - [top, QtGui.QStandardItem(), QtGui.QStandardItem()] - ) + self.propmodel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) for plist in plists: pname = plist[0] if ";;" in pname: @@ -613,31 +594,18 @@ class BIM_IfcProperties: if name in self.objectslist: # print("object",name,self.objectslist[name][1]) if pvalue == "*VARIES*": - if not ( - prop + ";;" + pset in self.objectslist[name][1] - ): + if not (prop + ";;" + pset in self.objectslist[name][1]): # print("adding",prop) - self.objectslist[name][1][ - prop + ";;" + pset - ] = (ptype + ";;") + self.objectslist[name][1][prop + ";;" + pset] = ptype + ";;" else: pval = ptype + ";;" + pvalue if prop in self.objectslist[name][1]: - if ( - self.objectslist[name][1][ - prop + ";;" + pset - ] - != pval - ): + if self.objectslist[name][1][prop + ";;" + pset] != pval: # print("modifying",prop) - self.objectslist[name][1][ - prop + ";;" + pset - ] = pval + self.objectslist[name][1][prop + ";;" + pset] = pval else: # print("adding",prop) - self.objectslist[name][1][ - prop + ";;" + pset - ] = pval + self.objectslist[name][1][prop + ";;" + pset] = pval if remove: for index in sel: if index.column() == 0: @@ -724,9 +692,7 @@ class BIM_IfcProperties: top = QtGui.QStandardItem(name) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.propmodel.appendRow( - [top, QtGui.QStandardItem(), QtGui.QStandardItem()] - ) + self.propmodel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) elif idx > 1: psetlabel = self.psetkeys[idx - 2] psetdef = "Pset_" + psetlabel.replace(" ", "") @@ -734,9 +700,7 @@ class BIM_IfcProperties: top = QtGui.QStandardItem(psetdef) top.setDragEnabled(False) top.setToolTip("PropertySet") - self.propmodel.appendRow( - [top, QtGui.QStandardItem(), QtGui.QStandardItem()] - ) + self.propmodel.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()]) for i in range(0, len(self.psetdefs[psetdef]), 2): self.addProperty( pset=top, @@ -808,9 +772,9 @@ if FreeCAD.GuiUp: elif "Real" in ptype: editor = QtGui.QDoubleSpinBox(parent) editor.setDecimals( - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Units" - ).GetInt("Decimals", 2) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( + "Decimals", 2 + ) ) elif ("Boolean" in ptype) or ("Logical" in ptype): editor = QtGui.QComboBox(parent) @@ -842,13 +806,9 @@ if FreeCAD.GuiUp: editor.setValue(float(index.data())) except (TypeError, ValueError, AttributeError): editor.setValue(0) - elif ("Boolean" in editor.objectName()) or ( - "Logical" in editor.objectName() - ): + elif ("Boolean" in editor.objectName()) or ("Logical" in editor.objectName()): try: - editor.setCurrentIndex( - ["true", "false"].index(index.data().lower()) - ) + editor.setCurrentIndex(["true", "false"].index(index.data().lower())) except (ValueError, AttributeError): editor.setCurrentIndex(1) elif "Measure" in editor.objectName(): @@ -888,13 +848,9 @@ if FreeCAD.GuiUp: data = self.plabels[idx] model.setData(index, data) else: - if ("Integer" in editor.objectName()) or ( - "Real" in editor.objectName() - ): + if ("Integer" in editor.objectName()) or ("Real" in editor.objectName()): model.setData(index, str(editor.value())) - elif ("Boolean" in editor.objectName()) or ( - "Logical" in editor.objectName() - ): + elif ("Boolean" in editor.objectName()) or ("Logical" in editor.objectName()): model.setData(index, editor.currentText()) elif "Measure" in editor.objectName(): model.setData(index, editor.property("text")) diff --git a/src/Mod/BIM/bimcommands/BimIfcQuantities.py b/src/Mod/BIM/bimcommands/BimIfcQuantities.py index 12442948ae..f5ea094058 100644 --- a/src/Mod/BIM/bimcommands/BimIfcQuantities.py +++ b/src/Mod/BIM/bimcommands/BimIfcQuantities.py @@ -69,9 +69,7 @@ class BIM_IfcQuantities: def GetResources(self): return { "Pixmap": "BIM_IfcQuantities", - "MenuText": QT_TRANSLATE_NOOP( - "BIM_IfcQuantities", "Manage IFC Quantities" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_IfcQuantities", "Manage IFC Quantities"), "ToolTip": QT_TRANSLATE_NOOP( "BIM_IfcQuantities", "Manages how the quantities of different elements of the BIM project will be exported to IFC", @@ -101,8 +99,8 @@ class BIM_IfcQuantities: # support for arrays array = self.getArray(obj) for i in range(array): - if i > 0: # the first item already went above - self.objectslist[obj.Name+"+array"+str(i)] = role + if i > 0: # the first item already went above + self.objectslist[obj.Name + "+array" + str(i)] = role try: import ArchIFC @@ -129,9 +127,9 @@ class BIM_IfcQuantities: self.form.buttonBox.accepted.connect(self.accept) self.form.buttonBox.rejected.connect(self.reject) self.form.quantities.clicked.connect(self.onClickTree) - if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 + if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0 self.form.onlyVisible.checkStateChanged.connect(self.update) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 self.form.onlyVisible.stateChanged.connect(self.update) self.form.buttonRefresh.clicked.connect(self.update) self.form.buttonApply.clicked.connect(self.add_qto) @@ -139,9 +137,7 @@ class BIM_IfcQuantities: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) self.update() @@ -186,7 +182,9 @@ class BIM_IfcQuantities: ] self.qtokeys.sort() self.form.comboQto.addItems( - [translate("BIM", "Add quantity set..."),] + [ + translate("BIM", "Add quantity set..."), + ] + self.qtokeys ) @@ -199,10 +197,10 @@ class BIM_IfcQuantities: if len(FreeCADGui.Selection.getSelection()) != 1: return obj = FreeCADGui.Selection.getSelection()[0] - qto = list(self.qtodefs.keys())[index-1] + qto = list(self.qtodefs.keys())[index - 1] self.ifcqtolist.setdefault(obj.Name, []).append(qto) self.update_line(obj.Name, qto) - FreeCAD.Console.PrintMessage(translate("BIM", "Adding quantity set")+": "+qto+"\n") + FreeCAD.Console.PrintMessage(translate("BIM", "Adding quantity set") + ": " + qto + "\n") def apply_qto(self, obj, qto): "Adds a standard qto set to the object" @@ -211,6 +209,7 @@ class BIM_IfcQuantities: qset = None if hasattr(obj, "StepId"): from nativeifc import ifc_tools + ifcfile = ifc_tools.get_ifcfile(obj) element = ifc_tools.get_ifc_element(obj) if not ifcfile or not element: @@ -218,17 +217,17 @@ class BIM_IfcQuantities: qset = ifc_tools.api_run("pset.add_qto", ifcfile, product=element, name=qto) for i in range(0, len(val), 2): qname = val[i] - qtype = QTO_TYPES[val[i+1]] + qtype = QTO_TYPES[val[i + 1]] if not qname in obj.PropertiesList: - obj.addProperty(qtype, qname, "Quantities", val[i+1], locked=True) + obj.addProperty(qtype, qname, "Quantities", val[i + 1], locked=True) qval = 0 i = self.get_row(obj.Name) if i > -1: for j, p in enumerate(QPROPS): - it = self.qmodel.item(i, j+1) + it = self.qmodel.item(i, j + 1) t = it.text() if t: - t = t.replace("²","^2").replace("Âł","^3") + t = t.replace("²", "^2").replace("Âł", "^3") qval = FreeCAD.Units.Quantity(t).Value if qval: setattr(obj, qname, qval) @@ -245,9 +244,7 @@ class BIM_IfcQuantities: # quantities tab self.qmodel.clear() - self.qmodel.setHorizontalHeaderLabels( - [translate("BIM", "Label")] + TR_QPROPS - ) + self.qmodel.setHorizontalHeaderLabels([translate("BIM", "Label")] + TR_QPROPS) self.form.quantities.setColumnWidth(0, 200) # TODO remember width quantheaders = self.form.quantities.header() # QHeaderView instance quantheaders.setSectionsClickable(True) @@ -266,9 +263,7 @@ class BIM_IfcQuantities: suffix = " (duplicate)" obj = FreeCAD.ActiveDocument.getObject(name) if obj: - if ( - not self.form.onlyVisible.isChecked() - ) or obj.ViewObject.isVisible(): + if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible(): if obj.isDerivedFrom("Part::Feature") and not ( Draft.getType(obj) == "Site" ): @@ -280,9 +275,7 @@ class BIM_IfcQuantities: for prop in QPROPS: it = QtGui.QStandardItem() val = None - if hasattr(obj, prop) and ( - "Hidden" not in obj.getEditorMode(prop) - ): + if hasattr(obj, prop) and ("Hidden" not in obj.getEditorMode(prop)): val = self.get_text(obj, prop) it.setText(val) it.setCheckable(True) @@ -311,6 +304,7 @@ class BIM_IfcQuantities: if not "StepId" in obj.PropertiesList: return False from nativeifc import ifc_tools + element = ifc_tools.get_ifc_element(obj) if not element: return False @@ -349,7 +343,7 @@ class BIM_IfcQuantities: obj = FreeCAD.ActiveDocument.getObject(name) qto_val = self.qtodefs[qto] for j, p in enumerate(QPROPS): - it = self.qmodel.item(i, j+1) + it = self.qmodel.item(i, j + 1) if p in obj.PropertiesList: val = self.get_text(obj, p) it.setText(val) @@ -390,9 +384,7 @@ class BIM_IfcQuantities: changed = False if self.ifcqtolist: if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change quantities" - ) + FreeCAD.ActiveDocument.openTransaction("Change quantities") changed = True for key, val in self.ifcqtolist.items(): obj = FreeCAD.ActiveDocument.getObject(key) @@ -412,9 +404,7 @@ class BIM_IfcQuantities: if getattr(obj, QPROPS[i]).getUserPreferred()[0] != val: setattr(obj, QPROPS[i], val) if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change quantities" - ) + FreeCAD.ActiveDocument.openTransaction("Change quantities") changed = True d = None if hasattr(obj, "IfcAttributes"): @@ -431,9 +421,7 @@ class BIM_IfcQuantities: d["Export" + QPROPS[i]] = "True" setattr(obj, att, d) if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change quantities" - ) + FreeCAD.ActiveDocument.openTransaction("Change quantities") changed = True else: if "Export" + QPROPS[i] in d: @@ -441,9 +429,7 @@ class BIM_IfcQuantities: d["Export" + QPROPS[i]] = "False" setattr(obj, att, d) if not changed: - FreeCAD.ActiveDocument.openTransaction( - "Change quantities" - ) + FreeCAD.ActiveDocument.openTransaction("Change quantities") changed = True elif "StepId" not in obj.PropertiesList: FreeCAD.Console.PrintError( @@ -496,9 +482,7 @@ class BIM_IfcQuantities: sel = self.form.quantities.selectedIndexes() for index in sel: if index.column() == 0: - obj = FreeCAD.ActiveDocument.getObject( - self.qmodel.itemFromIndex(index).toolTip() - ) + obj = FreeCAD.ActiveDocument.getObject(self.qmodel.itemFromIndex(index).toolTip()) if obj: FreeCADGui.Selection.addSelection(obj) diff --git a/src/Mod/BIM/bimcommands/BimImagePlane.py b/src/Mod/BIM/bimcommands/BimImagePlane.py index 44486ed9f3..446be6d8ee 100644 --- a/src/Mod/BIM/bimcommands/BimImagePlane.py +++ b/src/Mod/BIM/bimcommands/BimImagePlane.py @@ -36,9 +36,7 @@ class BIM_ImagePlane: return { "Pixmap": "BIM_ImagePlane.svg", "MenuText": QT_TRANSLATE_NOOP("BIM_ImagePlane", "Image Plane"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_ImagePlane", "Creates a plane from an image" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_ImagePlane", "Creates a plane from an image"), } def IsActive(self): @@ -108,9 +106,7 @@ class BIM_ImagePlane: FreeCAD.activeDraftCommand = None FreeCADGui.Snapper.off() self.tracker.off() - midpoint = self.basepoint.add( - self.opposite.sub(self.basepoint).multiply(0.5) - ) + midpoint = self.basepoint.add(self.opposite.sub(self.basepoint).multiply(0.5)) wp = WorkingPlane.get_working_plane() rotation = wp.get_placement().Rotation diagonal = self.opposite.sub(self.basepoint) diff --git a/src/Mod/BIM/bimcommands/BimLayers.py b/src/Mod/BIM/bimcommands/BimLayers.py index a0454c26de..20c8607407 100644 --- a/src/Mod/BIM/bimcommands/BimLayers.py +++ b/src/Mod/BIM/bimcommands/BimLayers.py @@ -34,7 +34,6 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") def getColorIcon(color): - "returns a QtGui.QIcon from a color 3-float tuple" from PySide import QtGui @@ -47,7 +46,6 @@ def getColorIcon(color): class BIM_Layers: - "The BIM_Layers FreeCAD command" def GetResources(self): @@ -70,9 +68,9 @@ class BIM_Layers: if getattr(self, "dialog", None): self.dialog.raise_() return - + from PySide import QtGui - + # store changes to be committed self.deleteList = [] @@ -105,9 +103,7 @@ class BIM_Layers: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.dialog.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.dialog.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.dialog.rect().center() ) # connect signals/slots @@ -176,15 +172,15 @@ class BIM_Layers: obj = Draft.make_layer(self.model.item(row, 1).text()) # By default BIM layers should not swallow their children otherwise # they will disappear from the tree root - obj.ViewObject.addProperty("App::PropertyBool", "HideChildren", "Layer", locked=True) + obj.ViewObject.addProperty( + "App::PropertyBool", "HideChildren", "Layer", locked=True + ) obj.ViewObject.HideChildren = True else: from nativeifc import ifc_tools import FreeCADGui - active = FreeCADGui.ActiveDocument.ActiveView.getActiveObject( - "NativeIFC" - ) + active = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") project = None if active: project = ifc_tools.get_project(active) @@ -208,15 +204,10 @@ class BIM_Layers: ) else: FreeCAD.Console.PrintError( - translate( - "BIM", "There is no IFC project in this document" - ) - + "\n" + translate("BIM", "There is no IFC project in this document") + "\n" ) if project: - obj = ifc_tools.create_layer( - self.model.item(row, 1).text(), project - ) + obj = ifc_tools.create_layer(self.model.item(row, 1).text(), project) vobj = obj.ViewObject # visibility @@ -331,11 +322,7 @@ class BIM_Layers: self.dialog.tree.setColumnWidth(1, 128) # name column # populate - objs = [ - obj - for obj in FreeCAD.ActiveDocument.Objects - if Draft.getType(obj) == "Layer" - ] + objs = [obj for obj in FreeCAD.ActiveDocument.Objects if Draft.getType(obj) == "Layer"] objs.sort(key=lambda o: o.Label) for obj in objs: self.addItem(obj) @@ -356,19 +343,13 @@ class BIM_Layers: ) styleItem = QtGui.QStandardItem("Solid") lineColorItem = QtGui.QStandardItem() - lineColorItem.setData( - self.getPref("DefaultShapeLineColor", 421075455), QtCore.Qt.UserRole - ) + lineColorItem.setData(self.getPref("DefaultShapeLineColor", 421075455), QtCore.Qt.UserRole) shapeColorItem = QtGui.QStandardItem() - shapeColorItem.setData( - self.getPref("DefaultShapeColor", 3435973887), QtCore.Qt.UserRole - ) + shapeColorItem.setData(self.getPref("DefaultShapeColor", 3435973887), QtCore.Qt.UserRole) transparencyItem = QtGui.QStandardItem() transparencyItem.setData(0, QtCore.Qt.DisplayRole) linePrintColorItem = QtGui.QStandardItem() - linePrintColorItem.setData( - self.getPref("DefaultPrintColor", 0), QtCore.Qt.UserRole - ) + linePrintColorItem.setData(self.getPref("DefaultPrintColor", 0), QtCore.Qt.UserRole) if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC"): nameItem.setIcon(self.ifcicon) @@ -387,14 +368,10 @@ class BIM_Layers: shapeColorItem.setData(obj.ViewObject.ShapeColor[:3], QtCore.Qt.UserRole) transparencyItem.setData(obj.ViewObject.Transparency, QtCore.Qt.DisplayRole) if hasattr(obj.ViewObject, "LinePrintColor"): - linePrintColorItem.setData( - obj.ViewObject.LinePrintColor[:3], QtCore.Qt.UserRole - ) + linePrintColorItem.setData(obj.ViewObject.LinePrintColor[:3], QtCore.Qt.UserRole) lineColorItem.setIcon(getColorIcon(lineColorItem.data(QtCore.Qt.UserRole))) shapeColorItem.setIcon(getColorIcon(shapeColorItem.data(QtCore.Qt.UserRole))) - linePrintColorItem.setIcon( - getColorIcon(linePrintColorItem.data(QtCore.Qt.UserRole)) - ) + linePrintColorItem.setIcon(getColorIcon(linePrintColorItem.data(QtCore.Qt.UserRole))) # append row self.model.appendRow( @@ -458,10 +435,7 @@ class BIM_Layers: if index.column() == 0: # get state from first selected row if state is None: - if ( - self.model.itemFromIndex(index).checkState() - == QtCore.Qt.Checked - ): + if self.model.itemFromIndex(index).checkState() == QtCore.Qt.Checked: state = QtCore.Qt.Unchecked else: state = QtCore.Qt.Checked @@ -561,9 +535,7 @@ if FreeCAD.GuiUp: elif index.column() == 2: # Line width editor.setValue(index.data()) elif index.column() == 3: # Line style - editor.setCurrentIndex( - ["Solid", "Dashed", "Dotted", "Dashdot"].index(index.data()) - ) + editor.setCurrentIndex(["Solid", "Dashed", "Dotted", "Dashdot"].index(index.data())) elif index.column() == 4: # Line color editor.setText(str(index.data(QtCore.Qt.UserRole))) if self.first: diff --git a/src/Mod/BIM/bimcommands/BimLeader.py b/src/Mod/BIM/bimcommands/BimLeader.py index 92a087061d..76a32bb754 100644 --- a/src/Mod/BIM/bimcommands/BimLeader.py +++ b/src/Mod/BIM/bimcommands/BimLeader.py @@ -32,6 +32,7 @@ from draftguitools import gui_lines # Line tool from Draft QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate + class BIM_Leader(gui_lines.Line): def __init__(self): @@ -48,13 +49,12 @@ class BIM_Leader(gui_lines.Line): } def Activated(self): - super().Activated( - name="Leader", icon="BIM_Leader", task_title=translate("BIM", "Leader") - ) + super().Activated(name="Leader", icon="BIM_Leader", task_title=translate("BIM", "Leader")) def finish(self, closed=False, cont=False): import DraftVecUtils from draftutils import params + self.end_callbacks(self.call) self.removeTemporaryObject() if len(self.node) > 1: @@ -79,4 +79,5 @@ class BIM_Leader(gui_lines.Line): if self.ui and self.ui.continueMode: self.Activated() + FreeCADGui.addCommand("BIM_Leader", BIM_Leader()) diff --git a/src/Mod/BIM/bimcommands/BimLibrary.py b/src/Mod/BIM/bimcommands/BimLibrary.py index 8ab9df20f8..9184175689 100644 --- a/src/Mod/BIM/bimcommands/BimLibrary.py +++ b/src/Mod/BIM/bimcommands/BimLibrary.py @@ -61,9 +61,7 @@ LIBRARYURL = "https://github.com/FreeCAD/FreeCAD-library/tree/master" RAWURL = LIBRARYURL.replace("/tree", "/raw") LIBINDEXFILE = "OfflineLibrary.py" USE_API = True # True to use github API instead of web fetching... Way faster -REFRESH_INTERVAL = ( - 3600 # Min seconds between allowing a new API calls (3600 = one hour) -) +REFRESH_INTERVAL = 3600 # Min seconds between allowing a new API calls (3600 = one hour) # TODO as https://github.com/yorikvanhavre/BIM_Workbench/pull/77 @@ -122,9 +120,9 @@ class BIM_Library_TaskPanel: self.linked = False - self.librarypath = FreeCAD.ParamGet( - "User parameter:Plugins/parts_library" - ).GetString("destination", "") + self.librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString( + "destination", "" + ) self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogLibrary.ui") self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_Library.svg")) @@ -172,14 +170,12 @@ class BIM_Library_TaskPanel: ], } for k, v in sites.items(): - self.form.comboSearch.addItem(QtGui.QIcon(":/icons/"+v[0]), k, v[1]) + self.form.comboSearch.addItem(QtGui.QIcon(":/icons/" + v[0]), k, v[1]) self.form.comboSearch.currentIndexChanged.connect(self.onExternalSearch) # retrieve preferences self.form.checkOnline.toggled.connect(self.onCheckOnline) - self.form.checkOnline.setChecked( - PARAMS.GetBool("LibraryOnline", not offlinemode) - ) + self.form.checkOnline.setChecked(PARAMS.GetBool("LibraryOnline", not offlinemode)) self.form.checkFCStdOnly.toggled.connect(self.onCheckFCStdOnly) self.form.checkFCStdOnly.setChecked(PARAMS.GetBool("LibraryFCStdOnly", False)) self.form.checkWebSearch.toggled.connect(self.onCheckWebSearch) @@ -309,7 +305,9 @@ class BIM_Library_TaskPanel: try: # check if the working document is saved if FreeCAD.getDocument(self.mainDocName).FileName == "": - FreeCAD.Console.PrintWarning(translate("BIM","Save the working file before linking.")+"\n") + FreeCAD.Console.PrintWarning( + translate("BIM", "Save the working file before linking.") + "\n" + ) else: self.previewOn = PARAMS.GetBool("3DPreview", False) self.linked = True @@ -347,7 +345,9 @@ class BIM_Library_TaskPanel: return self.linked except: FreeCAD.Console.PrintWarning( - translate("BIM","It is not possible to link because the main document is closed.")+"\n") + translate("BIM", "It is not possible to link because the main document is closed.") + + "\n" + ) def addtolibrary(self): # DISABLED @@ -356,10 +356,8 @@ class BIM_Library_TaskPanel: import Mesh import Part - self.fileDialog = QtGui.QFileDialog.getSaveFileName( - None, "Save As", self.librarypath - ) - #print(self.fileDialog[0]) + self.fileDialog = QtGui.QFileDialog.getSaveFileName(None, "Save As", self.librarypath) + # print(self.fileDialog[0]) # check if file saving has been canceled and save .fcstd, .step and .stl copies if self.fileDialog[0] != "": # remove the file extension from the file path @@ -462,9 +460,7 @@ class BIM_Library_TaskPanel: it.setToolTip(path + "/" + k) if isinstance(v, dict): it.setIcon( - QtGui.QIcon.fromTheme( - "folder", QtGui.QIcon(":/icons/Group.svg") - ) + QtGui.QIcon.fromTheme("folder", QtGui.QIcon(":/icons/Group.svg")) ) addItems(it, v, path + "/" + k) it.setToolTip("") @@ -474,6 +470,7 @@ class BIM_Library_TaskPanel: it.setIcon(QtGui.QIcon(":/icons/IFC.svg")) else: it.setIcon(QtGui.QIcon(":/icons/Part_document.svg")) + self.form.tree.setModel(self.filemodel) self.filemodel.clear() d = self.getOfflineLib() @@ -518,7 +515,7 @@ class BIM_Library_TaskPanel: def urlencode(self, text): - #print(text, type(text)) + # print(text, type(text)) if sys.version_info.major < 3: import urllib @@ -619,9 +616,7 @@ class BIM_Library_TaskPanel: + "\n" ) else: - path = CadExchangerIO.insert( - path, FreeCAD.ActiveDocument.Name, returnpath=True - ) + path = CadExchangerIO.insert(path, FreeCAD.ActiveDocument.Name, returnpath=True) self.place(path) else: path = importerIL.insert(path, FreeCAD.ActiveDocument.Name) @@ -687,9 +682,7 @@ class BIM_Library_TaskPanel: w = QtGui.QWidget() w.setWindowTitle(translate("BIM", "Insertion point")) w.setWindowIcon( - QtGui.QIcon( - os.path.join(os.path.dirname(__file__), "icons", "BIM_Library.svg") - ) + QtGui.QIcon(os.path.join(os.path.dirname(__file__), "icons", "BIM_Library.svg")) ) l = QtGui.QVBoxLayout() w.setLayout(l) @@ -737,17 +730,13 @@ class BIM_Library_TaskPanel: def getDelta(self): - d = FreeCAD.Vector( - -self.shape.BoundBox.Center.x, -self.shape.BoundBox.Center.y, 0 - ) + d = FreeCAD.Vector(-self.shape.BoundBox.Center.x, -self.shape.BoundBox.Center.y, 0) idx = self.origin.comboOrigin.currentIndex() if idx <= 0: return FreeCAD.Vector() elif idx == 1: return d.add( - FreeCAD.Vector( - self.shape.BoundBox.XLength / 2, -self.shape.BoundBox.YLength / 2, 0 - ) + FreeCAD.Vector(self.shape.BoundBox.XLength / 2, -self.shape.BoundBox.YLength / 2, 0) ) elif idx == 2: return d.add(FreeCAD.Vector(0, -self.shape.BoundBox.YLength / 2, 0)) @@ -767,17 +756,13 @@ class BIM_Library_TaskPanel: return d.add(FreeCAD.Vector(-self.shape.BoundBox.XLength / 2, 0, 0)) elif idx == 7: return d.add( - FreeCAD.Vector( - self.shape.BoundBox.XLength / 2, self.shape.BoundBox.YLength / 2, 0 - ) + FreeCAD.Vector(self.shape.BoundBox.XLength / 2, self.shape.BoundBox.YLength / 2, 0) ) elif idx == 8: return d.add(FreeCAD.Vector(0, self.shape.BoundBox.YLength / 2, 0)) elif idx == 9: return d.add( - FreeCAD.Vector( - -self.shape.BoundBox.XLength / 2, self.shape.BoundBox.YLength / 2, 0 - ) + FreeCAD.Vector(-self.shape.BoundBox.XLength / 2, self.shape.BoundBox.YLength / 2, 0) ) def getOnlineContentsAPI(self, url): @@ -821,9 +806,7 @@ class BIM_Library_TaskPanel: host[name] = name count += 1 else: - FreeCAD.Console.PrintError( - translate("BIM", "Could not fetch library contents") + "\n" - ) + FreeCAD.Console.PrintError(translate("BIM", "Could not fetch library contents") + "\n") # print("result:",result) if not result: FreeCAD.Console.PrintError( @@ -1042,9 +1025,7 @@ if FreeCAD.GuiUp: if index.data().lower().endswith(".fcstd"): return QtGui.QIcon(":icons/freecad-doc.png") elif index.data().lower().endswith(".ifc"): - return QtGui.QIcon( - os.path.join(os.path.dirname(__file__), "icons", "IFC.svg") - ) + return QtGui.QIcon(os.path.join(os.path.dirname(__file__), "icons", "IFC.svg")) elif index.data().lower() == "private": return QtGui.QIcon.fromTheme("folder-lock") return super(LibraryModel, self).data(index, role) diff --git a/src/Mod/BIM/bimcommands/BimMaterial.py b/src/Mod/BIM/bimcommands/BimMaterial.py index 3fb96ecce9..f5b64f497e 100644 --- a/src/Mod/BIM/bimcommands/BimMaterial.py +++ b/src/Mod/BIM/bimcommands/BimMaterial.py @@ -88,9 +88,7 @@ class BIM_Material: self.dlg.setWindowTitle(translate("BIM", "Select material")) self.dlg.setWindowIcon(QtGui.QIcon(":/icons/Arch_Material.svg")) mw = FreeCADGui.getMainWindow() - self.dlg.move( - mw.frameGeometry().topLeft() + mw.rect().center() - self.dlg.rect().center() - ) + self.dlg.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.dlg.rect().center()) lay = QtGui.QVBoxLayout(self.dlg) matList = QtGui.QListWidget(self.dlg) matList.setSortingEnabled(True) @@ -134,26 +132,20 @@ class BIM_Material: createButtonsLayout = QtGui.QGridLayout() # create - buttonCreate = QtGui.QPushButton( - translate("BIM", "New Material"), self.dlg - ) + buttonCreate = QtGui.QPushButton(translate("BIM", "New Material"), self.dlg) buttonCreate.setIcon(QtGui.QIcon(":/icons/Arch_Material.svg")) createButtonsLayout.addWidget(buttonCreate, 0, 0) buttonCreate.clicked.connect(self.onCreate) # create multi - buttonMulti = QtGui.QPushButton( - translate("BIM", "Create new Multi-Material"), self.dlg - ) + buttonMulti = QtGui.QPushButton(translate("BIM", "Create new Multi-Material"), self.dlg) buttonMulti.setIcon(QtGui.QIcon(":/icons/Arch_Material_Multi.svg")) createButtonsLayout.addWidget(buttonMulti, 0, 1) buttonMulti.clicked.connect(self.onMulti) # merge dupes opsLayout = QtGui.QHBoxLayout() - buttonMergeDupes = QtGui.QPushButton( - translate("BIM", "Merge Duplicates"), self.dlg - ) + buttonMergeDupes = QtGui.QPushButton(translate("BIM", "Merge Duplicates"), self.dlg) buttonMergeDupes.setIcon(QtGui.QIcon(":/icons/view-refresh.svg")) createButtonsLayout.addWidget(buttonMergeDupes, 1, 0) self.dlg.buttonMergeDupes = buttonMergeDupes @@ -162,9 +154,7 @@ class BIM_Material: buttonMergeDupes.setEnabled(False) # delete unused - buttonDeleteUnused = QtGui.QPushButton( - translate("BIM", "Delete Unused"), self.dlg - ) + buttonDeleteUnused = QtGui.QPushButton(translate("BIM", "Delete Unused"), self.dlg) buttonDeleteUnused.setIcon(QtGui.QIcon(":/icons/delete.svg")) createButtonsLayout.addWidget(buttonDeleteUnused, 1, 1) buttonDeleteUnused.clicked.connect(self.onDeleteUnused) @@ -175,9 +165,7 @@ class BIM_Material: # add standard buttons buttonBox = QtGui.QDialogButtonBox(self.dlg) buttonBox.setOrientation(QtCore.Qt.Horizontal) - buttonBox.setStandardButtons( - QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok - ) + buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) lay.addWidget(buttonBox) buttonBox.accepted.connect(self.onAccept) buttonBox.rejected.connect(self.onReject) @@ -218,11 +206,7 @@ class BIM_Material: first = True for mat in self.dlg.materials: orig = None - if ( - mat.Label[-1].isdigit() - and mat.Label[-2].isdigit() - and mat.Label[-3].isdigit() - ): + if mat.Label[-1].isdigit() and mat.Label[-2].isdigit() and mat.Label[-3].isdigit(): for om in self.dlg.materials: if om.Label == mat.Label[:-3].strip(): orig = om @@ -243,19 +227,14 @@ class BIM_Material: + "\n" ) if first: - FreeCAD.ActiveDocument.openTransaction( - "Merge materials" - ) + FreeCAD.ActiveDocument.openTransaction("Merge materials") first = False setattr(par, prop, orig) todelete.append(mat) for tod in todelete: if not tod.InList: FreeCAD.Console.PrintMessage( - translate("BIM", "Merging duplicate material") - + " " - + tod.Label - + "\n" + translate("BIM", "Merging duplicate material") + " " + tod.Label + "\n" ) if first: FreeCAD.ActiveDocument.openTransaction("Merge materials") @@ -265,10 +244,7 @@ class BIM_Material: tod.InList[0].isDerivedFrom("App::DocumentObjectGroup") ): FreeCAD.Console.PrintMessage( - translate("BIM", "Merging duplicate material") - + " " - + tod.Label - + "\n" + translate("BIM", "Merging duplicate material") + " " + tod.Label + "\n" ) if first: FreeCAD.ActiveDocument.openTransaction("Merge materials") @@ -305,15 +281,10 @@ class BIM_Material: name = obj.Name label = obj.Label if first: - FreeCAD.ActiveDocument.openTransaction( - "Delete materials" - ) + FreeCAD.ActiveDocument.openTransaction("Delete materials") first = False FreeCAD.Console.PrintMessage( - translate("BIM", "Deleting unused material") - + " " - + label - + "\n" + translate("BIM", "Deleting unused material") + " " + label + "\n" ) FreeCAD.ActiveDocument.removeObject(name) if not first: @@ -362,19 +333,13 @@ class BIM_Material: form = FreeCADGui.PySideUic.loadUi(":/ui/dialogListWidget.ui") # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() - form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - form.rect().center() - ) + form.move(mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center()) form.setWindowTitle(translate("BIM", "Select material to merge to")) form.setWindowIcon(QtGui.QIcon(":/icons/Arch_Material.svg")) for i in range(self.dlg.matList.count()): oit = self.dlg.matList.item(i) if oit != item: - nit = QtGui.QListWidgetItem( - oit.icon(), oit.text(), form.listWidget - ) + nit = QtGui.QListWidgetItem(oit.icon(), oit.text(), form.listWidget) nit.setToolTip(oit.toolTip()) result = form.exec_() if result: @@ -385,10 +350,7 @@ class BIM_Material: parents = [ parent for parent in oldmat.InList - if ( - hasattr(parent, "Material") - and (parent.Material == oldmat) - ) + if (hasattr(parent, "Material") and (parent.Material == oldmat)) ] name = oldmat.Name FreeCAD.ActiveDocument.openTransaction("Merge material") @@ -448,6 +410,7 @@ class BIM_Material: for obj in self.dlg.objects: if hasattr(obj, "StepId"): from nativeifc import ifc_materials + ifc_materials.set_material(mat, obj) else: obj.Material = mat @@ -457,6 +420,7 @@ class BIM_Material: p.SetInt("BimMaterialDialogWidth", self.dlg.width()) p.SetInt("BimMaterialDialogHeight", self.dlg.height()) from DraftGui import todo + # delay required for matList.itemDoubleClicked action todo.delay(self.onReject, None) @@ -519,9 +483,7 @@ class BIM_Material: name = "None" self.dlg.matList.clear() for o in self.dlg.materials: - i = QtGui.QListWidgetItem( - self.createIcon(o), o.Label, self.dlg.matList - ) + i = QtGui.QListWidgetItem(self.createIcon(o), o.Label, self.dlg.matList) i.setToolTip(o.Name) i.setFlags(i.flags() | QtCore.Qt.ItemIsEditable) if o.Name == name: @@ -544,18 +506,14 @@ class BIM_Material: im = QtGui.QImage(48, 48, QtGui.QImage.Format_ARGB32) im.fill(QtCore.Qt.transparent) pt = QtGui.QPainter(im) - pt.setPen( - QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap) - ) + pt.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) # pt.setBrush(QtGui.QBrush(matcolor, QtCore.Qt.SolidPattern)) gradient = QtGui.QLinearGradient(0, 0, 48, 48) gradient.setColorAt(0, matcolor) gradient.setColorAt(1, darkcolor) pt.setBrush(QtGui.QBrush(gradient)) pt.drawEllipse(6, 6, 36, 36) - pt.setPen( - QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap) - ) + pt.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) pt.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern)) pt.drawEllipse(12, 12, 12, 12) pt.end() @@ -566,27 +524,31 @@ class BIM_Material: class Arch_Material: - - "the Arch Material command definition" def GetResources(self): - return {'Pixmap': 'Arch_Material_Group', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Material","Material"), - 'Accel': "M, T", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Material","Creates or edits the material definition of a selected object.")} + return { + "Pixmap": "Arch_Material_Group", + "MenuText": QT_TRANSLATE_NOOP("Arch_Material", "Material"), + "Accel": "M, T", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Material", "Creates or edits the material definition of a selected object." + ), + } def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create material")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create material")) FreeCADGui.addModule("Arch") FreeCADGui.Control.closeDialog() FreeCADGui.doCommand("mat = Arch.makeMaterial()") for obj in sel: - if hasattr(obj,"Material"): - FreeCADGui.doCommand("FreeCAD.ActiveDocument.getObject(\""+obj.Name+"\").Material = mat") + if hasattr(obj, "Material"): + FreeCADGui.doCommand( + 'FreeCAD.ActiveDocument.getObject("' + obj.Name + '").Material = mat' + ) FreeCADGui.doCommandGui("mat.ViewObject.Document.setEdit(mat.ViewObject, 0)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -597,28 +559,28 @@ class Arch_Material: class Arch_MultiMaterial: - - "the Arch MultiMaterial command definition" def GetResources(self): - return {'Pixmap': 'Arch_Material_Multi', - 'MenuText': QT_TRANSLATE_NOOP("Arch_MultiMaterial","Multi-Material"), - 'Accel': "M, T", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_MultiMaterial","Creates or edits multi-materials")} + return { + "Pixmap": "Arch_Material_Multi", + "MenuText": QT_TRANSLATE_NOOP("Arch_MultiMaterial", "Multi-Material"), + "Accel": "M, T", + "ToolTip": QT_TRANSLATE_NOOP("Arch_MultiMaterial", "Creates or edits multi-materials"), + } def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create multi-material")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create multi-material")) FreeCADGui.addModule("Arch") FreeCADGui.Control.closeDialog() FreeCADGui.doCommand("mat = Arch.makeMultiMaterial()") for obj in sel: - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if not obj.isDerivedFrom("App::MaterialObject"): - FreeCADGui.doCommand("FreeCAD.ActiveDocument."+obj.Name+".Material = mat") + FreeCADGui.doCommand("FreeCAD.ActiveDocument." + obj.Name + ".Material = mat") FreeCADGui.doCommandGui("mat.ViewObject.Document.setEdit(mat.ViewObject, 0)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -631,17 +593,20 @@ class Arch_MultiMaterial: class Arch_MaterialToolsCommand: def GetCommands(self): - return tuple(['Arch_Material','Arch_MultiMaterial']) + return tuple(["Arch_Material", "Arch_MultiMaterial"]) + def GetResources(self): - return { 'MenuText': QT_TRANSLATE_NOOP("Arch_MaterialTools",'Material Tools'), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_MaterialTools",'Material tools') - } + return { + "MenuText": QT_TRANSLATE_NOOP("Arch_MaterialTools", "Material Tools"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_MaterialTools", "Material tools"), + } + def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v FreeCADGui.addCommand("BIM_Material", BIM_Material()) -FreeCADGui.addCommand('Arch_Material',Arch_Material()) -FreeCADGui.addCommand('Arch_MultiMaterial',Arch_MultiMaterial()) -FreeCADGui.addCommand('Arch_MaterialTools', Arch_MaterialToolsCommand()) +FreeCADGui.addCommand("Arch_Material", Arch_Material()) +FreeCADGui.addCommand("Arch_MultiMaterial", Arch_MultiMaterial()) +FreeCADGui.addCommand("Arch_MaterialTools", Arch_MaterialToolsCommand()) diff --git a/src/Mod/BIM/bimcommands/BimMoveView.py b/src/Mod/BIM/bimcommands/BimMoveView.py index 1c64ac763b..0da7169e9b 100644 --- a/src/Mod/BIM/bimcommands/BimMoveView.py +++ b/src/Mod/BIM/bimcommands/BimMoveView.py @@ -36,9 +36,7 @@ class BIM_MoveView: return { "Pixmap": "BIM_MoveView", "MenuText": QT_TRANSLATE_NOOP("BIM_MoveView", "Move View"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_MoveView", "Moves this view to an existing page" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_MoveView", "Moves this view to an existing page"), } def Activated(self): diff --git a/src/Mod/BIM/bimcommands/BimNudge.py b/src/Mod/BIM/bimcommands/BimNudge.py index afd5c677eb..079a17a56b 100644 --- a/src/Mod/BIM/bimcommands/BimNudge.py +++ b/src/Mod/BIM/bimcommands/BimNudge.py @@ -48,9 +48,9 @@ class BIM_Nudge: nudgeValue = statuswidget.nudge.text().replace("&", "") dist = 0 if "auto" in nudgeValue.lower(): - unit = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Units" - ).GetInt("UserSchema", 0) + unit = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( + "UserSchema", 0 + ) if unit in [2, 3, 5, 7]: scale = [1.5875, 3.175, 6.35, 25.4, 152.4, 304.8] else: @@ -102,9 +102,7 @@ class BIM_Nudge: def toStr(self, objs): "builds a string which is a list of objects" - return ( - "[" + ",".join(["FreeCAD.ActiveDocument." + obj.Name for obj in objs]) + "]" - ) + return "[" + ",".join(["FreeCAD.ActiveDocument." + obj.Name for obj in objs]) + "]" def getCenter(self, objs): "returns the center point of a group of objects" @@ -309,9 +307,7 @@ class BIM_Nudge_RotateRight(BIM_Nudge): def GetResources(self): return { - "MenuText": QT_TRANSLATE_NOOP( - "BIM_Nudge_RotateRight", "Nudge Rotate Right" - ), + "MenuText": QT_TRANSLATE_NOOP("BIM_Nudge_RotateRight", "Nudge Rotate Right"), "Accel": "Ctrl+.", } diff --git a/src/Mod/BIM/bimcommands/BimOffset.py b/src/Mod/BIM/bimcommands/BimOffset.py index 975ff2c0e5..c356f48d5b 100644 --- a/src/Mod/BIM/bimcommands/BimOffset.py +++ b/src/Mod/BIM/bimcommands/BimOffset.py @@ -33,11 +33,10 @@ QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP class BIM_Offset2D: def GetResources(self): - return {"Pixmap":"Part_Offset2D", - "MenuText": QT_TRANSLATE_NOOP("Part_Offset2D", "2D Offset"), - "ToolTip": QT_TRANSLATE_NOOP( - "Part_Offset2D", "Utility to offset planar shapes" - ), + return { + "Pixmap": "Part_Offset2D", + "MenuText": QT_TRANSLATE_NOOP("Part_Offset2D", "2D Offset"), + "ToolTip": QT_TRANSLATE_NOOP("Part_Offset2D", "Utility to offset planar shapes"), } def IsActive(self): diff --git a/src/Mod/BIM/bimcommands/BimPanel.py b/src/Mod/BIM/bimcommands/BimPanel.py index 66d3dee306..210d91af00 100644 --- a/src/Mod/BIM/bimcommands/BimPanel.py +++ b/src/Mod/BIM/bimcommands/BimPanel.py @@ -33,27 +33,32 @@ translate = FreeCAD.Qt.translate # Description l w t -Presets = [None, - ["Plywood 12mm, 1220 x 2440",1220,2440,12], - ["Plywood 15mm, 1220 x 2440",1220,2440,15], - ["Plywood 18mm, 1220 x 2440",1220,2440,18], - ["Plywood 25mm, 1220 x 2440",1220,2440,25], - ["MDF 3mm, 900 x 600", 900, 600, 3], - ["MDF 6mm, 900 x 600", 900, 600, 6], - ["OSB 18mm, 1220 x 2440", 1220,2440,18], - ] +Presets = [ + None, + ["Plywood 12mm, 1220 x 2440", 1220, 2440, 12], + ["Plywood 15mm, 1220 x 2440", 1220, 2440, 15], + ["Plywood 18mm, 1220 x 2440", 1220, 2440, 18], + ["Plywood 25mm, 1220 x 2440", 1220, 2440, 25], + ["MDF 3mm, 900 x 600", 900, 600, 3], + ["MDF 6mm, 900 x 600", 900, 600, 6], + ["OSB 18mm, 1220 x 2440", 1220, 2440, 18], +] class Arch_Panel: - "the Arch Panel Arch_ definition" def GetResources(self): - return {'Pixmap' : 'Arch_Panel', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel","Panel"), - 'Accel': "P, A", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel","Creates a panel object from scratch or from a selected object (sketch, wire, face or solid)")} + return { + "Pixmap": "Arch_Panel", + "MenuText": QT_TRANSLATE_NOOP("Arch_Panel", "Panel"), + "Accel": "P, A", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Panel", + "Creates a panel object from scratch or from a selected object (sketch, wire, face or solid)", + ), + } def IsActive(self): @@ -79,11 +84,17 @@ class Arch_Panel: if len(sel) == 1: if Draft.getType(sel[0]) == "Panel": return - self.doc.openTransaction(str(translate("Arch","Create Panel"))) + self.doc.openTransaction(str(translate("Arch", "Create Panel"))) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") for obj in sel: - FreeCADGui.doCommand("obj = Arch.makePanel(FreeCAD.ActiveDocument." + obj.Name + ",thickness=" + str(self.Thickness) + ")") + FreeCADGui.doCommand( + "obj = Arch.makePanel(FreeCAD.ActiveDocument." + + obj.Name + + ",thickness=" + + str(self.Thickness) + + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() self.doc.recompute() @@ -99,11 +110,12 @@ class Arch_Panel: self.tracker.height(self.Thickness) self.tracker.length(self.Length) self.tracker.on() - FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox()) + FreeCADGui.Snapper.getPoint( + callback=self.getPoint, movecallback=self.update, extradlg=self.taskbox() + ) FreeCADGui.draftToolBar.continueCmd.show() - def getPoint(self,point=None,obj=None): - + def getPoint(self, point=None, obj=None): "this function is called by the snapper when it has a 3D point" import DraftVecUtils @@ -113,76 +125,97 @@ class Arch_Panel: self.tracker.finalize() if point is None: return - self.doc.openTransaction(translate("Arch","Create Panel")) + self.doc.openTransaction(translate("Arch", "Create Panel")) FreeCADGui.addModule("Arch") if self.Profile: pr = Presets[self.Profile] - FreeCADGui.doCommand('p = Arch.makeProfile('+str(pr[2])+','+str(pr[3])+','+str(pr[4])+','+str(pr[5])+')') - FreeCADGui.doCommand('s = Arch.makePanel(p,thickness='+str(self.Thickness)+')') - #FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)') + FreeCADGui.doCommand( + "p = Arch.makeProfile(" + + str(pr[2]) + + "," + + str(pr[3]) + + "," + + str(pr[4]) + + "," + + str(pr[5]) + + ")" + ) + FreeCADGui.doCommand("s = Arch.makePanel(p,thickness=" + str(self.Thickness) + ")") + # FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)') else: - FreeCADGui.doCommand('s = Arch.makePanel(length='+str(self.Length)+',width='+str(self.Width)+',thickness='+str(self.Thickness)+')') - FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point)) + FreeCADGui.doCommand( + "s = Arch.makePanel(length=" + + str(self.Length) + + ",width=" + + str(self.Width) + + ",thickness=" + + str(self.Thickness) + + ")" + ) + FreeCADGui.doCommand("s.Placement.Base = " + DraftVecUtils.toString(point)) if self.rotated: - FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)') + FreeCADGui.doCommand( + "s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)" + ) self.doc.commitTransaction() self.doc.recompute() from PySide import QtCore + QtCore.QTimer.singleShot(100, self.check_continueMode) - def check_continueMode(self): - "checks if continueMode is true and restarts Panel" if FreeCADGui.draftToolBar.continueMode: self.Activated() def taskbox(self): - "sets up a taskbox widget" from draftutils import params from PySide import QtCore, QtGui + w = QtGui.QWidget() ui = FreeCADGui.UiLoader() - w.setWindowTitle(translate("Arch","Panel options")) + w.setWindowTitle(translate("Arch", "Panel options")) grid = QtGui.QGridLayout(w) # presets box - labelp = QtGui.QLabel(translate("Arch","Preset")) + labelp = QtGui.QLabel(translate("Arch", "Preset")) valuep = QtGui.QComboBox() fpresets = [" "] for p in Presets[1:]: - fpresets.append(translate("Arch",p[0])) + fpresets.append(translate("Arch", p[0])) valuep.addItems(fpresets) - grid.addWidget(labelp,0,0,1,1) - grid.addWidget(valuep,0,1,1,1) + grid.addWidget(labelp, 0, 0, 1, 1) + grid.addWidget(valuep, 0, 1, 1, 1) # length - label1 = QtGui.QLabel(translate("Arch","Length")) + label1 = QtGui.QLabel(translate("Arch", "Length")) self.vLength = ui.createWidget("Gui::InputField") - self.vLength.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString) - grid.addWidget(label1,1,0,1,1) - grid.addWidget(self.vLength,1,1,1,1) + self.vLength.setText(FreeCAD.Units.Quantity(self.Length, FreeCAD.Units.Length).UserString) + grid.addWidget(label1, 1, 0, 1, 1) + grid.addWidget(self.vLength, 1, 1, 1, 1) # width - label2 = QtGui.QLabel(translate("Arch","Width")) + 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,2,0,1,1) - grid.addWidget(self.vWidth,2,1,1,1) + self.vWidth.setText(FreeCAD.Units.Quantity(self.Width, FreeCAD.Units.Length).UserString) + grid.addWidget(label2, 2, 0, 1, 1) + grid.addWidget(self.vWidth, 2, 1, 1, 1) # height - label3 = QtGui.QLabel(translate("Arch","Thickness")) + label3 = QtGui.QLabel(translate("Arch", "Thickness")) self.vHeight = ui.createWidget("Gui::InputField") - self.vHeight.setText(FreeCAD.Units.Quantity(self.Thickness,FreeCAD.Units.Length).UserString) - grid.addWidget(label3,3,0,1,1) - grid.addWidget(self.vHeight,3,1,1,1) + self.vHeight.setText( + FreeCAD.Units.Quantity(self.Thickness, FreeCAD.Units.Length).UserString + ) + grid.addWidget(label3, 3, 0, 1, 1) + grid.addWidget(self.vHeight, 3, 1, 1, 1) # horizontal button - value4= QtGui.QPushButton(translate("Arch","Rotate")) - grid.addWidget(value4,4,0,1,2) + value4 = QtGui.QPushButton(translate("Arch", "Rotate")) + grid.addWidget(value4, 4, 0, 1, 2) valuep.currentIndexChanged.connect(self.setPreset) self.vLength.valueChanged.connect(self.setLength) @@ -191,8 +224,7 @@ class Arch_Panel: value4.pressed.connect(self.rotate) return w - def update(self,point,info): - + def update(self, point, info): "this function is called by the Snapper when the mouse is moved" if FreeCADGui.Control.activeDialog(): @@ -206,30 +238,39 @@ class Arch_Panel: self.tracker.height(self.Thickness) self.tracker.length(self.Length) - def setWidth(self,d): + def setWidth(self, d): from draftutils import params + self.Width = d.Value - params.set_param_arch("PanelWidth",d) + params.set_param_arch("PanelWidth", d) - def setThickness(self,d): + def setThickness(self, d): from draftutils import params + self.Thickness = d.Value - params.set_param_arch("PanelThickness",d) + params.set_param_arch("PanelThickness", d) - def setLength(self,d): + def setLength(self, d): from draftutils import params - self.Length = d.Value - params.set_param_arch("PanelLength",d) - def setPreset(self,i): + self.Length = d.Value + params.set_param_arch("PanelLength", d) + + def setPreset(self, i): if i > 0: - self.vLength.setText(FreeCAD.Units.Quantity(float(Presets[i][1]),FreeCAD.Units.Length).UserString) - self.vWidth.setText(FreeCAD.Units.Quantity(float(Presets[i][2]),FreeCAD.Units.Length).UserString) - self.vHeight.setText(FreeCAD.Units.Quantity(float(Presets[i][3]),FreeCAD.Units.Length).UserString) + self.vLength.setText( + FreeCAD.Units.Quantity(float(Presets[i][1]), FreeCAD.Units.Length).UserString + ) + self.vWidth.setText( + FreeCAD.Units.Quantity(float(Presets[i][2]), FreeCAD.Units.Length).UserString + ) + self.vHeight.setText( + FreeCAD.Units.Quantity(float(Presets[i][3]), FreeCAD.Units.Length).UserString + ) def rotate(self): @@ -237,15 +278,16 @@ class Arch_Panel: class Arch_PanelCut: - "the Arch Panel Cut command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Panel_Cut', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Panel Cut"), - 'Accel': "P, C", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Creates 2D views of selected panels")} + return { + "Pixmap": "Arch_Panel_Cut", + "MenuText": QT_TRANSLATE_NOOP("Arch_Panel_Cut", "Panel Cut"), + "Accel": "P, C", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Panel_Cut", "Creates 2D views of selected panels"), + } def IsActive(self): @@ -255,26 +297,32 @@ class Arch_PanelCut: def Activated(self): import Draft + if FreeCADGui.Selection.getSelection(): - FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Cut"))) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch", "Create Panel Cut"))) FreeCADGui.addModule("Arch") for obj in FreeCADGui.Selection.getSelection(): if Draft.getType(obj) == "Panel": - FreeCADGui.doCommand("Arch.makePanelCut(FreeCAD.ActiveDocument."+obj.Name+")") + FreeCADGui.doCommand( + "Arch.makePanelCut(FreeCAD.ActiveDocument." + obj.Name + ")" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class Arch_PanelSheet: - "the Arch Panel Sheet definition" def GetResources(self): - return {'Pixmap' : 'Arch_Panel_Sheet', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Panel Sheet"), - 'Accel': "P, S", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates a 2D sheet which can contain panel cuts")} + return { + "Pixmap": "Arch_Panel_Sheet", + "MenuText": QT_TRANSLATE_NOOP("Arch_Panel_Sheet", "Panel Sheet"), + "Accel": "P, S", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Panel_Sheet", "Creates a 2D sheet which can contain panel cuts" + ), + } def IsActive(self): @@ -283,16 +331,16 @@ class Arch_PanelSheet: def Activated(self): - FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Sheet"))) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch", "Create Panel Sheet"))) FreeCADGui.addModule("Arch") if FreeCADGui.Selection.getSelection(): l = "[" for obj in FreeCADGui.Selection.getSelection(): - l += "FreeCAD.ActiveDocument."+obj.Name+"," + l += "FreeCAD.ActiveDocument." + obj.Name + "," l += "]" FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - FreeCADGui.doCommand("__objs__ = "+l) + FreeCADGui.doCommand("__objs__ = " + l) FreeCADGui.doCommand("Arch.makePanelSheet(__objs__)") FreeCADGui.doCommand("del __objs__") else: @@ -302,15 +350,18 @@ class Arch_PanelSheet: class Arch_Nest: - "the Arch Panel definition" def GetResources(self): - return {'Pixmap' : 'Arch_Nest', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Nest","Nest"), - 'Accel': "N, E", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Nest","Nests a series of selected shapes in a container")} + return { + "Pixmap": "Arch_Nest", + "MenuText": QT_TRANSLATE_NOOP("Arch_Nest", "Nest"), + "Accel": "N, E", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Nest", "Nests a series of selected shapes in a container" + ), + } def IsActive(self): @@ -324,13 +375,12 @@ class Arch_Nest: class NestTaskPanel: + """The TaskPanel for Arch Nest""" - - '''The TaskPanel for Arch Nest''' - - def __init__(self,obj=None): + def __init__(self, obj=None): import ArchNesting + self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchNest.ui") self.form.progressBar.hide() self.form.ButtonPreview.setEnabled(False) @@ -372,18 +422,19 @@ class NestTaskPanel: def getContainer(self): import Draft + s = FreeCADGui.Selection.getSelection() if len(s) == 1: - if hasattr(s[0],'Shape'): + if hasattr(s[0], "Shape"): if len(s[0].Shape.Faces) == 1: if not (s[0] in self.shapes): self.form.Container.clear() - self.addObject(s[0],self.form.Container) + self.addObject(s[0], self.form.Container) self.container = s[0] else: - FreeCAD.Console.PrintError(translate("Arch","This object has no face")) + FreeCAD.Console.PrintError(translate("Arch", "This object has no face")) if Draft.getType(s[0]) == "PanelSheet": - if hasattr(s[0],"Rotations"): + if hasattr(s[0], "Rotations"): if s[0].Rotations: self.form.Rotations.setText(str(s[0].Rotations)) @@ -391,20 +442,21 @@ class NestTaskPanel: s = FreeCADGui.Selection.getSelection() for o in s: - if hasattr(o,'Shape'): + if hasattr(o, "Shape"): if not o in self.shapes: if o != self.container: - self.addObject(o,self.form.Shapes) + self.addObject(o, self.form.Shapes) self.shapes.append(o) - def addObject(self,obj,form): + def addObject(self, obj, form): from PySide import QtGui + i = QtGui.QListWidgetItem() i.setText(obj.Label) i.setToolTip(obj.Name) - if hasattr(obj.ViewObject,"Proxy"): - if hasattr(obj.ViewObject.Proxy,"getIcon"): + if hasattr(obj.ViewObject, "Proxy"): + if hasattr(obj.ViewObject.Proxy, "getIcon"): i.setIcon(QtGui.QIcon(obj.ViewObject.Proxy.getIcon())) elif hasattr(obj.ViewObject, "Icon"): i.setIcon(QtGui.QIcon(obj.ViewObject.Icon)) @@ -421,7 +473,7 @@ class NestTaskPanel: self.shapes.remove(o) self.form.Shapes.takeItem(self.form.Shapes.row(i)) - def setCounter(self,value): + def setCounter(self, value): self.form.progressBar.setValue(value) @@ -429,6 +481,7 @@ class NestTaskPanel: from PySide import QtGui import ArchNesting + self.clearTemps() self.form.progressBar.setFormat("pass 1: %p%") self.form.progressBar.setValue(0) @@ -475,20 +528,21 @@ class NestTaskPanel: class Arch_PanelGroup: def GetCommands(self): - return tuple(['Arch_Panel','Arch_Panel_Cut','Arch_Panel_Sheet','Arch_Nest']) + return tuple(["Arch_Panel", "Arch_Panel_Cut", "Arch_Panel_Sheet", "Arch_Nest"]) def GetResources(self): - return { 'MenuText': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel Tools'), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel tools') - } + return { + "MenuText": QT_TRANSLATE_NOOP("Arch_PanelTools", "Panel Tools"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_PanelTools", "Panel tools"), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v -FreeCADGui.addCommand('Arch_Panel',Arch_Panel()) -FreeCADGui.addCommand('Arch_Panel_Cut',Arch_PanelCut()) -FreeCADGui.addCommand('Arch_Panel_Sheet',Arch_PanelSheet()) -FreeCADGui.addCommand('Arch_Nest',Arch_Nest()) -FreeCADGui.addCommand('Arch_PanelTools', Arch_PanelGroup()) +FreeCADGui.addCommand("Arch_Panel", Arch_Panel()) +FreeCADGui.addCommand("Arch_Panel_Cut", Arch_PanelCut()) +FreeCADGui.addCommand("Arch_Panel_Sheet", Arch_PanelSheet()) +FreeCADGui.addCommand("Arch_Nest", Arch_Nest()) +FreeCADGui.addCommand("Arch_PanelTools", Arch_PanelGroup()) diff --git a/src/Mod/BIM/bimcommands/BimPipe.py b/src/Mod/BIM/bimcommands/BimPipe.py index 69d37cc682..22b22c8abc 100644 --- a/src/Mod/BIM/bimcommands/BimPipe.py +++ b/src/Mod/BIM/bimcommands/BimPipe.py @@ -34,15 +34,18 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Pipe: - "the Arch Pipe command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Pipe', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Pipe","Pipe"), - 'Accel': "P, I", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Pipe","Creates a pipe object from a given wire or line")} + return { + "Pixmap": "Arch_Pipe", + "MenuText": QT_TRANSLATE_NOOP("Arch_Pipe", "Pipe"), + "Accel": "P, I", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Pipe", "Creates a pipe object from a given wire or line" + ), + } def IsActive(self): @@ -54,16 +57,18 @@ class Arch_Pipe: s = FreeCADGui.Selection.getSelection() if s: for obj in s: - if hasattr(obj,'Shape'): + if hasattr(obj, "Shape"): if len(obj.Shape.Wires) == 1: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Pipe")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Pipe")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") - FreeCADGui.doCommand("obj = Arch.makePipe(FreeCAD.ActiveDocument."+obj.Name+")") + FreeCADGui.doCommand( + "obj = Arch.makePipe(FreeCAD.ActiveDocument." + obj.Name + ")" + ) FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() else: - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Pipe")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Pipe")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("obj = Arch.makePipe()") @@ -73,15 +78,18 @@ class Arch_Pipe: class Arch_PipeConnector: - "the Arch Pipe command definition" def GetResources(self): - return {'Pixmap' : 'Arch_PipeConnector', - 'MenuText': QT_TRANSLATE_NOOP("Arch_PipeConnector","Connector"), - 'Accel': "P, C", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_PipeConnector","Creates a connector between 2 or 3 selected pipes")} + return { + "Pixmap": "Arch_PipeConnector", + "MenuText": QT_TRANSLATE_NOOP("Arch_PipeConnector", "Connector"), + "Accel": "P, C", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_PipeConnector", "Creates a connector between 2 or 3 selected pipes" + ), + } def IsActive(self): @@ -91,21 +99,24 @@ class Arch_PipeConnector: def Activated(self): import Draft + s = FreeCADGui.Selection.getSelection() - if not (len(s) in [2,3]): - FreeCAD.Console.PrintError(translate("Arch","Select exactly 2 or 3 pipe objects")+"\n") + if not (len(s) in [2, 3]): + FreeCAD.Console.PrintError( + translate("Arch", "Select exactly 2 or 3 pipe objects") + "\n" + ) return o = "[" for obj in s: if Draft.getType(obj) != "Pipe": - FreeCAD.Console.PrintError(translate("Arch","Select only pipe objects")+"\n") + FreeCAD.Console.PrintError(translate("Arch", "Select only pipe objects") + "\n") return - o += "FreeCAD.ActiveDocument."+obj.Name+"," + o += "FreeCAD.ActiveDocument." + obj.Name + "," o += "]" - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Connector")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Connector")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") - FreeCADGui.doCommand("obj = Arch.makePipeConnector("+o+")") + FreeCADGui.doCommand("obj = Arch.makePipeConnector(" + o + ")") FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -114,16 +125,19 @@ class Arch_PipeConnector: class Arch_PipeGroupCommand: def GetCommands(self): - return tuple(['Arch_Pipe','Arch_PipeConnector']) + return tuple(["Arch_Pipe", "Arch_PipeConnector"]) + def GetResources(self): - return { 'MenuText': QT_TRANSLATE_NOOP("Arch_PipeTools",'Pipe Tools'), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_PipeTools",'Pipe tools') - } + return { + "MenuText": QT_TRANSLATE_NOOP("Arch_PipeTools", "Pipe Tools"), + "ToolTip": QT_TRANSLATE_NOOP("Arch_PipeTools", "Pipe tools"), + } + def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v -FreeCADGui.addCommand('Arch_Pipe',Arch_Pipe()) -FreeCADGui.addCommand('Arch_PipeConnector',Arch_PipeConnector()) -FreeCADGui.addCommand('Arch_PipeTools', Arch_PipeGroupCommand()) +FreeCADGui.addCommand("Arch_Pipe", Arch_Pipe()) +FreeCADGui.addCommand("Arch_PipeConnector", Arch_PipeConnector()) +FreeCADGui.addCommand("Arch_PipeTools", Arch_PipeGroupCommand()) diff --git a/src/Mod/BIM/bimcommands/BimPreflight.py b/src/Mod/BIM/bimcommands/BimPreflight.py index 00b178e02d..929254ea1a 100644 --- a/src/Mod/BIM/bimcommands/BimPreflight.py +++ b/src/Mod/BIM/bimcommands/BimPreflight.py @@ -89,9 +89,7 @@ class BIM_Preflight_TaskPanel: self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_Preflight.svg")) for test in tests: getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_right.svg")) - getattr(self.form, test).setToolTip( - translate("BIM", "Press to perform the test") - ) + getattr(self.form, test).setToolTip(translate("BIM", "Press to perform the test")) if hasattr(self, test): getattr(self.form, test).clicked.connect(getattr(self, test)) self.results[test] = None @@ -101,9 +99,7 @@ class BIM_Preflight_TaskPanel: self.customTests = {} customModulePath = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Preflight") if os.path.exists(customModulePath): - customModules = [ - m[:-3] for m in os.listdir(customModulePath) if m.endswith(".py") - ] + customModules = [m[:-3] for m in os.listdir(customModulePath) if m.endswith(".py")] if customModules: sys.path.append(customModulePath) for customModule in customModules: @@ -119,17 +115,9 @@ class BIM_Preflight_TaskPanel: ) continue FreeCAD.Console.PrintLog( - "Preflight: found custom module: " - + customModule - + " " - + str(mod) - + "\n" + "Preflight: found custom module: " + customModule + " " + str(mod) + "\n" ) - functions = [ - o[0] - for o in inspect.getmembers(mod) - if inspect.isfunction(o[1]) - ] + functions = [o[0] for o in inspect.getmembers(mod) if inspect.isfunction(o[1])] if functions: box = QtGui.QGroupBox(customModule) lay = QtGui.QGridLayout(box) @@ -174,9 +162,7 @@ class BIM_Preflight_TaskPanel: getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_valid.svg")) getattr(self.form, test).setText(translate("BIM", "Passed")) - getattr(self.form, test).setToolTip( - translate("BIM", "This test has succeeded.") - ) + getattr(self.form, test).setToolTip(translate("BIM", "This test has succeeded.")) def failed(self, test): "sets the button as failed" @@ -196,9 +182,7 @@ class BIM_Preflight_TaskPanel: getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_right.svg")) getattr(self.form, test).setText(translate("BIM", "Test")) - getattr(self.form, test).setToolTip( - translate("BIM", "Press to perform the test") - ) + getattr(self.form, test).setToolTip(translate("BIM", "Press to perform the test")) def show(self, test): "shows test results" @@ -213,9 +197,7 @@ class BIM_Preflight_TaskPanel: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.rform.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.rform.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.rform.rect().center() ) self.rform.buttonReport.clicked.connect(self.toReport) self.rform.buttonOK.clicked.connect(self.closeReport) @@ -247,11 +229,7 @@ class BIM_Preflight_TaskPanel: if self.form.getAll.isChecked(): objs = FreeCAD.ActiveDocument.Objects elif self.form.getVisible.isChecked(): - objs = [ - o - for o in FreeCAD.ActiveDocument.Objects - if o.ViewObject.Visibility == True - ] + objs = [o for o in FreeCAD.ActiveDocument.Objects if o.ViewObject.Visibility == True] else: objs = FreeCADGui.Selection.getSelection() # clean objects list of unwanted types @@ -316,7 +294,9 @@ class BIM_Preflight_TaskPanel: translate( "BIM", "ifcopenshell is not installed on the system or not available to FreeCAD. This library is responsible for IFC support in FreeCAD, and therefore IFC support is currently disabled. Check %1 to obtain more information.", - ).replace("%1", "https://www.freecadweb.org/wiki/Extra_python_modules#IfcOpenShell") + ).replace( + "%1", "https://www.freecadweb.org/wiki/Extra_python_modules#IfcOpenShell" + ) + " " ) self.failed(test) @@ -328,10 +308,11 @@ class BIM_Preflight_TaskPanel: elif hasattr(ifcopenshell, "version"): try: from packaging import version + if "-" in ifcopenshell.version: # Prebuild version have a version like 'v0.7.0-, # trying to remove the commit id. - cur_version = version.parse(ifcopenshell.version.split('-')[0]) + cur_version = version.parse(ifcopenshell.version.split("-")[0]) else: cur_version = version.parse(ifcopenshell.version) min_version = version.parse("0.6") @@ -396,18 +377,13 @@ class BIM_Preflight_TaskPanel: or (hasattr(obj, "IfcType") and (obj.IfcType == "Building")) ): buildings = True - elif ( - hasattr(obj, "IfcRole") and (obj.IfcRole == "Building Storey") - ) or (hasattr(obj, "IfcType") and (obj.IfcType == "Building Storey")): + elif (hasattr(obj, "IfcRole") and (obj.IfcRole == "Building Storey")) or ( + hasattr(obj, "IfcType") and (obj.IfcType == "Building Storey") + ): storeys = True if (not sites) or (not buildings) or (not storeys): msg = self.getToolTip(test) - msg += ( - translate( - "BIM", "The following types were not found in the project:" - ) - + "\n" - ) + msg += translate("BIM", "The following types were not found in the project:") + "\n" if not sites: msg += "\nSite" if not buildings: @@ -446,14 +422,8 @@ class BIM_Preflight_TaskPanel: for parent in obj.InList: if ( (Draft.getType(parent) == "Site") - or ( - hasattr(parent, "IfcRole") - and (parent.IfcRole == "Site") - ) - or ( - hasattr(parent, "IfcType") - and (parent.IfcType == "Site") - ) + or (hasattr(parent, "IfcRole") and (parent.IfcRole == "Site")) + or (hasattr(parent, "IfcType") and (parent.IfcType == "Site")) ): if hasattr(parent, "Group") and parent.Group: if obj in parent.Group: @@ -498,12 +468,8 @@ class BIM_Preflight_TaskPanel: ): ok = False for parent in obj.InList: - if ( - hasattr(parent, "IfcRole") - and (parent.IfcRole == "Building") - ) or ( - hasattr(parent, "IfcType") - and (parent.IfcType == "Building") + if (hasattr(parent, "IfcRole") and (parent.IfcRole == "Building")) or ( + hasattr(parent, "IfcType") and (parent.IfcType == "Building") ): if hasattr(parent, "Group") and parent.Group: if obj in parent.Group: @@ -553,9 +519,9 @@ class BIM_Preflight_TaskPanel: ok = False ancestors = obj.InListRecursive # append extra objects not in InList - if hasattr(obj,"Host") and not obj.Host in ancestors: + if hasattr(obj, "Host") and not obj.Host in ancestors: ancestors.append(obj.Host) - if hasattr(obj,"Hosts"): + if hasattr(obj, "Hosts"): for h in obj.Hosts: if not h in ancestors: ancestors.append(h) @@ -631,10 +597,7 @@ class BIM_Preflight_TaskPanel: for o in undefined: msg += o.Label + "\n" if notbim: - msg += ( - translate("BIM", "The following objects are not BIM objects:") - + "\n\n" - ) + msg += translate("BIM", "The following objects are not BIM objects:") + "\n\n" for o in notbim: msg += o.Label + "\n" msg += translate( @@ -704,9 +667,7 @@ class BIM_Preflight_TaskPanel: msg = None for obj in self.getObjects(): - if hasattr(obj, "IfcAttributes") and ( - Draft.getType(obj) != "BuildingPart" - ): + if hasattr(obj, "IfcAttributes") and (Draft.getType(obj) != "BuildingPart"): for prop in ["Length", "Width", "Height"]: if prop in obj.PropertiesList: if (not "Export" + prop in obj.IfcAttributes) or ( @@ -765,17 +726,13 @@ class BIM_Preflight_TaskPanel: for row in reader: if "Common" in row[0]: psets.append(row[0][5:-6]) - psets = [ - "".join(map(lambda x: x if x.islower() else " " + x, p)) for p in psets - ] + psets = ["".join(map(lambda x: x if x.islower() else " " + x, p)) for p in psets] psets = [pset.strip() for pset in psets] # print(psets) for obj in self.getObjects(): ok = True - if hasattr(obj, "IfcProperties") and isinstance( - obj.IfcProperties, dict - ): + if hasattr(obj, "IfcProperties") and isinstance(obj.IfcProperties, dict): r = None if hasattr(obj, "IfcType"): r = obj.IfcType @@ -844,9 +801,7 @@ class BIM_Preflight_TaskPanel: for obj in self.getObjects(): ok = True - if hasattr(obj, "IfcProperties") and isinstance( - obj.IfcProperties, dict - ): + if hasattr(obj, "IfcProperties") and isinstance(obj.IfcProperties, dict): r = None if hasattr(obj, "IfcType"): r = obj.IfcType @@ -890,7 +845,10 @@ class BIM_Preflight_TaskPanel: + translate( "BIM", "Verify which properties a certain property set must contain on %1", - ).replace("%1", "https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/annex/annex-b/alphabeticalorder_psets.htm") + ).replace( + "%1", + "https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/annex/annex-b/alphabeticalorder_psets.htm", + ) + "\n\n" ) msg += translate( @@ -925,9 +883,7 @@ class BIM_Preflight_TaskPanel: if self.culprits[test]: msg = self.getToolTip(test) msg += ( - translate( - "BIM", "The following BIM objects have no material attributed:" - ) + translate("BIM", "The following BIM objects have no material attributed:") + "\n\n" ) for o in self.culprits[test]: @@ -1003,9 +959,9 @@ class BIM_Preflight_TaskPanel: and (obj.IfcAttributes["FlagForceBrep"] == "True") ): self.culprits[test].append(obj) - elif hasattr( - obj.Proxy, "getExtrusionData" - ) and not obj.Proxy.getExtrusionData(obj): + elif hasattr(obj.Proxy, "getExtrusionData") and not obj.Proxy.getExtrusionData( + obj + ): self.culprits[test].append(obj) elif Draft.getType(obj) == "BuildingPart": pass @@ -1019,10 +975,7 @@ class BIM_Preflight_TaskPanel: self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) - msg += ( - translate("BIM", "The following BIM objects are not extrusions:") - + "\n\n" - ) + msg += translate("BIM", "The following BIM objects are not extrusions:") + "\n\n" for o in self.culprits[test]: msg += o.Label + "\n" if msg: @@ -1053,17 +1006,13 @@ class BIM_Preflight_TaskPanel: self.culprits[test].append(obj) elif Draft.getType(obj) == "Structure": if obj.Base and ( - (len(obj.Base.Shape.Wires) != 1) - or (not obj.Base.Shape.Wires[0].isClosed()) + (len(obj.Base.Shape.Wires) != 1) or (not obj.Base.Shape.Wires[0].isClosed()) ): self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( - translate( - "BIM", "The following BIM objects are not standard cases:" - ) - + "\n\n" + translate("BIM", "The following BIM objects are not standard cases:") + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" @@ -1101,9 +1050,8 @@ class BIM_Preflight_TaskPanel: objs.append(obj) if edges: import Part - result = FreeCAD.ActiveDocument.addObject( - "Part::Feature", "TinyLinesResult" - ) + + result = FreeCAD.ActiveDocument.addObject("Part::Feature", "TinyLinesResult") result.Shape = Part.makeCompound(edges) result.ViewObject.LineWidth = 5 self.culprits[test] = [result] diff --git a/src/Mod/BIM/bimcommands/BimProfile.py b/src/Mod/BIM/bimcommands/BimProfile.py index bf6f4ac618..883ed20f42 100644 --- a/src/Mod/BIM/bimcommands/BimProfile.py +++ b/src/Mod/BIM/bimcommands/BimProfile.py @@ -34,15 +34,16 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Profile: - """The FreeCAD Arch_Profile command definition""" def GetResources(self): - return {'Pixmap' : 'Arch_Profile', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Profile","Profile"), - 'Accel': "P, F", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Profile","Creates a profile")} + return { + "Pixmap": "Arch_Profile", + "MenuText": QT_TRANSLATE_NOOP("Arch_Profile", "Profile"), + "Accel": "P, F", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Profile", "Creates a profile"), + } def IsActive(self): @@ -61,34 +62,38 @@ class Arch_Profile: for pre in self.Presets: if pre[1] not in self.Categories: self.Categories.append(pre[1]) - FreeCADGui.Snapper.getPoint(callback=self.getPoint,extradlg=[self.taskbox()],title=translate("Arch","Create profile")) + FreeCADGui.Snapper.getPoint( + callback=self.getPoint, + extradlg=[self.taskbox()], + title=translate("Arch", "Create profile"), + ) def taskbox(self): - "sets up a taskbox widget" from PySide import QtGui from draftutils import params + w = QtGui.QWidget() ui = FreeCADGui.UiLoader() - w.setWindowTitle(translate("Arch","Profile settings")) + w.setWindowTitle(translate("Arch", "Profile settings")) grid = QtGui.QGridLayout(w) # categories box - labelc = QtGui.QLabel(translate("Arch","Category")) + labelc = QtGui.QLabel(translate("Arch", "Category")) self.vCategory = QtGui.QComboBox() self.vCategory.addItems(self.Categories) - grid.addWidget(labelc,1,0,1,1) - grid.addWidget(self.vCategory,1,1,1,1) + grid.addWidget(labelc, 1, 0, 1, 1) + grid.addWidget(self.vCategory, 1, 1, 1, 1) # presets box - labelp = QtGui.QLabel(translate("Arch","Preset")) + labelp = QtGui.QLabel(translate("Arch", "Preset")) self.vPresets = QtGui.QComboBox() self.pSelect = [None] fpresets = [" "] self.vPresets.addItems(fpresets) - grid.addWidget(labelp,2,0,1,1) - grid.addWidget(self.vPresets,2,1,1,1) + grid.addWidget(labelp, 2, 0, 1, 1) + grid.addWidget(self.vPresets, 2, 1, 1, 1) # restore preset categoryIdx = -1 @@ -118,8 +123,7 @@ class Arch_Profile: return w - def getPoint(self,point=None,obj=None): - + def getPoint(self, point=None, obj=None): "this function is called by the snapper when it has a 3D point" FreeCAD.activeDraftCommand = None @@ -128,39 +132,41 @@ class Arch_Profile: return if not self.Profile: return - pt = "FreeCAD.Vector("+str(point.x)+","+str(point.y)+","+str(point.z)+")" - self.doc.openTransaction(translate("Arch","Create Profile")) + pt = "FreeCAD.Vector(" + str(point.x) + "," + str(point.y) + "," + str(point.z) + ")" + self.doc.openTransaction(translate("Arch", "Create Profile")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand('p = Arch.makeProfile('+str(self.Profile)+')') - FreeCADGui.addModule('WorkingPlane') - FreeCADGui.doCommand('p.Placement = WorkingPlane.get_working_plane().get_placement()') - FreeCADGui.doCommand('p.Placement.Base = ' + pt) + FreeCADGui.doCommand("p = Arch.makeProfile(" + str(self.Profile) + ")") + FreeCADGui.addModule("WorkingPlane") + FreeCADGui.doCommand("p.Placement = WorkingPlane.get_working_plane().get_placement()") + FreeCADGui.doCommand("p.Placement.Base = " + pt) FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(p)") self.doc.commitTransaction() self.doc.recompute() - def setCategory(self,i): + def setCategory(self, i): from draftutils import params + self.vPresets.clear() self.pSelect = [p for p in self.Presets if p[1] == self.Categories[i]] fpresets = [] for p in self.pSelect: - 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)) + 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]) - fpresets.append(p[2]+" ("+s1+"x"+s2+s3+")") + fpresets.append(p[2] + " (" + s1 + "x" + s2 + s3 + ")") self.vPresets.addItems(fpresets) self.setPreset(0) - def setPreset(self,i): + def setPreset(self, i): from draftutils import params + self.Profile = self.pSelect[i] - params.set_param_arch("ProfilePreset",";".join([str(i) for i in self.Profile])) + params.set_param_arch("ProfilePreset", ";".join([str(i) for i in self.Profile])) -FreeCADGui.addCommand('Arch_Profile',Arch_Profile()) +FreeCADGui.addCommand("Arch_Profile", Arch_Profile()) diff --git a/src/Mod/BIM/bimcommands/BimProject.py b/src/Mod/BIM/bimcommands/BimProject.py index f5b935b67e..041b564959 100644 --- a/src/Mod/BIM/bimcommands/BimProject.py +++ b/src/Mod/BIM/bimcommands/BimProject.py @@ -36,8 +36,7 @@ class BIM_Project: return { "Pixmap": "BIM_Project", "MenuText": QT_TRANSLATE_NOOP("BIM_Project", "Project"), - "ToolTip": QT_TRANSLATE_NOOP("BIM_Project", - "Creates an empty NativeIFC project"), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Project", "Creates an empty NativeIFC project"), } def IsActive(self): @@ -46,6 +45,7 @@ class BIM_Project: def Activated(self): from nativeifc import ifc_tools + project = ifc_tools.create_document(FreeCAD.ActiveDocument) project.Modified = True FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/BIM/bimcommands/BimProjectManager.py b/src/Mod/BIM/bimcommands/BimProjectManager.py index 445f578394..576b29d4f5 100644 --- a/src/Mod/BIM/bimcommands/BimProjectManager.py +++ b/src/Mod/BIM/bimcommands/BimProjectManager.py @@ -41,9 +41,7 @@ class BIM_ProjectManager: return { "Pixmap": "BIM_ProjectManager", "MenuText": QT_TRANSLATE_NOOP("BIM_ProjectManager", "Setup Project"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_ProjectManager", "Creates or manages a BIM project" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_ProjectManager", "Creates or manages a BIM project"), } def Activated(self): @@ -65,9 +63,7 @@ class BIM_ProjectManager: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) # set things up @@ -97,6 +93,7 @@ class BIM_ProjectManager: self.form.groupNewProject.setEnabled(False) if self.project: from nativeifc import ifc_tools + sites = ifc_tools.get_children(self.project, ifctype="IfcSite") sites = list(filter(None, [ifc_tools.get_object(s) for s in sites])) self.form.projectName.setText(self.project.Label) @@ -105,33 +102,35 @@ class BIM_ProjectManager: if sites: self.site = sites[0] self.form.siteName.setText(self.site.Label) - if hasattr(self.site,"Address"): + if hasattr(self.site, "Address"): self.form.siteAddress.setText(self.site.Address) - elif hasattr(self.site,"SiteAddress"): + elif hasattr(self.site, "SiteAddress"): self.form.siteAddress.setText(self.site.SiteAddress) - if hasattr(self.site,"Longitude"): + if hasattr(self.site, "Longitude"): self.form.siteLongitude.setValue(self.site.Longitude) - elif hasattr(self.site,"RefLongitude"): + elif hasattr(self.site, "RefLongitude"): self.form.siteLongitude.setValue(self.site.RefLongitude) - if hasattr(self.site,"Latitude"): + if hasattr(self.site, "Latitude"): self.form.siteLatitude.setValue(self.site.Latitude) - elif hasattr(self.site,"RefLatitude"): + elif hasattr(self.site, "RefLatitude"): self.form.siteLatitude.setValue(self.site.RefLatitude) - if hasattr(self.site,"Elevation"): + if hasattr(self.site, "Elevation"): self.form.siteElevation.setText(self.site.Elevation.UserString) - elif hasattr(self.site,"RefElevation"): + elif hasattr(self.site, "RefElevation"): self.form.siteElevation.setText(self.site.RefElevation.UserString) if hasattr(self.site, "Declination"): self.form.siteElevation.setText(str(self.site.Declination)) buildings = [] if self.site and self.project: from nativeifc import ifc_tools + buildings = ifc_tools.get_children(self.site, ifctype="IfcBuilding") buildings = list(filter(None, [ifc_tools.get_object(b) for b in buildings])) if not buildings: buildings = [o for o in doc.Objects if getattr(o, "IfcType", "") == "Building"] if buildings: from nativeifc import ifc_tools + self.building = buildings[0] self.form.buildingName.setText(self.building.Label) levels = ifc_tools.get_children(self.building, ifctype="IfcBuildingStorey") @@ -178,9 +177,11 @@ class BIM_ProjectManager: # Document creation doc = FreeCAD.ActiveDocument if self.form.groupNewProject.isChecked(): - if self.form.radioNative1.isChecked() or \ - self.form.radioNative3.isChecked() or \ - (self.form.radioNative2.isChecked() and doc is None): + if ( + self.form.radioNative1.isChecked() + or self.form.radioNative3.isChecked() + or (self.form.radioNative2.isChecked() and doc is None) + ): doc = FreeCAD.newDocument() if self.form.projectName.text(): doc.Label = self.form.projectName.text() @@ -190,6 +191,7 @@ class BIM_ProjectManager: # Project creation if self.form.groupNewProject.isChecked(): from nativeifc import ifc_tools + if self.form.radioNative2.isChecked(): self.project = ifc_tools.create_document_object(doc, silent=True) if self.form.projectName.text(): @@ -213,19 +215,20 @@ class BIM_ProjectManager: self.site = Arch.makeSite() if self.project: from nativeifc import ifc_tools + self.site = ifc_tools.aggregate(self.site, self.project) self.site.Label = self.form.siteName.text() - if hasattr(self.site,"Address"): + if hasattr(self.site, "Address"): self.site.Address = self.form.siteAddress.text() - elif hasattr(self.site,"SiteAddress"): + elif hasattr(self.site, "SiteAddress"): self.site.SiteAddress = self.form.siteAddress.text() - if hasattr(self.site,"Longitude"): + if hasattr(self.site, "Longitude"): self.site.Longitude = self.form.siteLongitude.value() - elif hasattr(self.site,"RefLongitude"): + elif hasattr(self.site, "RefLongitude"): self.site.RefLongitude = self.form.siteLongitude.value() - if hasattr(self.site,"Latitude"): + if hasattr(self.site, "Latitude"): self.site.Latitude = self.form.siteLatitude.value() - elif hasattr(self.site,"RefLatitude"): + elif hasattr(self.site, "RefLatitude"): self.site.RefLatitude = self.form.siteLatitude.value() if hasattr(self.site, "NorthDeviation"): self.site.NorthDeviation = self.form.siteDeviation.value() @@ -243,6 +246,7 @@ class BIM_ProjectManager: self.building = Arch.makeBuilding() if self.project: from nativeifc import ifc_tools + if self.site: self.building = ifc_tools.aggregate(self.building, self.site) else: @@ -262,9 +266,9 @@ class BIM_ProjectManager: axes = [o for o in grp.Group if o.Name.startswith("Axis")] if axes: for ax in axes: - if round(math.degrees(ax.Placement.Rotation.Angle),0) in [0, 180, 360]: + if round(math.degrees(ax.Placement.Rotation.Angle), 0) in [0, 180, 360]: vaxes.append(ax) - elif round(math.degrees(ax.Placement.Rotation.Angle),0) in [90, 270]: + elif round(math.degrees(ax.Placement.Rotation.Angle), 0) in [90, 270]: haxes.append(ax) outline = [o for o in grp.Group if o.Name.startswith("Rectangle")] if outline: @@ -273,8 +277,12 @@ class BIM_ProjectManager: if human: human = human[0] else: - if self.form.addHumanFigure.isChecked() or self.form.countVAxes.value() \ - or self.form.countHAxes.value() or (buildingWidth and buildingLength): + if ( + self.form.addHumanFigure.isChecked() + or self.form.countVAxes.value() + or self.form.countHAxes.value() + or (buildingWidth and buildingLength) + ): grp = doc.addObject("App::DocumentObjectGroup", "BuildingLayout") grp.Label = translate("BIM", "Building Layout") if self.building: @@ -373,17 +381,17 @@ class BIM_ProjectManager: lev.Placement.move(FreeCAD.Vector(0, 0, h)) if self.project and self.building: from nativeifc import ifc_tools + lev = ifc_tools.aggregate(lev, self.building) elif self.building: self.building.addObject(lev) h += levelHeight for group in groups: - levGroup = FreeCAD.ActiveDocument.addObject( - "App::DocumentObjectGroup" - ) + levGroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup") levGroup.Label = group if self.project: from nativeifc import ifc_tools + ifc_tools.aggregate(levGroup, lev) else: lev.addObject(levGroup) @@ -398,6 +406,7 @@ class BIM_ProjectManager: if self.building and grp: if hasattr(self.building, "IfcClass"): from nativeifc import ifc_tools + ifc_tools.aggregate(grp, self.building) self.form.hide() FreeCAD.ActiveDocument.recompute() @@ -405,7 +414,8 @@ class BIM_ProjectManager: FreeCADGui.Snapper.show() if self.form.radioNative3.isChecked(): from nativeifc import ifc_status - ifc_status.set_button(True,True) + + ifc_status.set_button(True, True) return self.reject() def addGroup(self): @@ -442,11 +452,7 @@ class BIM_ProjectManager: groups.append(self.form.groupsList.item(i).text()) s = "# FreeCAD BIM Project setup preset " + name + "\n" - s += ( - "groupNewDocument=" - + str(int(self.form.groupNewProject.isChecked())) - + "\n" - ) + s += "groupNewDocument=" + str(int(self.form.groupNewProject.isChecked())) + "\n" s += "projectName=" + self.form.projectName.text() + "\n" s += "groupSite=" + str(int(self.form.groupSite.isChecked())) + "\n" @@ -469,17 +475,9 @@ class BIM_ProjectManager: s += "countLevels=" + str(self.form.countLevels.value()) + "\n" s += "levelHeight=" + self.form.levelHeight.text() + "\n" s += "lineWidth=" + str(self.form.lineWidth.value()) + "\n" - s += ( - "lineColor=" - + str(self.form.lineColor.property("color").getRgbF()[:3]) - + "\n" - ) + s += "lineColor=" + str(self.form.lineColor.property("color").getRgbF()[:3]) + "\n" s += "groups=" + ";;".join(groups) + "\n" - s += ( - "addHumanFigure=" - + str(int(self.form.addHumanFigure.isChecked())) - + "\n" - ) + s += "addHumanFigure=" + str(int(self.form.addHumanFigure.isChecked())) + "\n" f = open(os.path.join(presetdir, name + ".txt"), "w") f.write(s) @@ -553,12 +551,7 @@ class BIM_ProjectManager: elif s[0] == "lineWidth": self.form.lineWidth.setValue(int(s[1])) elif s[0] == "lineColor": - col = tuple( - [ - float(t) - for t in s[1].strip("(").strip(")").split(",") - ] - ) + col = tuple([float(t) for t in s[1].strip("(").strip(")").split(",")]) col = QtGui.QColor.fromRgbF(*col) self.form.lineColor.setProperty("color", col) elif s[0] == "groups": @@ -585,9 +578,7 @@ class BIM_ProjectManager: values["wpv"] = str(wp.v) values["wpaxis"] = str(wp.axis) values["unit"] = str( - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( - "UserSchema", 0 - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("UserSchema", 0) ) values["textsize"] = str( FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat( @@ -600,9 +591,7 @@ class BIM_ProjectManager: ) ) values["dimstyle"] = str( - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( - "dimsymbol", 0 - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt("dimsymbol", 0) ) values["arrowsize"] = str( FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat( @@ -610,9 +599,7 @@ class BIM_ProjectManager: ) ) values["decimals"] = str( - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( - "Decimals", 2 - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals", 2) ) values["grid"] = str( FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat( @@ -620,9 +607,7 @@ class BIM_ProjectManager: ) ) values["squares"] = str( - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( - "gridEvery", 10 - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt("gridEvery", 10) ) values["linewidth"] = str( FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetInt( @@ -645,9 +630,9 @@ class BIM_ProjectManager: ) ) values["colConst"] = str( - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetUnsigned("constructioncolor", 746455039) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetUnsigned( + "constructioncolor", 746455039 + ) ) d.Meta = values @@ -664,9 +649,7 @@ class BIM_ProjectManager: if not filename.lower().endswith(".FCStd"): filename += ".FCStd" d.saveCopy(filename) - FreeCAD.Console.PrintMessage( - translate("BIM", "Template saved successfully") + "\n" - ) + FreeCAD.Console.PrintMessage(translate("BIM", "Template saved successfully") + "\n") self.reject() def loadTemplate(self): @@ -692,9 +675,7 @@ class BIM_ProjectManager: values = td.Meta FreeCAD.closeDocument(tname) d.mergeProject(filename) - FreeCADGui.ActiveDocument = FreeCADGui.getDocument( - d.Name - ) # fix b/c hidden doc + FreeCADGui.ActiveDocument = FreeCADGui.getDocument(d.Name) # fix b/c hidden doc else: d = FreeCAD.openDocument(filename) FreeCAD.ActiveDocument = d @@ -718,33 +699,31 @@ class BIM_ProjectManager: FreeCAD.Units.setSchema(int(values["unit"])) bimunit = [0, 2, 3, 3, 1, 5, 0, 4][int(values["unit"])] if "textsize" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).SetFloat("textheight", float(values["textsize"])) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetFloat( + "textheight", float(values["textsize"]) + ) if hasattr(FreeCADGui, "draftToolBar"): - FreeCADGui.draftToolBar.fontsizeButton.setValue( - float(values["textsize"]) - ) + FreeCADGui.draftToolBar.fontsizeButton.setValue(float(values["textsize"])) if "textfont" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).SetString("textfont", values["textfont"]) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetString( + "textfont", values["textfont"] + ) if "dimstyle" in values: FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetInt( "dimsymbol", int(values["dimstyle"]) ) if "arrowsize" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).SetFloat("arrowsize", float(values["arrowsize"])) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetFloat( + "arrowsize", float(values["arrowsize"]) + ) if "decimals" in values: FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt( "Decimals", int(values["decimals"]) ) if "grid" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).SetFloat("gridSpacing", float(values["grid"])) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetFloat( + "gridSpacing", float(values["grid"]) + ) if "squares" in values: FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetInt( "gridEvery", int(values["squares"]) @@ -756,9 +735,7 @@ class BIM_ProjectManager: "DefautShapeLineWidth", int(values["linewidth"]) ) if hasattr(FreeCADGui, "draftToolBar"): - FreeCADGui.draftToolBar.widthButton.setValue( - int(values["linewidth"]) - ) + FreeCADGui.draftToolBar.widthButton.setValue(int(values["linewidth"])) if "colFace" in values: FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").SetUnsigned( "DefaultShapeColor", int(values["colFace"]) @@ -768,13 +745,13 @@ class BIM_ProjectManager: "DefaultShapeLineColor", int(values["colLine"]) ) if "colHelp" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Arch" - ).SetUnsigned("ColorHelpers", int(values["colHelp"])) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetUnsigned( + "ColorHelpers", int(values["colHelp"]) + ) if "colConst" in values: - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).SetUnsigned("constructioncolor", int(values["colConst"])) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetUnsigned( + "constructioncolor", int(values["colConst"]) + ) # set the status bar widgets mw = FreeCADGui.getMainWindow() @@ -799,8 +776,7 @@ class BIM_ProjectManager: ) FreeCAD.Console.PrintMessage( - translate("BIM", "Template successfully loaded into the current document") - + "\n" + translate("BIM", "Template successfully loaded into the current document") + "\n" ) self.reject() diff --git a/src/Mod/BIM/bimcommands/BimRebar.py b/src/Mod/BIM/bimcommands/BimRebar.py index d59fdc7282..0dfb9ae420 100644 --- a/src/Mod/BIM/bimcommands/BimRebar.py +++ b/src/Mod/BIM/bimcommands/BimRebar.py @@ -34,15 +34,19 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Rebar: - "the Arch Rebar command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Rebar', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Rebar","Custom Rebar"), - 'Accel': "R, B", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Rebar","Creates a reinforcement bar from the selected face of solid object and/or a sketch")} + return { + "Pixmap": "Arch_Rebar", + "MenuText": QT_TRANSLATE_NOOP("Arch_Rebar", "Custom Rebar"), + "Accel": "R, B", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Rebar", + "Creates a reinforcement bar from the selected face of solid object and/or a sketch", + ), + } def IsActive(self): @@ -52,19 +56,28 @@ class Arch_Rebar: def Activated(self): import ArchComponent + sel = FreeCADGui.Selection.getSelectionEx() if sel: obj = sel[0].Object - if hasattr(obj,"Shape") and obj.Shape.Solids: + if hasattr(obj, "Shape") and obj.Shape.Solids: # this is our host object if len(sel) > 1: sk = sel[1].Object - if hasattr(sk,'Shape'): + if hasattr(sk, "Shape"): if len(sk.Shape.Wires) == 1: # we have a structure and a wire: create the rebar now - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Rebar")) + FreeCAD.ActiveDocument.openTransaction( + translate("Arch", "Create Rebar") + ) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.makeRebar(FreeCAD.ActiveDocument."+obj.Name+",FreeCAD.ActiveDocument."+sk.Name+")") + FreeCADGui.doCommand( + "Arch.makeRebar(FreeCAD.ActiveDocument." + + obj.Name + + ",FreeCAD.ActiveDocument." + + sk.Name + + ")" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() return @@ -72,29 +85,40 @@ class Arch_Rebar: # we have only a structure: open the sketcher FreeCADGui.activateWorkbench("SketcherWorkbench") FreeCADGui.runCommand("Sketcher_NewSketch") - FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(obj,FreeCAD.ActiveDocument.Objects[-1],hide=False,nextCommand="Arch_Rebar") + FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver( + obj, + FreeCAD.ActiveDocument.Objects[-1], + hide=False, + nextCommand="Arch_Rebar", + ) FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) return - elif hasattr(obj,'Shape'): + elif hasattr(obj, "Shape"): if len(obj.Shape.Wires) == 1: # we have only a wire: extract its support object, if available, and make the rebar support = "None" - if hasattr(obj,"AttachmentSupport"): + if hasattr(obj, "AttachmentSupport"): if obj.AttachmentSupport: if len(obj.AttachmentSupport) != 0: - support = "FreeCAD.ActiveDocument."+obj.AttachmentSupport[0][0].Name - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Rebar")) + support = ( + "FreeCAD.ActiveDocument." + obj.AttachmentSupport[0][0].Name + ) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Rebar")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.makeRebar("+support+",FreeCAD.ActiveDocument."+obj.Name+")") + FreeCADGui.doCommand( + "Arch.makeRebar(" + support + ",FreeCAD.ActiveDocument." + obj.Name + ")" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() return - FreeCAD.Console.PrintMessage(translate("Arch","Select a base face on a structural object")+"\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Select a base face on a structural object") + "\n" + ) FreeCADGui.Control.closeDialog() FreeCADGui.Control.showDialog(ArchComponent.SelectionTaskPanel()) FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(nextCommand="Arch_Rebar") FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) -FreeCADGui.addCommand('Arch_Rebar',Arch_Rebar()) +FreeCADGui.addCommand("Arch_Rebar", Arch_Rebar()) diff --git a/src/Mod/BIM/bimcommands/BimReextrude.py b/src/Mod/BIM/bimcommands/BimReextrude.py index 71d263e213..e53f73ccfb 100644 --- a/src/Mod/BIM/bimcommands/BimReextrude.py +++ b/src/Mod/BIM/bimcommands/BimReextrude.py @@ -133,9 +133,7 @@ class BIM_Reextrude: + parent.Label + "'s reference to this object has been updated\n" ) - elif isinstance(getattr(parent, prop), list) and ( - obj in getattr(parent, prop) - ): + elif isinstance(getattr(parent, prop), list) and (obj in getattr(parent, prop)): if (prop == "Group") and hasattr(parent, "addObject"): parent.addObject(newobj) else: diff --git a/src/Mod/BIM/bimcommands/BimReference.py b/src/Mod/BIM/bimcommands/BimReference.py index 5c79c269a0..ed5ee875e2 100644 --- a/src/Mod/BIM/bimcommands/BimReference.py +++ b/src/Mod/BIM/bimcommands/BimReference.py @@ -34,15 +34,16 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Reference: - "the Arch Reference command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Reference', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Reference","External Reference"), - 'Accel': "E, X", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Reference","Creates an external reference object")} + return { + "Pixmap": "Arch_Reference", + "MenuText": QT_TRANSLATE_NOOP("Arch_Reference", "External Reference"), + "Accel": "E, X", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Reference", "Creates an external reference object"), + } def IsActive(self): @@ -52,7 +53,7 @@ class Arch_Reference: def Activated(self): FreeCADGui.Control.closeDialog() - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create external reference")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create external reference")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("obj = Arch.makeReference()") @@ -61,4 +62,4 @@ class Arch_Reference: FreeCADGui.doCommand("obj.ViewObject.Document.setEdit(obj.ViewObject, 0)") -FreeCADGui.addCommand('Arch_Reference', Arch_Reference()) +FreeCADGui.addCommand("Arch_Reference", Arch_Reference()) diff --git a/src/Mod/BIM/bimcommands/BimReorder.py b/src/Mod/BIM/bimcommands/BimReorder.py index 6bbe6d1cdd..f724e463ce 100644 --- a/src/Mod/BIM/bimcommands/BimReorder.py +++ b/src/Mod/BIM/bimcommands/BimReorder.py @@ -31,15 +31,14 @@ import FreeCADGui QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate + class BIM_Reorder: def GetResources(self): return { "Pixmap": "BIM_Reorder", "MenuText": QT_TRANSLATE_NOOP("BIM_Reorder", "Reorder Children"), # 'Accel': "R, D", - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Reorder", "Reorders children of the selected object" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Reorder", "Reorders children of the selected object"), } def Activated(self): @@ -50,8 +49,7 @@ class BIM_Reorder: FreeCADGui.Control.showDialog(BIM_Reorder_TaskPanel(obj)) return FreeCAD.Console.PrintError( - translate("BIM", "You must choose a group object before using this command") - + "\n" + translate("BIM", "You must choose a group object before using this command") + "\n" ) @@ -72,8 +70,7 @@ class BIM_Reorder_TaskPanel: def accept(self): names = [ - self.form.listWidget.item(i).toolTip() - for i in range(self.form.listWidget.count()) + self.form.listWidget.item(i).toolTip() for i in range(self.form.listWidget.count()) ] group = [FreeCAD.ActiveDocument.getObject(n) for n in names] FreeCAD.ActiveDocument.openTransaction("Reorder children") diff --git a/src/Mod/BIM/bimcommands/BimRewire.py b/src/Mod/BIM/bimcommands/BimRewire.py index 24f173f239..69c21b1362 100644 --- a/src/Mod/BIM/bimcommands/BimRewire.py +++ b/src/Mod/BIM/bimcommands/BimRewire.py @@ -37,9 +37,7 @@ class BIM_Rewire: return { "Pixmap": "BIM_Rewire", "MenuText": QT_TRANSLATE_NOOP("BIM_Rewire", "Rewire"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Rewire", "Recreates wires from selected objects" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Rewire", "Recreates wires from selected objects"), "Accel": "R,W", } @@ -56,11 +54,7 @@ class BIM_Rewire: names = [] edges = [] for obj in objs: - if ( - hasattr(obj, "Shape") - and hasattr(obj.Shape, "Edges") - and obj.Shape.Edges - ): + if hasattr(obj, "Shape") and hasattr(obj.Shape, "Edges") and obj.Shape.Edges: edges.extend(obj.Shape.Edges) names.append(obj.Name) wires = DraftGeomUtils.findWires(edges) @@ -72,9 +66,7 @@ class BIM_Rewire: nobj.shape = wire selectlist.append(nobj) else: - selectlist.append( - Draft.makeWire([v.Point for v in wire.OrderedVertexes]) - ) + selectlist.append(Draft.makeWire([v.Point for v in wire.OrderedVertexes])) for name in names: FreeCAD.ActiveDocument.removeObject(name) FreeCAD.ActiveDocument.commitTransaction() diff --git a/src/Mod/BIM/bimcommands/BimRoof.py b/src/Mod/BIM/bimcommands/BimRoof.py index c6aa16affa..eaf6780dff 100644 --- a/src/Mod/BIM/bimcommands/BimRoof.py +++ b/src/Mod/BIM/bimcommands/BimRoof.py @@ -34,13 +34,17 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Roof: - '''the Arch Roof command definition''' + """the Arch Roof command definition""" def GetResources(self): - return {"Pixmap" : "Arch_Roof", - "MenuText": QT_TRANSLATE_NOOP("Arch_Roof", "Roof"), - "Accel" : "R, F", - "ToolTip" : QT_TRANSLATE_NOOP("Arch_Roof", "Creates a roof object from the selected wire.")} + return { + "Pixmap": "Arch_Roof", + "MenuText": QT_TRANSLATE_NOOP("Arch_Roof", "Roof"), + "Accel": "R, F", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Roof", "Creates a roof object from the selected wire." + ), + } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") @@ -48,6 +52,7 @@ class Arch_Roof: def Activated(self): import ArchComponent + sel = FreeCADGui.Selection.getSelectionEx() if sel: sel = sel[0] @@ -58,7 +63,13 @@ class Arch_Roof: i = int(sel.SubElementNames[0][4:]) FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Roof")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("obj = Arch.makeRoof(FreeCAD.ActiveDocument." + obj.Name + "," + str(i) + ")") + FreeCADGui.doCommand( + "obj = Arch.makeRoof(FreeCAD.ActiveDocument." + + obj.Name + + "," + + str(i) + + ")" + ) FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() @@ -68,7 +79,9 @@ class Arch_Roof: if obj.Shape.Wires: FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Roof")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("obj = Arch.makeRoof(FreeCAD.ActiveDocument." + obj.Name + ")") + FreeCADGui.doCommand( + "obj = Arch.makeRoof(FreeCAD.ActiveDocument." + obj.Name + ")" + ) FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCAD.ActiveDocument.commitTransaction() @@ -79,7 +92,7 @@ class Arch_Roof: else: FreeCAD.Console.PrintMessage(translate("Arch", "Please select a base object") + "\n") FreeCADGui.Control.showDialog(ArchComponent.SelectionTaskPanel()) - FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(nextCommand = "Arch_Roof") + FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(nextCommand="Arch_Roof") FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) diff --git a/src/Mod/BIM/bimcommands/BimSchedule.py b/src/Mod/BIM/bimcommands/BimSchedule.py index 39eb317b11..ff8051005a 100644 --- a/src/Mod/BIM/bimcommands/BimSchedule.py +++ b/src/Mod/BIM/bimcommands/BimSchedule.py @@ -34,19 +34,23 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Schedule: - "the Arch Schedule command definition" def GetResources(self): - return {'Pixmap': 'Arch_Schedule', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Schedule","Schedule"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Schedule","Creates a schedule to collect data from the model")} + return { + "Pixmap": "Arch_Schedule", + "MenuText": QT_TRANSLATE_NOOP("Arch_Schedule", "Schedule"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Schedule", "Creates a schedule to collect data from the model" + ), + } def Activated(self): - if hasattr(self,"taskd"): + if hasattr(self, "taskd"): if self.taskd: self.taskd.form.hide() import ArchSchedule + self.taskd = ArchSchedule.ArchScheduleTaskPanel() def IsActive(self): @@ -54,4 +58,4 @@ class Arch_Schedule: return v -FreeCADGui.addCommand('Arch_Schedule',Arch_Schedule()) +FreeCADGui.addCommand("Arch_Schedule", Arch_Schedule()) diff --git a/src/Mod/BIM/bimcommands/BimSectionPlane.py b/src/Mod/BIM/bimcommands/BimSectionPlane.py index 16f6a54bee..8a83e6dc64 100644 --- a/src/Mod/BIM/bimcommands/BimSectionPlane.py +++ b/src/Mod/BIM/bimcommands/BimSectionPlane.py @@ -34,15 +34,19 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_SectionPlane: - "the Arch SectionPlane command definition" def GetResources(self): - return {'Pixmap' : 'Arch_SectionPlane', - 'Accel': "S, E", - 'MenuText': QT_TRANSLATE_NOOP("Arch_SectionPlane","Section Plane"), - 'ToolTip': QT_TRANSLATE_NOOP("Arch_SectionPlane","Creates a section plane object, including the selected objects")} + return { + "Pixmap": "Arch_SectionPlane", + "Accel": "S, E", + "MenuText": QT_TRANSLATE_NOOP("Arch_SectionPlane", "Section Plane"), + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_SectionPlane", + "Creates a section plane object, including the selected objects", + ), + } def IsActive(self): @@ -56,20 +60,19 @@ class Arch_SectionPlane: for o in sel: if len(ss) > 1: ss += "," - ss += "FreeCAD.ActiveDocument."+o.Name + ss += "FreeCAD.ActiveDocument." + o.Name ss += "]" - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Section Plane")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Section Plane")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("section = Arch.makeSectionPlane("+ss+")") + FreeCADGui.doCommand("section = Arch.makeSectionPlane(" + ss + ")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() if len(sel) == 1 and getattr(sel[0], "IfcClass", None) == "IfcProject": # remove the IFC project, otherwise we can't aggregate (circular loop) FreeCADGui.doCommand("section.Objects = []") - #FreeCADGui.addModule("nativeifc.ifc_tools") - #p = "FreeCAD.ActiveDocument."+sel[0].Name - #FreeCADGui.doCommand("nativeifc.ifc_tools.aggregate(section,"+p+")") + # FreeCADGui.addModule("nativeifc.ifc_tools") + # p = "FreeCAD.ActiveDocument."+sel[0].Name + # FreeCADGui.doCommand("nativeifc.ifc_tools.aggregate(section,"+p+")") - -FreeCADGui.addCommand('Arch_SectionPlane', Arch_SectionPlane()) +FreeCADGui.addCommand("Arch_SectionPlane", Arch_SectionPlane()) diff --git a/src/Mod/BIM/bimcommands/BimSetup.py b/src/Mod/BIM/bimcommands/BimSetup.py index 0c951d66d5..2bcbe4685f 100644 --- a/src/Mod/BIM/bimcommands/BimSetup.py +++ b/src/Mod/BIM/bimcommands/BimSetup.py @@ -53,9 +53,7 @@ class BIM_Setup: return TARGETVERSION = 0.19 - TECHDRAWDIMFACTOR = ( - 0.16 # How many times TechDraw dim arrows are smaller than Draft - ) + TECHDRAWDIMFACTOR = 0.16 # How many times TechDraw dim arrows are smaller than Draft from PySide import QtGui import WorkingPlane @@ -66,9 +64,7 @@ class BIM_Setup: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) # connect signals / slots @@ -89,9 +85,9 @@ class BIM_Setup: except ImportError: m.append("Reinforcement") # disabled as WebTools can currentyl not be installed because of WebGui dependency - #try: + # try: # import BIMServer - #except ImportError: + # except ImportError: # m.append("WebTools") if sys.version_info.major < 3: try: @@ -118,16 +114,14 @@ class BIM_Setup: else: ifcok = True libok = False - librarypath = FreeCAD.ParamGet( - "User parameter:Plugins/parts_library" - ).GetString("destination", "") + librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString( + "destination", "" + ) if librarypath and os.path.exists(librarypath): libok = True else: # check if the library is at the standard addon location - librarypath = os.path.join( - FreeCAD.getUserAppDataDir(), "Mod", "parts_library" - ) + librarypath = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "parts_library") if os.path.exists(librarypath): FreeCAD.ParamGet("User parameter:Plugins/parts_library").SetString( "destination", librarypath @@ -144,9 +138,7 @@ class BIM_Setup: + " " + ",".join(m) + ". " - + translate( - "BIM", "Install them from menu Tools -> Addon Manager." - ) + + translate("BIM", "Install them from menu Tools -> Addon Manager.") ) self.form.labelMissingWorkbenches.setText(t) self.form.labelMissingWorkbenches.show() @@ -168,9 +160,7 @@ class BIM_Setup: self.form.labelVersion.show() # show dialog and exit if cancelled - FreeCADGui.BIMSetupDialog = ( - True # this is there to be easily detected by the BIM tutorial - ) + FreeCADGui.BIMSetupDialog = True # this is there to be easily detected by the BIM tutorial result = self.form.exec_() del FreeCADGui.BIMSetupDialog if not result: @@ -181,9 +171,7 @@ class BIM_Setup: # set preference values unit = self.form.settingUnits.currentIndex() unit = [0, 4, 1, 3, 7, 5][unit] # less choices in our simplified dialog - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt( - "UserSchema", unit - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt("UserSchema", unit) if FreeCAD.ActiveDocument is not None: docs_dict = FreeCAD.listDocuments() for doc in docs_dict.values(): @@ -199,16 +187,12 @@ class BIM_Setup: if hasattr(FreeCAD.Units, "setSchema"): FreeCAD.Units.setSchema(unit) decimals = self.form.settingDecimals.value() - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt( - "Decimals", decimals + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").SetInt("Decimals", decimals) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions").SetBool( + "UseGlobalDecimals", True ) - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions" - ).SetBool("UseGlobalDecimals", True) grid = self.form.settingGrid.text() - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Sketcher/General" - ).SetString( + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Sketcher/General").SetString( "GridSize", grid ) # Also set sketcher grid FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetString( @@ -219,26 +203,20 @@ class BIM_Setup: "gridEvery", squares ) wp = self.form.settingWP.currentIndex() - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetInt( - "defaultWP", wp - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetInt("defaultWP", wp) tsize = self.form.settingText.text() tsize = FreeCAD.Units.Quantity(tsize).Value FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetFloat( "textheight", tsize ) - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions" - ).SetFloat( + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions").SetFloat( "FontSize", tsize ) # TODO - check if this needs a mult factor? font = self.form.settingFont.currentFont().family() - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetString( - "textfont", font + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetString("textfont", font) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Labels").SetString( + "LabelFont", font ) - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/TechDraw/Labels" - ).SetString("LabelFont", font) linewidth = self.form.settingLinewidth.value() FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").SetInt( "DefaultShapeLineWidth", linewidth @@ -253,17 +231,17 @@ class BIM_Setup: "dimsymbol", ddimstyle ) tdimstyle = [3, 0, 2, 2][dimstyle] # TechDraw has different order than Draft - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions" - ).SetInt("dimsymbol", tdimstyle) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions").SetInt( + "dimsymbol", tdimstyle + ) asize = self.form.settingArrowsize.text() asize = FreeCAD.Units.Quantity(asize).Value FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").SetFloat( "arrowsize", asize ) - FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions" - ).SetFloat("ArrowSize", asize * TECHDRAWDIMFACTOR) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Dimensions").SetFloat( + "ArrowSize", asize * TECHDRAWDIMFACTOR + ) author = self.form.settingAuthor.text() FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document").SetString( "prefAuthor", author @@ -391,9 +369,7 @@ class BIM_Setup: ) # finish - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM").SetBool( - "FirstTime", False - ) + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM").SetBool("FirstTime", False) self.form.hide() del self.form @@ -479,87 +455,80 @@ class BIM_Setup: unit = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( "UserSchema", 0 ) - unit = [0, 2, 3, 3, 1, 5, 0, 4, 0, 2][ - unit - ] # less choices in our simplified dialog - decimals = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Units" - ).GetInt("Decimals", 2) - grid = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetString("gridSpacing", "1 cm") + unit = [0, 2, 3, 3, 1, 5, 0, 4, 0, 2][unit] # less choices in our simplified dialog + decimals = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( + "Decimals", 2 + ) + grid = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString( + "gridSpacing", "1 cm" + ) grid = FreeCAD.Units.Quantity(grid).UserString - squares = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetInt("gridEvery", 10) - wp = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetInt("defaultWP", 1) - tsize = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetFloat("textheight", 10) + squares = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( + "gridEvery", 10 + ) + wp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( + "defaultWP", 1 + ) + tsize = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat( + "textheight", 10 + ) tsize = FreeCAD.Units.Quantity(tsize, FreeCAD.Units.Length).UserString - font = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetString("textfont", "Sans") - linewidth = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetInt("DefaultShapeLineWidth", 2) - dimstyle = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetInt("dimsymbol", 0) - dimstyle = [0, 0, 1, 2, 3][ - dimstyle - ] # less choices in our simplified dialog - asize = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetFloat("arrowsize", 5) + font = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString( + "textfont", "Sans" + ) + linewidth = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetInt( + "DefaultShapeLineWidth", 2 + ) + dimstyle = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( + "dimsymbol", 0 + ) + dimstyle = [0, 0, 1, 2, 3][dimstyle] # less choices in our simplified dialog + asize = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat( + "arrowsize", 5 + ) asize = FreeCAD.Units.Quantity(asize, FreeCAD.Units.Length).UserString - author = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Document" - ).GetString("prefAuthor", "") - lic = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Document" - ).GetInt("prefLicenseType", 0) - lic = [0, - 1, 2, 1, 3, 4, 1, - 1, 2, 1, 3, 4, 1, - 0, 0, 0, 0, 0, 0][ + author = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document").GetString( + "prefAuthor", "" + ) + lic = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document").GetInt( + "prefLicenseType", 0 + ) + lic = [0, 1, 2, 1, 3, 4, 1, 1, 2, 1, 3, 4, 1, 0, 0, 0, 0, 0, 0][ lic ] # less choices in our simplified dialog - newdoc = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Document" - ).GetBool("CreateNewDoc", False) - bkp = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Document" - ).GetInt("CountBackupFiles", 2) - colTop = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetUnsigned("BackgroundColor2", 775244287) - colBottom = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetUnsigned("BackgroundColor3", 1905041919) - colFace = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetUnsigned("DefaultShapeColor", 4294967295) - colLine = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetUnsigned("DefaultShapeLineColor", 255) - colHelp = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Arch" - ).GetUnsigned("ColorHelpers", 674321151) - colConst = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetUnsigned("constructioncolor", 746455039) - height = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetInt("defaultCameraHeight", 5) - colSimple = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/View" - ).GetUnsigned("BackgroundColor", 4294967295) - colText = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/Draft" - ).GetUnsigned("DefaultTextColor", 255) + newdoc = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document").GetBool( + "CreateNewDoc", False + ) + bkp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document").GetInt( + "CountBackupFiles", 2 + ) + colTop = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned( + "BackgroundColor2", 775244287 + ) + colBottom = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned( + "BackgroundColor3", 1905041919 + ) + colFace = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned( + "DefaultShapeColor", 4294967295 + ) + colLine = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned( + "DefaultShapeLineColor", 255 + ) + colHelp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetUnsigned( + "ColorHelpers", 674321151 + ) + colConst = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetUnsigned( + "constructioncolor", 746455039 + ) + height = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt( + "defaultCameraHeight", 5 + ) + colSimple = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned( + "BackgroundColor", 4294967295 + ) + colText = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetUnsigned( + "DefaultTextColor", 255 + ) if unit != None: self.form.settingUnits.setCurrentIndex(unit) @@ -600,9 +569,7 @@ class BIM_Setup: if colHelp != None: self.form.colorButtonHelpers.setProperty("color", self.getPrefColor(colHelp)) if colConst != None: - self.form.colorButtonConstruction.setProperty( - "color", self.getPrefColor(colConst) - ) + self.form.colorButtonConstruction.setProperty("color", self.getPrefColor(colConst)) if colSimple != None: self.form.colorButtonSimple.setProperty("color", self.getPrefColor(colSimple)) if colText != None: @@ -622,8 +589,7 @@ class BIM_Setup: url = QtCore.QUrl(link) QtGui.QDesktopServices.openUrl(url) - - def getPrefColor(self,color): + def getPrefColor(self, color): r = ((color >> 24) & 0xFF) / 255.0 g = ((color >> 16) & 0xFF) / 255.0 b = ((color >> 8) & 0xFF) / 255.0 @@ -631,8 +597,7 @@ class BIM_Setup: return QtGui.QColor.fromRgbF(r, g, b) - - def getIfcOpenShell(self,force=False): + def getIfcOpenShell(self, force=False): """downloads and installs IfcOpenShell""" # TODO WARNING the IfcOpenBot repo below is not actively kept updated. @@ -683,11 +648,7 @@ class BIM_Setup: d = json.loads(r) l = d[-1]["body"] links = re.findall(r"http.*?zip", l) - pyv = ( - "python-" - + str(sys.version_info.major) - + str(sys.version_info.minor) - ) + pyv = "python-" + str(sys.version_info.major) + str(sys.version_info.minor) if sys.platform.startswith("linux"): plat = "linux" elif sys.platform.startswith("win"): @@ -742,6 +703,7 @@ class BIM_Setup: return from nativeifc import ifc_openshell - FreeCADGui.runCommand('IFC_UpdateIOS',1) + FreeCADGui.runCommand("IFC_UpdateIOS", 1) + FreeCADGui.addCommand("BIM_Setup", BIM_Setup()) diff --git a/src/Mod/BIM/bimcommands/BimShape2DView.py b/src/Mod/BIM/bimcommands/BimShape2DView.py index 02239b92e1..3535206242 100644 --- a/src/Mod/BIM/bimcommands/BimShape2DView.py +++ b/src/Mod/BIM/bimcommands/BimShape2DView.py @@ -39,7 +39,7 @@ class BIM_Shape2DView(gui_shape2dview.Shape2DView): d = super().GetResources() d["Pixmap"] = "Arch_BuildingPart_Tree" d["MenuText"] = QT_TRANSLATE_NOOP("BIM_Shape2DView", "Section View") - d['Accel'] = "V, V" + d["Accel"] = "V, V" return d def proceed(self): @@ -48,14 +48,14 @@ class BIM_Shape2DView(gui_shape2dview.Shape2DView): if self.commitlist_BimShape2DView: commitlist = self.commitlist_BimShape2DView commitlist.append("FreeCAD.ActiveDocument.recompute()") - self.commit(translate("draft", "Create 2D view"), - self.commitlist_BimShape2DView) + self.commit(translate("draft", "Create 2D view"), self.commitlist_BimShape2DView) self.finish() def proceed_BimShape2DView(self): # Common """Proceed with the command if one object was selected.""" # difference from Draft: it sets InPlace to False import DraftVecUtils + if self.call is not None: self.end_callbacks(self.call) faces = [] @@ -79,8 +79,8 @@ class BIM_Shape2DView(gui_shape2dview.Shape2DView): _cmd += DraftVecUtils.toString(vec) + ", " _cmd += "facenumbers=" + str(faces) _cmd += ")" - #commitlist.append("sv = " + _cmd) - #commitlist.append("sv.InPlace = False") + # commitlist.append("sv = " + _cmd) + # commitlist.append("sv.InPlace = False") commitlist.append("sv0 = " + _cmd) commitlist.append("sv0.InPlace = False") else: @@ -95,14 +95,14 @@ class BIM_Shape2DView(gui_shape2dview.Shape2DView): commitlist.append("sv" + str(n) + ".InPlace = False") n += 1 if commitlist: - #commitlist.append("FreeCAD.ActiveDocument.recompute()") + # commitlist.append("FreeCAD.ActiveDocument.recompute()") self.commitlist_BimShape2DView = commitlist else: self.commitlist_BimShape2DView = None # self.commit(translate("draft", "Create 2D view"), # commitlist) - #self.finish() + # self.finish() class BIM_Shape2DCut(BIM_Shape2DView): @@ -111,13 +111,13 @@ class BIM_Shape2DCut(BIM_Shape2DView): d = super().GetResources() d["Pixmap"] = "Arch_View_Cut" d["MenuText"] = QT_TRANSLATE_NOOP("BIM_Shape2DView", "Section Cut") - d['Accel'] = "V, C" + d["Accel"] = "V, C" return d def proceed(self): - #super().proceed() + # super().proceed() #'sv' is not passed from BIM_Shape2DView() to BIM_Shape2DCut() - #FreeCADGui.doCommand("sv.ProjectionMode = \"Cutfaces\"") + # FreeCADGui.doCommand("sv.ProjectionMode = \"Cutfaces\"") """Proceed with the command if one object was selected.""" self.proceed_BimShape2DView() # Common @@ -125,15 +125,14 @@ class BIM_Shape2DCut(BIM_Shape2DView): commitlist = self.commitlist_BimShape2DView # BIM_Shape2DCut specific - #commitlist.append("sv.ProjectionMode = \"Cutfaces\"") + # commitlist.append("sv.ProjectionMode = \"Cutfaces\"") objs = self.objs_BimShape2DView n = 0 for o in objs: - commitlist.append("sv" + str(n) + ".ProjectionMode = \"Cutfaces\"") + commitlist.append("sv" + str(n) + '.ProjectionMode = "Cutfaces"') n += 1 commitlist.append("FreeCAD.ActiveDocument.recompute()") - self.commit(translate("draft", "Create 2D Cut"), - commitlist) + self.commit(translate("draft", "Create 2D Cut"), commitlist) self.finish() diff --git a/src/Mod/BIM/bimcommands/BimSimpleCopy.py b/src/Mod/BIM/bimcommands/BimSimpleCopy.py index d9e2d95488..46fd6bf038 100644 --- a/src/Mod/BIM/bimcommands/BimSimpleCopy.py +++ b/src/Mod/BIM/bimcommands/BimSimpleCopy.py @@ -36,9 +36,7 @@ class BIM_SimpleCopy: return { "Pixmap": "Tree_Part", "MenuText": QT_TRANSLATE_NOOP("BIM_SimpleCopy", "Create Simple Copy"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_SimpleCopy", "Creates a simple non-parametric copy" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_SimpleCopy", "Creates a simple non-parametric copy"), } def IsActive(self): diff --git a/src/Mod/BIM/bimcommands/BimSite.py b/src/Mod/BIM/bimcommands/BimSite.py index bea052c76e..e1b53fcf81 100644 --- a/src/Mod/BIM/bimcommands/BimSite.py +++ b/src/Mod/BIM/bimcommands/BimSite.py @@ -37,15 +37,16 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Site: - "the Arch Site command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Site', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Site","Site"), - 'Accel': "S, I", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Site","Creates a site including selected objects")} + return { + "Pixmap": "Arch_Site", + "MenuText": QT_TRANSLATE_NOOP("Arch_Site", "Site"), + "Accel": "S, I", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Site", "Creates a site including selected objects"), + } def IsActive(self): @@ -54,7 +55,7 @@ class Arch_Site: def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Site")) + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Site")) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") FreeCADGui.doCommand("obj = Arch.makeSite()") @@ -63,4 +64,4 @@ class Arch_Site: FreeCAD.ActiveDocument.recompute() -FreeCADGui.addCommand('Arch_Site', Arch_Site()) +FreeCADGui.addCommand("Arch_Site", Arch_Site()) diff --git a/src/Mod/BIM/bimcommands/BimSketch.py b/src/Mod/BIM/bimcommands/BimSketch.py index 21247a33c5..fcc38849ff 100644 --- a/src/Mod/BIM/bimcommands/BimSketch.py +++ b/src/Mod/BIM/bimcommands/BimSketch.py @@ -49,6 +49,7 @@ class BIM_Sketch: def Activated(self): import WorkingPlane from draftutils import params + issnap = False if hasattr(FreeCAD, "DraftWorkingPlane"): FreeCAD.DraftWorkingPlane.setup() diff --git a/src/Mod/BIM/bimcommands/BimSlab.py b/src/Mod/BIM/bimcommands/BimSlab.py index 7cb936dc4a..e4224f0b52 100644 --- a/src/Mod/BIM/bimcommands/BimSlab.py +++ b/src/Mod/BIM/bimcommands/BimSlab.py @@ -41,9 +41,7 @@ class BIM_Slab: return { "Pixmap": "BIM_Slab", "MenuText": QT_TRANSLATE_NOOP("BIM_Slab", "Slab"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Slab", "Creates a slab from a planar shape" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Slab", "Creates a slab from a planar shape"), "Accel": "S,B", } @@ -61,14 +59,10 @@ class BIM_Slab: else: if hasattr(FreeCADGui, "draftToolBar"): FreeCADGui.draftToolBar.selectUi() - FreeCAD.Console.PrintMessage( - translate("BIM", "Select a planar object") + "\n" - ) + FreeCAD.Console.PrintMessage(translate("BIM", "Select a planar object") + "\n") FreeCAD.activeDraftCommand = self self.view = FreeCADGui.ActiveDocument.ActiveView - self.callback = self.view.addEventCallback( - "SoEvent", DraftTools.selectObject - ) + self.callback = self.view.addEventCallback("SoEvent", DraftTools.selectObject) def proceed(self): self.removeCallback() @@ -77,9 +71,7 @@ class BIM_Slab: FreeCADGui.addModule("Arch") FreeCAD.ActiveDocument.openTransaction("Create Slab") FreeCADGui.doCommand( - "s = Arch.makeStructure(FreeCAD.ActiveDocument." - + sel[0].Name - + ",height=200)" + "s = Arch.makeStructure(FreeCAD.ActiveDocument." + sel[0].Name + ",height=200)" ) FreeCADGui.doCommand('s.Label = "' + translate("BIM", "Slab") + '"') FreeCADGui.doCommand('s.IfcType = "Slab"') @@ -102,4 +94,4 @@ class BIM_Slab: FreeCADGui.draftToolBar.offUi() -FreeCADGui.addCommand('BIM_Slab', BIM_Slab()) +FreeCADGui.addCommand("BIM_Slab", BIM_Slab()) diff --git a/src/Mod/BIM/bimcommands/BimSpace.py b/src/Mod/BIM/bimcommands/BimSpace.py index 8ecbfa2619..1b770fde9e 100644 --- a/src/Mod/BIM/bimcommands/BimSpace.py +++ b/src/Mod/BIM/bimcommands/BimSpace.py @@ -34,15 +34,18 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Space: - "the Arch Space command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Space', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Space","Space"), - 'Accel': "S, A", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Space","Creates a space object from selected boundary objects")} + return { + "Pixmap": "Arch_Space", + "MenuText": QT_TRANSLATE_NOOP("Arch_Space", "Space"), + "Accel": "S, A", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Space", "Creates a space object from selected boundary objects" + ), + } def IsActive(self): @@ -52,7 +55,8 @@ class Arch_Space: def Activated(self): import ArchComponent - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Space")) + + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Space")) FreeCADGui.addModule("Arch") sel = FreeCADGui.Selection.getSelection() if sel: @@ -63,10 +67,10 @@ class Arch_Space: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() else: - FreeCAD.Console.PrintMessage(translate("Arch","Please select a base object")+"\n") + FreeCAD.Console.PrintMessage(translate("Arch", "Please select a base object") + "\n") FreeCADGui.Control.showDialog(ArchComponent.SelectionTaskPanel()) FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(nextCommand="Arch_Space") FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) -FreeCADGui.addCommand('Arch_Space', Arch_Space()) +FreeCADGui.addCommand("Arch_Space", Arch_Space()) diff --git a/src/Mod/BIM/bimcommands/BimStairs.py b/src/Mod/BIM/bimcommands/BimStairs.py index bafcc31573..b3bab916ca 100644 --- a/src/Mod/BIM/bimcommands/BimStairs.py +++ b/src/Mod/BIM/bimcommands/BimStairs.py @@ -34,15 +34,16 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Stairs: - "the Arch Stairs command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Stairs', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Stairs","Stairs"), - 'Accel': "S, R", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Stairs","Creates a flight of stairs")} + return { + "Pixmap": "Arch_Stairs", + "MenuText": QT_TRANSLATE_NOOP("Arch_Stairs", "Stairs"), + "Accel": "S, R", + "ToolTip": QT_TRANSLATE_NOOP("Arch_Stairs", "Creates a flight of stairs"), + } def IsActive(self): @@ -53,7 +54,8 @@ class Arch_Stairs: import Draft from draftutils import params - FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Stairs")) + + FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Stairs")) FreeCADGui.addModule("Arch") sel = FreeCADGui.Selection.getSelection() if sel: @@ -65,13 +67,19 @@ class Arch_Stairs: nStr += "FreeCAD.ActiveDocument." + obj.Name #'obj' in GUI not the same as obj in script, # make it 'stairs' to distinguish one from another - #Create Stairs object with steps numbers in user preference - FreeCADGui.doCommand("stairs = Arch.makeStairs(baseobj=["+nStr+"],steps="+str(params.get_param_arch("StairsSteps"))+")") + # Create Stairs object with steps numbers in user preference + FreeCADGui.doCommand( + "stairs = Arch.makeStairs(baseobj=[" + + nStr + + "],steps=" + + str(params.get_param_arch("StairsSteps")) + + ")" + ) FreeCADGui.Selection.clearSelection() FreeCADGui.doCommand("FreeCADGui.Selection.addSelection(stairs)") - #ArchSketch Support - if len(sel)==1 and Draft.getType(obj)=="ArchSketch": + # ArchSketch Support + if len(sel) == 1 and Draft.getType(obj) == "ArchSketch": # Get ArchSketch.FloorHeight as default and assign to Stairs try: height = str(obj.FloorHeight.Value) # vs obj.FloorHeight @@ -79,18 +87,20 @@ class Arch_Stairs: FreeCADGui.doCommand("stairs.Height = " + height) except: pass - #If base is ArchSketch, ArchSketchObject is already loaded, no - #need to load again : FreeCADGui.addModule("ArchSketchObject") + # If base is ArchSketch, ArchSketchObject is already loaded, no + # need to load again : FreeCADGui.addModule("ArchSketchObject") try: FreeCADGui.runCommand("EditStairs") except: pass else: - FreeCADGui.doCommand("stairs = Arch.makeStairs(steps="+str(params.get_param_arch("StairsSteps"))+")") + FreeCADGui.doCommand( + "stairs = Arch.makeStairs(steps=" + str(params.get_param_arch("StairsSteps")) + ")" + ) FreeCADGui.addModule("Draft") - #FreeCADGui.doCommand("Draft.autogroup(obj)") + # FreeCADGui.doCommand("Draft.autogroup(obj)") FreeCADGui.doCommand("Draft.autogroup(stairs)") FreeCAD.ActiveDocument.commitTransaction() @@ -98,4 +108,4 @@ class Arch_Stairs: print(" ActiveDocument.recompute, done ") -FreeCADGui.addCommand('Arch_Stairs', Arch_Stairs()) +FreeCADGui.addCommand("Arch_Stairs", Arch_Stairs()) diff --git a/src/Mod/BIM/bimcommands/BimTDPage.py b/src/Mod/BIM/bimcommands/BimTDPage.py index 5c6993bc80..cf3beeecb0 100644 --- a/src/Mod/BIM/bimcommands/BimTDPage.py +++ b/src/Mod/BIM/bimcommands/BimTDPage.py @@ -41,7 +41,7 @@ class BIM_TDPage: "ToolTip": QT_TRANSLATE_NOOP( "BIM_TDPage", "Creates a new TechDraw page from a template" ), - 'Accel': "T, P", + "Accel": "T, P", } def IsActive(self): @@ -52,9 +52,9 @@ class BIM_TDPage: from PySide import QtGui import TechDraw - templatedir = FreeCAD.ParamGet( - "User parameter:BaseApp/Preferences/Mod/BIM" - ).GetString("TDTemplateDir", "") + templatedir = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM").GetString( + "TDTemplateDir", "" + ) if not templatedir: templatedir = None filename, _ = QtGui.QFileDialog.getOpenFileName( @@ -68,9 +68,7 @@ class BIM_TDPage: FreeCAD.ActiveDocument.openTransaction("Create page") page = FreeCAD.ActiveDocument.addObject("TechDraw::DrawPage", "Page") page.Label = name - template = FreeCAD.ActiveDocument.addObject( - "TechDraw::DrawSVGTemplate", "Template" - ) + template = FreeCAD.ActiveDocument.addObject("TechDraw::DrawSVGTemplate", "Template") template.Template = filename template.Label = translate("BIM", "Template") page.Template = template diff --git a/src/Mod/BIM/bimcommands/BimTDView.py b/src/Mod/BIM/bimcommands/BimTDView.py index 3d898e5884..e81fc0ef17 100644 --- a/src/Mod/BIM/bimcommands/BimTDView.py +++ b/src/Mod/BIM/bimcommands/BimTDView.py @@ -42,7 +42,7 @@ class BIM_TDView: "To choose where to insert the view when multiple pages are available,\n" "select both the view and the page before executing the command.", ), - 'Accel': "V, I", + "Accel": "V, I", } def IsActive(self): @@ -51,6 +51,7 @@ class BIM_TDView: def Activated(self): import Draft + sections = [] page = None drafts = [] @@ -77,18 +78,14 @@ class BIM_TDView: return FreeCAD.ActiveDocument.openTransaction("Create view") for section in sections: - view = FreeCAD.ActiveDocument.addObject( - "TechDraw::DrawViewArch", "BIM view" - ) + view = FreeCAD.ActiveDocument.addObject("TechDraw::DrawViewArch", "BIM view") view.Label = section.Label view.Source = section page.addView(view) if page.Scale: view.Scale = page.Scale for draft in drafts: - view = FreeCAD.ActiveDocument.addObject( - "TechDraw::DrawViewDraft", "DraftView" - ) + view = FreeCAD.ActiveDocument.addObject("TechDraw::DrawViewDraft", "DraftView") view.Label = draft.Label view.Source = draft page.addView(view) diff --git a/src/Mod/BIM/bimcommands/BimText.py b/src/Mod/BIM/bimcommands/BimText.py index 5a2d1b23d1..b541229770 100644 --- a/src/Mod/BIM/bimcommands/BimText.py +++ b/src/Mod/BIM/bimcommands/BimText.py @@ -65,9 +65,7 @@ class BIM_Text: pagescale = page.Scale if not pagescale: pagescale = 1 - anno = FreeCAD.ActiveDocument.addObject( - "TechDraw::DrawViewAnnotation", "Annotation" - ) + anno = FreeCAD.ActiveDocument.addObject("TechDraw::DrawViewAnnotation", "Annotation") anno.Text = self.text page.addView(anno) param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") diff --git a/src/Mod/BIM/bimcommands/BimTogglePanels.py b/src/Mod/BIM/bimcommands/BimTogglePanels.py index 05c03dad31..2f19431549 100644 --- a/src/Mod/BIM/bimcommands/BimTogglePanels.py +++ b/src/Mod/BIM/bimcommands/BimTogglePanels.py @@ -38,9 +38,7 @@ class BIM_TogglePanels: return { "Pixmap": "BIM_TogglePanels", "MenuText": QT_TRANSLATE_NOOP("BIM_TogglePanels", "Toggle Bottom Panels"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_TogglePanels", "Toggles bottom dock panels on/off" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_TogglePanels", "Toggles bottom dock panels on/off"), "Accel": "Ctrl+0", } @@ -58,10 +56,7 @@ class BIM_TogglePanels: bottomwidgets = [ w for w in dockwidgets - if ( - (mw.dockWidgetArea(w) == QtCore.Qt.BottomDockWidgetArea) - and w.isVisible() - ) + if ((mw.dockWidgetArea(w) == QtCore.Qt.BottomDockWidgetArea) and w.isVisible()) ] if bottomwidgets: hidden = "" @@ -72,8 +67,8 @@ class BIM_TogglePanels: if togglebutton: togglebutton.setChecked(False) else: - widgets = PARAMS.GetString("HiddenWidgets", - "Python console;;Report view;;Selection view;;" + widgets = PARAMS.GetString( + "HiddenWidgets", "Python console;;Report view;;Selection view;;" ) widgets = [mw.findChild(QtGui.QWidget, w) for w in widgets.split(";;") if w] for w in widgets: diff --git a/src/Mod/BIM/bimcommands/BimTrash.py b/src/Mod/BIM/bimcommands/BimTrash.py index ddec6525af..dc2373b8c6 100644 --- a/src/Mod/BIM/bimcommands/BimTrash.py +++ b/src/Mod/BIM/bimcommands/BimTrash.py @@ -47,9 +47,7 @@ class BIM_Trash: if FreeCADGui.Selection.getSelection(): trash = FreeCAD.ActiveDocument.getObject("Trash") if not trash or not trash.isDerivedFrom("App::DocumentObjectGroup"): - trash = FreeCAD.ActiveDocument.addObject( - "App::DocumentObjectGroup", "Trash" - ) + trash = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Trash") trash.Label = translate("BIM", "Trash") for obj in FreeCADGui.Selection.getSelection(): trash.addObject(obj) @@ -69,6 +67,7 @@ class BIM_Trash: v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v + FreeCADGui.addCommand("BIM_Trash", BIM_Trash()) diff --git a/src/Mod/BIM/bimcommands/BimTruss.py b/src/Mod/BIM/bimcommands/BimTruss.py index 5a2595e232..14d5fca83a 100644 --- a/src/Mod/BIM/bimcommands/BimTruss.py +++ b/src/Mod/BIM/bimcommands/BimTruss.py @@ -34,15 +34,18 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") class Arch_Truss: - "the Arch Truss command definition" def GetResources(self): - return {'Pixmap' : 'Arch_Truss', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Truss","Truss"), - 'Accel': "T, U", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Truss","Creates a truss object from the selected line or from scratch")} + return { + "Pixmap": "Arch_Truss", + "MenuText": QT_TRANSLATE_NOOP("Arch_Truss", "Truss"), + "Accel": "T, U", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Truss", "Creates a truss object from the selected line or from scratch" + ), + } def IsActive(self): @@ -54,10 +57,12 @@ class Arch_Truss: self.doc = FreeCAD.ActiveDocument sel = FreeCADGui.Selection.getSelection() if len(sel) > 1: - FreeCAD.Console.PrintError(translate("Arch","Select only one base object or none")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Select only one base object or none") + "\n" + ) elif len(sel) == 1: # build on selection - basename = "FreeCAD.ActiveDocument."+FreeCADGui.Selection.getSelection()[0].Name + basename = "FreeCAD.ActiveDocument." + FreeCADGui.Selection.getSelection()[0].Name self.createTruss(basename) else: # interactive line drawing @@ -66,11 +71,10 @@ class Arch_Truss: FreeCAD.activeDraftCommand = self # register as a Draft command for auto grid on/off self.points = [] WorkingPlane.get_working_plane() - if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui, "Snapper"): FreeCADGui.Snapper.getPoint(callback=self.getPoint) - def getPoint(self,point=None,obj=None): - + def getPoint(self, point=None, obj=None): """Callback for clicks during interactive mode""" if point is None: @@ -80,30 +84,29 @@ class Arch_Truss: return self.points.append(point) if len(self.points) == 1: - FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint) + FreeCADGui.Snapper.getPoint(last=self.points[0], callback=self.getPoint) elif len(self.points) == 2: FreeCAD.activeDraftCommand = None FreeCADGui.Snapper.off() self.createTruss() def createTruss(self, basename=""): - """Creates the truss""" FreeCADGui.Control.closeDialog() - self.doc.openTransaction(translate("Arch","Create Truss")) + self.doc.openTransaction(translate("Arch", "Create Truss")) FreeCADGui.addModule("Draft") FreeCADGui.addModule("Arch") if not basename: if self.points: cmd = "base = Draft.makeLine(FreeCAD." - cmd += str(self.points[0])+",FreeCAD."+str(self.points[1])+")" + cmd += str(self.points[0]) + ",FreeCAD." + str(self.points[1]) + ")" FreeCADGui.doCommand(cmd) basename = "base" - FreeCADGui.doCommand("obj = Arch.makeTruss("+basename+")") + FreeCADGui.doCommand("obj = Arch.makeTruss(" + basename + ")") FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() self.doc.recompute() -FreeCADGui.addCommand('Arch_Truss', Arch_Truss()) +FreeCADGui.addCommand("Arch_Truss", Arch_Truss()) diff --git a/src/Mod/BIM/bimcommands/BimTutorial.py b/src/Mod/BIM/bimcommands/BimTutorial.py index 582dc38db5..6a8c5469a9 100644 --- a/src/Mod/BIM/bimcommands/BimTutorial.py +++ b/src/Mod/BIM/bimcommands/BimTutorial.py @@ -109,9 +109,7 @@ class BIM_Tutorial: return # load tutorial from wiki - offlineloc = os.path.join( - FreeCAD.getUserAppDataDir(), "BIM", "Tutorial", "Tutorial.html" - ) + offlineloc = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Tutorial", "Tutorial.html") try: u = urllib2.urlopen(URL) html = u.read() @@ -119,9 +117,7 @@ class BIM_Tutorial: html = html.decode("utf8") html = html.replace("\n", " ") html = html.replace('href="/', 'href="https://wiki.freecad.org/') - html = re.sub( - '
', "", html - ) # remove table of contents + html = re.sub('
', "", html) # remove table of contents u.close() except: # unable to load tutorial. Look for offline version @@ -195,9 +191,7 @@ class BIM_Tutorial: u.close() # descr = descr.replace(path,"file://"+storename.replace("\\","/")) # fix for windows - seems to work everywhere else too... - descr = descr.replace( - path, "file:///" + storename.replace("\\", "/") - ) + descr = descr.replace(path, "file:///" + storename.replace("\\", "/")) nd.append(descr) self.descriptions = nd @@ -249,11 +243,7 @@ class BIM_Tutorial: else: self.form.labelTasks.hide() self.dock.setWindowTitle( - translate("BIM", "BIM Tutorial - step") - + " " - + str(self.step) - + " / " - + str(self.steps) + translate("BIM", "BIM Tutorial - step") + " " + str(self.step) + " / " + str(self.steps) ) self.form.progressBar.setValue(int((float(self.step) / self.steps) * 100)) diff --git a/src/Mod/BIM/bimcommands/BimUnclone.py b/src/Mod/BIM/bimcommands/BimUnclone.py index 37a76d2405..ea43b261ef 100644 --- a/src/Mod/BIM/bimcommands/BimUnclone.py +++ b/src/Mod/BIM/bimcommands/BimUnclone.py @@ -138,9 +138,7 @@ class BIM_Unclone: translate("BIM", "The selected object is not a clone") + "\n" ) else: - FreeCAD.Console.PrintError( - translate("BIM", "Select exactly one object") + "\n" - ) + FreeCAD.Console.PrintError(translate("BIM", "Select exactly one object") + "\n") FreeCADGui.addCommand("BIM_Unclone", BIM_Unclone()) diff --git a/src/Mod/BIM/bimcommands/BimUngroup.py b/src/Mod/BIM/bimcommands/BimUngroup.py index 78bf4bb8db..5f924dfdc7 100644 --- a/src/Mod/BIM/bimcommands/BimUngroup.py +++ b/src/Mod/BIM/bimcommands/BimUngroup.py @@ -47,9 +47,9 @@ class BIM_Ungroup: if sel: for obj in sel: for parent in obj.InList: - if parent.isDerivedFrom( - "App::DocumentObjectGroup" - ) or parent.hasExtension("App::GroupExtension"): + if parent.isDerivedFrom("App::DocumentObjectGroup") or parent.hasExtension( + "App::GroupExtension" + ): if obj in parent.Group: if first: FreeCAD.ActiveDocument.openTransaction("Ungroup") diff --git a/src/Mod/BIM/bimcommands/BimViews.py b/src/Mod/BIM/bimcommands/BimViews.py index 0ccebeadb9..a1452c64e3 100644 --- a/src/Mod/BIM/bimcommands/BimViews.py +++ b/src/Mod/BIM/bimcommands/BimViews.py @@ -42,9 +42,7 @@ class BIM_Views: return { "Pixmap": "BIM_Views", "MenuText": QT_TRANSLATE_NOOP("BIM_Views", "Views Manager"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Views", "Shows or hides the views manager" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Views", "Shows or hides the views manager"), "Accel": "Ctrl+9", } @@ -87,14 +85,16 @@ class BIM_Views: # set button self.dialog.menu = QtGui.QMenu() - for button in [("Active", translate("BIM","Active")), - ("AddLevel", translate("BIM","New Level")), - ("AddProxy", translate("BIM","New Working Plane Proxy")), - ("Delete", translate("BIM","Delete")), - ("Toggle", translate("BIM","Toggle Visibility")), - ("Isolate", translate("BIM","Isolate")), - ("SaveView", translate("BIM","Save View Position")), - ("Rename", translate("BIM","Rename"))]: + for button in [ + ("Active", translate("BIM", "Active")), + ("AddLevel", translate("BIM", "New Level")), + ("AddProxy", translate("BIM", "New Working Plane Proxy")), + ("Delete", translate("BIM", "Delete")), + ("Toggle", translate("BIM", "Toggle Visibility")), + ("Isolate", translate("BIM", "Isolate")), + ("SaveView", translate("BIM", "Save View Position")), + ("Rename", translate("BIM", "Rename")), + ]: action = QtGui.QAction(button[1]) # Make the "Activate" button bold, as this is the default one @@ -105,7 +105,7 @@ class BIM_Views: action.setCheckable(True) self.dialog.menu.addAction(action) - setattr(self.dialog,"button"+button[0], action) + setattr(self.dialog, "button" + button[0], action) # # set button icons self.dialog.buttonAddLevel.setIcon(QtGui.QIcon(":/icons/Arch_Floor_Tree.svg")) @@ -114,19 +114,25 @@ class BIM_Views: self.dialog.buttonToggle.setIcon(QtGui.QIcon(":/icons/dagViewVisible.svg")) self.dialog.buttonIsolate.setIcon(QtGui.QIcon(":/icons/Std_ShowSelection.svg")) self.dialog.buttonSaveView.setIcon(QtGui.QIcon(":/icons/Std_ViewScreenShot.svg")) - self.dialog.buttonRename.setIcon( - QtGui.QIcon(":/icons/edit-edit.svg") - ) + self.dialog.buttonRename.setIcon(QtGui.QIcon(":/icons/edit-edit.svg")) # set tooltips - self.dialog.buttonAddLevel.setToolTip(translate("BIM","Creates a new level")) - self.dialog.buttonAddProxy.setToolTip(translate("BIM","Creates a new working plane proxy")) - self.dialog.buttonDelete.setToolTip(translate("BIM","Deletes the selected item")) - self.dialog.buttonToggle.setToolTip(translate("BIM","Toggles the visibility of selected items")) - self.dialog.buttonIsolate.setToolTip(translate("BIM","Turns all items off except the selected ones")) - self.dialog.buttonSaveView.setToolTip(translate("BIM","Saves the current camera position to the selected items")) - self.dialog.buttonRename.setToolTip(translate("BIM","Renames the selected item")) - self.dialog.buttonActive.setToolTip(translate("BIM","Activates the selected item")) + self.dialog.buttonAddLevel.setToolTip(translate("BIM", "Creates a new level")) + self.dialog.buttonAddProxy.setToolTip( + translate("BIM", "Creates a new working plane proxy") + ) + self.dialog.buttonDelete.setToolTip(translate("BIM", "Deletes the selected item")) + self.dialog.buttonToggle.setToolTip( + translate("BIM", "Toggles the visibility of selected items") + ) + self.dialog.buttonIsolate.setToolTip( + translate("BIM", "Turns all items off except the selected ones") + ) + self.dialog.buttonSaveView.setToolTip( + translate("BIM", "Saves the current camera position to the selected items") + ) + self.dialog.buttonRename.setToolTip(translate("BIM", "Renames the selected item")) + self.dialog.buttonActive.setToolTip(translate("BIM", "Activates the selected item")) # connect signals self.dialog.buttonAddLevel.triggered.connect(self.addLevel) @@ -194,11 +200,13 @@ class BIM_Views: def _treeToStringList(self, treeViewItems): "generates a (nested) string list representation of treeViewItems" + def _toStringList(itm): children = [] for i in range(itm.childCount()): children.append(_toStringList(itm.child(i))) return [itm.toolTip(0), itm.text(0), itm.text(1), children] + return [_toStringList(itm) for itm in treeViewItems] def update(self, retrigger=True): @@ -249,10 +257,7 @@ class BIM_Views: subSubObjs = subObj.Group # find every working plane proxy belongs to the level for subSubObj in subSubObjs: - if ( - Draft.getType(subSubObj) - == "WorkingPlaneProxy" - ): + if Draft.getType(subSubObj) == "WorkingPlaneProxy": wp, _ = getTreeViewItem(subSubObj) lv.addChild(wp) lvHold.append((lv, lvH)) @@ -268,8 +273,7 @@ class BIM_Views: or getattr(obj, "IfcType", "") == "Building Storey" ): if ( - Draft.getType(getParent(obj)) - in ["Building", "IfcBuilding"] + Draft.getType(getParent(obj)) in ["Building", "IfcBuilding"] or getattr(getParent(obj), "IfcType", "") == "Building" ): continue @@ -282,10 +286,7 @@ class BIM_Views: lv.addChild(wp) lvHold.append((lv, lvH)) if obj and (t == "WorkingPlaneProxy"): - if ( - obj.getParent() - and obj.getParent().IfcType == "Building Storey" - ): + if obj.getParent() and obj.getParent().IfcType == "Building Storey": continue wp, _ = getTreeViewItem(obj) soloProxyHold.append(wp) @@ -305,7 +306,7 @@ class BIM_Views: views = self.getViews() if views: - top = QtGui.QTreeWidgetItem([translate("BIM","2D Views"), ""]) + top = QtGui.QTreeWidgetItem([translate("BIM", "2D Views"), ""]) top.setIcon(0, ficon) for v in views: if hasattr(v, "Label"): @@ -318,12 +319,12 @@ class BIM_Views: pages = self.getPages() if pages: - top = QtGui.QTreeWidgetItem([translate("BIM","Sheets"), ""]) + top = QtGui.QTreeWidgetItem([translate("BIM", "Sheets"), ""]) top.setIcon(0, ficon) for p in pages: i = QtGui.QTreeWidgetItem([p.Label, ""]) if hasattr(p.ViewObject, "Icon"): - i.setIcon(0, p.ViewObject.Icon) + i.setIcon(0, p.ViewObject.Icon) i.setToolTip(0, p.Name) top.addChild(i) treeViewItems.append(top) @@ -353,7 +354,7 @@ class BIM_Views: item.setSelected(item.toolTip(0) in objNameSelected) if objActive and item.toolTip(0) == objActive.Name: tparam = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/TreeView") - activeColor = tparam.GetUnsigned("TreeActiveColor",0) + activeColor = tparam.GetUnsigned("TreeActiveColor", 0) if activeColor: r = ((activeColor >> 24) & 0xFF) / 255.0 g = ((activeColor >> 16) & 0xFF) / 255.0 @@ -435,7 +436,6 @@ class BIM_Views: sel.Group = g return - def delete(self): "deletes the selected object" @@ -460,9 +460,11 @@ class BIM_Views: if vm.tree.selectedItems(): item = vm.tree.selectedItems()[-1] vm.tree.editItem(item, 0) + @staticmethod def activate(dialog=None): from draftutils.utils import toggle_working_plane + vm = findWidget() if vm: if vm.tree.selectedItems(): @@ -503,6 +505,7 @@ class BIM_Views: def isolate(self): import Draft + """ Isolate the currently selected items in the tree view. @@ -542,12 +545,13 @@ class BIM_Views: toolTip = item.toolTip(0) obj = FreeCAD.ActiveDocument.getObject(toolTip) if obj: - if item not in selectedItems and not (checkAncestors and self._isAncestor(selectedItems[0], item)): + if item not in selectedItems and not ( + checkAncestors and self._isAncestor(selectedItems[0], item) + ): obj.ViewObject.Visibility = False else: obj.ViewObject.Visibility = True - def saveView(self): "save the current camera angle to the selected item" @@ -592,6 +596,7 @@ class BIM_Views: def onContextMenu(self, pos): """Fires the context menu""" import Draft + self.dialog.buttonAddProxy.setEnabled(True) selobj = self.dialog.tree.currentItem() if selobj: @@ -608,6 +613,7 @@ class BIM_Views: def getViews(self): """Returns a list of 2D views""" import Draft + views = [] for p in self.getPages(): for v in p.Views: @@ -621,7 +627,7 @@ class BIM_Views: def getPages(self): """Returns a list of TD pages""" - return [o for o in FreeCAD.ActiveDocument.Objects if o.isDerivedFrom('TechDraw::DrawPage')] + return [o for o in FreeCAD.ActiveDocument.Objects if o.isDerivedFrom("TechDraw::DrawPage")] # These functions need to be localized outside the command class, as they are used outside this module @@ -645,9 +651,7 @@ def show(item, column=None): obj = None vm = findWidget() - if isinstance(item, str) or ( - (sys.version_info.major < 3) and isinstance(item, unicode) - ): + if isinstance(item, str) or ((sys.version_info.major < 3) and isinstance(item, unicode)): # called from Python code obj = FreeCAD.ActiveDocument.getObject(item) else: @@ -667,11 +671,11 @@ def show(item, column=None): if obj.isDerivedFrom("TechDraw::DrawPage"): # case 1: the object is a TD page. We switch to it simply - obj.ViewObject.Visibility=True + obj.ViewObject.Visibility = True elif isView(obj): # case 2: the object is a 2D view - ssel = [obj]+obj.OutListRecursive + ssel = [obj] + obj.OutListRecursive FreeCADGui.Selection.clearSelection() for o in ssel: o.ViewObject.Visibility = True @@ -682,7 +686,7 @@ def show(item, column=None): if hasattr(w, "getSceneGraph"): FreeCADGui.getMainWindow().setActiveWindow(w) break - FreeCADGui.runCommand('Std_OrthographicCamera') + FreeCADGui.runCommand("Std_OrthographicCamera") FreeCADGui.ActiveDocument.ActiveView.viewTop() FreeCADGui.SendMsgToActiveView("ViewSelection") FreeCADGui.ActiveDocument.ActiveView.viewTop() @@ -700,9 +704,7 @@ def show(item, column=None): if vm: # store the last double-clicked item for the BIM WPView command - if isinstance(item, str) or ( - (sys.version_info.major < 3) and isinstance(item, unicode) - ): + if isinstance(item, str) or ((sys.version_info.major < 3) and isinstance(item, unicode)): vm.lastSelected = item else: vm.lastSelected = item.toolTip(0) @@ -716,7 +718,7 @@ def isView(obj): if hasattr(p, "Source"): if p.Source == obj: return True - if getattr(obj,"DrawingView",False): + if getattr(obj, "DrawingView", False): return True if getattr(obj, "IfcType", None) == "Annotation": if getattr(obj, "ObjectType", "").upper() == "DRAWING": @@ -745,9 +747,7 @@ def getTreeViewItem(obj): it.setFlags(it.flags() | QtCore.Qt.ItemIsEditable) it.setToolTip(0, obj.Name) if obj.ViewObject: - if hasattr(obj.ViewObject, "Proxy") and hasattr( - obj.ViewObject.Proxy, "getIcon" - ): + if hasattr(obj.ViewObject, "Proxy") and hasattr(obj.ViewObject.Proxy, "getIcon"): it.setIcon(0, QtGui.QIcon(obj.ViewObject.Proxy.getIcon())) return (it, z) diff --git a/src/Mod/BIM/bimcommands/BimWPCommands.py b/src/Mod/BIM/bimcommands/BimWPCommands.py index 0b1bf710fd..162da872b1 100644 --- a/src/Mod/BIM/bimcommands/BimWPCommands.py +++ b/src/Mod/BIM/bimcommands/BimWPCommands.py @@ -36,9 +36,7 @@ class BIM_SetWPFront: return { "Pixmap": "view-front.svg", "MenuText": QT_TRANSLATE_NOOP("BIM_SetWPFront", "Working Plane Front"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_SetWPFront", "Sets the working plane to Front" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_SetWPFront", "Sets the working plane to Front"), "Accel": "W,P,1", } @@ -53,9 +51,7 @@ class BIM_SetWPSide: return { "Pixmap": "view-right.svg", "MenuText": QT_TRANSLATE_NOOP("BIM_SetWPSide", "Working Plane Side"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_SetWPSide", "Sets the working plane to Side" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_SetWPSide", "Sets the working plane to Side"), "Accel": "W,P,3", } @@ -70,9 +66,7 @@ class BIM_SetWPTop: return { "Pixmap": "view-top.svg", "MenuText": QT_TRANSLATE_NOOP("BIM_SetWPTop", "Working Plane Top"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_SetWPTop", "Sets the working plane to Top" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_SetWPTop", "Sets the working plane to Top"), "Accel": "W,P,2", } diff --git a/src/Mod/BIM/bimcommands/BimWall.py b/src/Mod/BIM/bimcommands/BimWall.py index babbe06235..d639b97969 100644 --- a/src/Mod/BIM/bimcommands/BimWall.py +++ b/src/Mod/BIM/bimcommands/BimWall.py @@ -49,10 +49,15 @@ class Arch_Wall: def GetResources(self): """Returns a dictionary with the visual aspects of the Arch Wall tool.""" - return {'Pixmap' : 'Arch_Wall', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Wall","Wall"), - 'Accel': "W, A", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Wall","Creates a wall object from scratch or from a selected object (wire, face or solid)")} + return { + "Pixmap": "Arch_Wall", + "MenuText": QT_TRANSLATE_NOOP("Arch_Wall", "Wall"), + "Accel": "W, A", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Wall", + "Creates a wall object from scratch or from a selected object (wire, face or solid)", + ), + } def IsActive(self): @@ -73,7 +78,7 @@ class Arch_Wall: import draftguitools.gui_trackers as DraftTrackers self.doc = FreeCAD.ActiveDocument - self.Align = ["Center","Left","Right"][params.get_param_arch("WallAlignment")] + self.Align = ["Center", "Left", "Right"][params.get_param_arch("WallAlignment")] self.MultiMat = None self.Length = None self.lengthValue = 0 @@ -90,16 +95,26 @@ class Arch_Wall: if sel: # automatic mode if Draft.getType(sel[0].Object) != "Wall": - self.doc.openTransaction(translate("Arch","Create Wall")) + self.doc.openTransaction(translate("Arch", "Create Wall")) FreeCADGui.addModule("Arch") for selobj in sel: - if Draft.getType(selobj.Object) == "Space" \ - and selobj.HasSubObjects \ - and "Face" in selobj.SubElementNames[0]: + if ( + Draft.getType(selobj.Object) == "Space" + and selobj.HasSubObjects + and "Face" in selobj.SubElementNames[0] + ): idx = int(selobj.SubElementNames[0][4:]) - FreeCADGui.doCommand("obj = Arch.makeWall(FreeCAD.ActiveDocument."+selobj.Object.Name+",face="+str(idx)+")") + FreeCADGui.doCommand( + "obj = Arch.makeWall(FreeCAD.ActiveDocument." + + selobj.Object.Name + + ",face=" + + str(idx) + + ")" + ) else: - FreeCADGui.doCommand("obj = Arch.makeWall(FreeCAD.ActiveDocument."+selobj.Object.Name+")") + FreeCADGui.doCommand( + "obj = Arch.makeWall(FreeCAD.ActiveDocument." + selobj.Object.Name + ")" + ) FreeCADGui.addModule("Draft") FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() @@ -112,12 +127,14 @@ class Arch_Wall: self.points = [] self.wp = WorkingPlane.get_working_plane() self.tracker = DraftTrackers.boxTracker() - FreeCADGui.Snapper.getPoint(callback=self.getPoint, - extradlg=self.taskbox(), - title=translate("Arch", "First point of wall")) + FreeCADGui.Snapper.getPoint( + callback=self.getPoint, + extradlg=self.taskbox(), + title=translate("Arch", "First point of wall"), + ) FreeCADGui.draftToolBar.continueCmd.show() - def getPoint(self,point=None,obj=None): + def getPoint(self, point=None, obj=None): """Callback for clicks during interactive mode. When method _CommandWall.Activated() has entered the interactive mode, @@ -134,6 +151,7 @@ class Arch_Wall: import Draft import ArchWall from draftutils import gui_utils + if obj: if Draft.getType(obj) == "Wall": if not obj in self.existing: @@ -148,12 +166,14 @@ class Arch_Wall: self.tracker.width(self.Width) self.tracker.height(self.Height) self.tracker.on() - FreeCADGui.Snapper.getPoint(last=self.points[0], - callback=self.getPoint, - movecallback=self.update, - extradlg=self.taskbox(), - title=translate("Arch", "Next point"), - mode="line") + FreeCADGui.Snapper.getPoint( + last=self.points[0], + callback=self.getPoint, + movecallback=self.update, + extradlg=self.taskbox(), + title=translate("Arch", "Next point"), + mode="line", + ) elif len(self.points) == 2: FreeCAD.activeDraftCommand = None @@ -168,13 +188,17 @@ class Arch_Wall: wall = self.doc.Objects[-1] wallGrp = wall.getParentGroup() - if (self.JOIN_WALLS_SKETCHES or self.AUTOJOIN) \ - and self.existing \ - and self.existing[-1].getParentGroup() == wallGrp: + if ( + (self.JOIN_WALLS_SKETCHES or self.AUTOJOIN) + and self.existing + and self.existing[-1].getParentGroup() == wallGrp + ): oldWall = self.existing[-1] if self.JOIN_WALLS_SKETCHES and ArchWall.areSameWallTypes([wall, oldWall]): FreeCADGui.doCommand( - "Arch.joinWalls([wall, doc." + oldWall.Name + "], " + "Arch.joinWalls([wall, doc." + + oldWall.Name + + "], " + "delete=True, deletebase=True)" ) elif self.AUTOJOIN: @@ -213,12 +237,11 @@ class Arch_Wall: # Use ArchSketch if SketchArch add-on is present try: import ArchSketchObject + FreeCADGui.doCommand("import ArchSketchObject") FreeCADGui.doCommand("base = ArchSketchObject.makeArchSketch()") except: - FreeCADGui.doCommand( - "base = doc.addObject(\"Sketcher::SketchObject\", \"WallTrace\")" - ) + FreeCADGui.doCommand('base = doc.addObject("Sketcher::SketchObject", "WallTrace")') FreeCADGui.doCommand("base.Placement = wp.get_placement()") FreeCADGui.doCommand("base.addGeometry(trace)") else: @@ -229,16 +252,23 @@ class Arch_Wall: FreeCADGui.doCommand("base.Placement = wp.get_placement()") FreeCADGui.doCommand("doc.recompute()") FreeCADGui.doCommand( - "wall = Arch.makeWall(base, width=" + str(self.Width) - + ", height=" + str(self.Height) + ", align=\"" + str(self.Align) + "\")" + "wall = Arch.makeWall(base, width=" + + str(self.Width) + + ", height=" + + str(self.Height) + + ', align="' + + str(self.Align) + + '")' ) FreeCADGui.doCommand("wall.Normal = wp.axis") if self.MultiMat: FreeCADGui.doCommand("wall.Material = doc." + self.MultiMat.Name) - FreeCADGui.doCommand("doc.recompute()") # required as some autogroup code requires the wall shape + FreeCADGui.doCommand( + "doc.recompute()" + ) # required as some autogroup code requires the wall shape FreeCADGui.doCommand("Draft.autogroup(wall)") - def update(self,point,info): + def update(self, point, info): # info parameter is not used but needed for compatibility with the snapper """Callback for the mouse moving during the interactive mode. @@ -254,21 +284,24 @@ class Arch_Wall: """ import DraftVecUtils + if FreeCADGui.Control.activeDialog(): b = self.points[0] n = self.wp.axis bv = point.sub(b) dv = bv.cross(n) - dv = DraftVecUtils.scaleTo(dv,self.Width/2) + dv = DraftVecUtils.scaleTo(dv, self.Width / 2) if self.Align == "Center": - self.tracker.update([b,point]) + self.tracker.update([b, point]) elif self.Align == "Left": - self.tracker.update([b.add(dv),point.add(dv)]) + self.tracker.update([b.add(dv), point.add(dv)]) else: dv = dv.negative() - self.tracker.update([b.add(dv),point.add(dv)]) + self.tracker.update([b.add(dv), point.add(dv)]) if self.Length: - self.Length.setText(FreeCAD.Units.Quantity(bv.Length,FreeCAD.Units.Length).UserString) + self.Length.setText( + FreeCAD.Units.Quantity(bv.Length, FreeCAD.Units.Length).UserString + ) def taskbox(self): """Set up a simple gui widget for the interactive mode.""" @@ -276,76 +309,80 @@ class Arch_Wall: from PySide import QtCore, QtGui import Draft from draftutils import params + w = QtGui.QWidget() ui = FreeCADGui.UiLoader() - w.setWindowTitle(translate("Arch","Wall options")) + w.setWindowTitle(translate("Arch", "Wall options")) grid = QtGui.QGridLayout(w) # Wall presets input comboWallPresets = QtGui.QComboBox() - comboWallPresets.addItem(translate("Arch","Wall Presets")) - comboWallPresets.setToolTip(translate("Arch","This list shows all the MultiMaterials objects of this document. Create some to define wall types.")) + comboWallPresets.addItem(translate("Arch", "Wall Presets")) + comboWallPresets.setToolTip( + translate( + "Arch", + "This list shows all the MultiMaterials objects of this document. Create some to define wall types.", + ) + ) self.multimats = [] self.MultiMat = None for o in self.doc.Objects: if Draft.getType(o) == "MultiMaterial": self.multimats.append(o) comboWallPresets.addItem(o.Label) - if hasattr(FreeCAD,"LastArchMultiMaterial"): - for i,o in enumerate(self.multimats): + if hasattr(FreeCAD, "LastArchMultiMaterial"): + for i, o in enumerate(self.multimats): if o.Name == FreeCAD.LastArchMultiMaterial: - comboWallPresets.setCurrentIndex(i+1) + comboWallPresets.setCurrentIndex(i + 1) self.MultiMat = o - grid.addWidget(comboWallPresets,0,0,1,2) + grid.addWidget(comboWallPresets, 0, 0, 1, 2) # Wall length input - labelLength = QtGui.QLabel(translate("Arch","Length")) + labelLength = QtGui.QLabel(translate("Arch", "Length")) self.Length = ui.createWidget("Gui::InputField") self.Length.setText("0.00 mm") - grid.addWidget(labelLength,1,0,1,1) - grid.addWidget(self.Length,1,1,1,1) + grid.addWidget(labelLength, 1, 0, 1, 1) + grid.addWidget(self.Length, 1, 1, 1, 1) # Wall width input - labelWidth = QtGui.QLabel(translate("Arch","Width")) + labelWidth = QtGui.QLabel(translate("Arch", "Width")) inputWidth = ui.createWidget("Gui::InputField") - inputWidth.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString) - grid.addWidget(labelWidth,2,0,1,1) - grid.addWidget(inputWidth,2,1,1,1) + inputWidth.setText(FreeCAD.Units.Quantity(self.Width, FreeCAD.Units.Length).UserString) + grid.addWidget(labelWidth, 2, 0, 1, 1) + grid.addWidget(inputWidth, 2, 1, 1, 1) # Wall height input - labelHeight = QtGui.QLabel(translate("Arch","Height")) + labelHeight = QtGui.QLabel(translate("Arch", "Height")) inputHeight = ui.createWidget("Gui::InputField") - inputHeight.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString) - grid.addWidget(labelHeight,3,0,1,1) - grid.addWidget(inputHeight,3,1,1,1) + inputHeight.setText(FreeCAD.Units.Quantity(self.Height, FreeCAD.Units.Length).UserString) + grid.addWidget(labelHeight, 3, 0, 1, 1) + grid.addWidget(inputHeight, 3, 1, 1, 1) # Wall alignment input - labelAlignment = QtGui.QLabel(translate("Arch","Alignment")) + labelAlignment = QtGui.QLabel(translate("Arch", "Alignment")) comboAlignment = QtGui.QComboBox() - items = [translate("Arch","Center"),translate("Arch","Left"),translate("Arch","Right")] + items = [translate("Arch", "Center"), translate("Arch", "Left"), translate("Arch", "Right")] comboAlignment.addItems(items) - comboAlignment.setCurrentIndex(["Center","Left","Right"].index(self.Align)) - grid.addWidget(labelAlignment,4,0,1,1) - grid.addWidget(comboAlignment,4,1,1,1) + comboAlignment.setCurrentIndex(["Center", "Left", "Right"].index(self.Align)) + grid.addWidget(labelAlignment, 4, 0, 1, 1) + grid.addWidget(comboAlignment, 4, 1, 1, 1) # Wall offset input labelOffset = QtGui.QLabel(translate("Arch", "Offset")) inputOffset = ui.createWidget("Gui::InputField") - inputOffset.setText(FreeCAD.Units.Quantity( - self.Offset, - FreeCAD.Units.Length).UserString) + inputOffset.setText(FreeCAD.Units.Quantity(self.Offset, FreeCAD.Units.Length).UserString) grid.addWidget(labelOffset, 5, 0, 1, 1) grid.addWidget(inputOffset, 5, 1, 1, 1) # Wall "use sketches" checkbox - labelUseSketches = QtGui.QLabel(translate("Arch","Use sketches")) + labelUseSketches = QtGui.QLabel(translate("Arch", "Use sketches")) checkboxUseSketches = QtGui.QCheckBox() checkboxUseSketches.setObjectName("UseSketches") checkboxUseSketches.setLayoutDirection(QtCore.Qt.RightToLeft) labelUseSketches.setBuddy(checkboxUseSketches) checkboxUseSketches.setChecked(params.get_param_arch("WallSketches")) - grid.addWidget(labelUseSketches,6,0,1,1) - grid.addWidget(checkboxUseSketches,6,1,1,1) + grid.addWidget(labelUseSketches, 6, 0, 1, 1) + grid.addWidget(checkboxUseSketches, 6, 1, 1, 1) # Enable/disable inputOffset based on inputAlignment def updateOffsetState(index): @@ -364,9 +401,9 @@ class Arch_Wall: inputHeight.valueChanged.connect(self.setHeight) comboAlignment.currentIndexChanged.connect(self.setAlign) inputOffset.valueChanged.connect(self.setOffset) - if hasattr(checkboxUseSketches, "checkStateChanged"): # Qt version >= 6.7.0 + if hasattr(checkboxUseSketches, "checkStateChanged"): # Qt version >= 6.7.0 checkboxUseSketches.checkStateChanged.connect(self.setUseSketch) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 checkboxUseSketches.stateChanged.connect(self.setUseSketch) comboWallPresets.currentIndexChanged.connect(self.setMat) @@ -382,55 +419,58 @@ class Arch_Wall: return w - def setMat(self,d): + def setMat(self, d): """Simple callback for the interactive mode gui widget to set material.""" if d == 0: self.MultiMat = None del FreeCAD.LastArchMultiMaterial elif d <= len(self.multimats): - self.MultiMat = self.multimats[d-1] + self.MultiMat = self.multimats[d - 1] FreeCAD.LastArchMultiMaterial = self.MultiMat.Name - def setLength(self,d): + def setLength(self, d): """Simple callback for the interactive mode gui widget to set length.""" if isinstance(d, FreeCAD.Units.Quantity): d = d.Value self.lengthValue = d - def setWidth(self,d): + def setWidth(self, d): """Simple callback for the interactive mode gui widget to set width.""" from draftutils import params + if isinstance(d, FreeCAD.Units.Quantity): d = d.Value self.Width = d self.tracker.width(d) - params.set_param_arch("WallWidth",d) + params.set_param_arch("WallWidth", d) - - def setHeight(self,d): + def setHeight(self, d): """Simple callback for the interactive mode gui widget to set height.""" from draftutils import params + if isinstance(d, FreeCAD.Units.Quantity): d = d.Value self.Height = d self.tracker.height(d) - params.set_param_arch("WallHeight",d) + params.set_param_arch("WallHeight", d) - def setAlign(self,i): + def setAlign(self, i): """Simple callback for the interactive mode gui widget to set alignment.""" from draftutils import params - self.Align = ["Center","Left","Right"][i] - params.set_param_arch("WallAlignment",i) + + self.Align = ["Center", "Left", "Right"][i] + params.set_param_arch("WallAlignment", i) def setOffset(self, d): """Simple callback for the interactive mode GUI widget to set offset.""" from draftutils import params + if isinstance(d, FreeCAD.Units.Quantity): d = d.Value self.Offset = d @@ -440,20 +480,31 @@ class Arch_Wall: """Simple callback to set if walls should based on sketches.""" from draftutils import params - params.set_param_arch("WallSketches",bool(getattr(i, "value", i))) + + params.set_param_arch("WallSketches", bool(getattr(i, "value", i))) def createFromGUI(self): """Callback to create wall by using the _CommandWall.taskbox()""" - self.doc.openTransaction(translate("Arch","Create Wall")) + self.doc.openTransaction(translate("Arch", "Create Wall")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand('wall = Arch.makeWall(length='+str(self.lengthValue)+',width='+str(self.Width)+',height='+str(self.Height)+',align="'+str(self.Align)+'")') + FreeCADGui.doCommand( + "wall = Arch.makeWall(length=" + + str(self.lengthValue) + + ",width=" + + str(self.Width) + + ",height=" + + str(self.Height) + + ',align="' + + str(self.Align) + + '")' + ) if self.MultiMat: - FreeCADGui.doCommand("wall.Material = FreeCAD.ActiveDocument."+self.MultiMat.Name) + FreeCADGui.doCommand("wall.Material = FreeCAD.ActiveDocument." + self.MultiMat.Name) self.doc.commitTransaction() self.doc.recompute() - if hasattr(FreeCADGui,"draftToolBar"): + if hasattr(FreeCADGui, "draftToolBar"): FreeCADGui.draftToolBar.escape() -FreeCADGui.addCommand('Arch_Wall', Arch_Wall()) +FreeCADGui.addCommand("Arch_Wall", Arch_Wall()) diff --git a/src/Mod/BIM/bimcommands/BimWelcome.py b/src/Mod/BIM/bimcommands/BimWelcome.py index 6055e49582..bd2d67fc1e 100644 --- a/src/Mod/BIM/bimcommands/BimWelcome.py +++ b/src/Mod/BIM/bimcommands/BimWelcome.py @@ -37,9 +37,7 @@ class BIM_Welcome: return { "Pixmap": "BIM_Welcome.svg", "MenuText": QT_TRANSLATE_NOOP("BIM_Welcome", "BIM Welcome Screen"), - "ToolTip": QT_TRANSLATE_NOOP( - "BIM_Welcome", "Shows the BIM workbench welcome screen" - ), + "ToolTip": QT_TRANSLATE_NOOP("BIM_Welcome", "Shows the BIM workbench welcome screen"), } def Activated(self): @@ -54,9 +52,7 @@ class BIM_Welcome: # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( - mw.frameGeometry().topLeft() - + mw.rect().center() - - self.form.rect().center() + mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() ) # show dialog and run setup dialog afterwards if OK was pressed @@ -65,9 +61,7 @@ class BIM_Welcome: FreeCADGui.runCommand("BIM_Setup") # remove first time flag - PARAMS.SetBool( - "FirstTime", False - ) + PARAMS.SetBool("FirstTime", False) def handleLink(self, link): from PySide import QtCore, QtGui diff --git a/src/Mod/BIM/bimcommands/BimWindow.py b/src/Mod/BIM/bimcommands/BimWindow.py index bea5600287..b89f611a78 100644 --- a/src/Mod/BIM/bimcommands/BimWindow.py +++ b/src/Mod/BIM/bimcommands/BimWindow.py @@ -33,11 +33,10 @@ QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") -ALLOWEDHOSTS = ["Wall","Structure","Roof"] +ALLOWEDHOSTS = ["Wall", "Structure", "Roof"] class Arch_Window: - "the Arch Window command definition" def __init__(self): @@ -46,10 +45,15 @@ class Arch_Window: def GetResources(self): - return {'Pixmap' : 'Arch_Window', - 'MenuText': QT_TRANSLATE_NOOP("Arch_Window","Window"), - 'Accel': "W, N", - 'ToolTip': QT_TRANSLATE_NOOP("Arch_Window","Creates a window object from a selected object (wire, rectangle or sketch)")} + return { + "Pixmap": "Arch_Window", + "MenuText": QT_TRANSLATE_NOOP("Arch_Window", "Window"), + "Accel": "W, N", + "ToolTip": QT_TRANSLATE_NOOP( + "Arch_Window", + "Creates a window object from a selected object (wire, rectangle or sketch)", + ), + } def IsActive(self): @@ -78,52 +82,60 @@ class Arch_Window: self.Sill = 0 self.Include = True self.baseFace = None - self.wparams = ["Width","Height","H1","H2","H3","W1","W2","O1","O2"] + self.wparams = ["Width", "Height", "H1", "H2", "H3", "W1", "W2", "O1", "O2"] self.wp = None # autobuild mode if FreeCADGui.Selection.getSelectionEx(): FreeCADGui.draftToolBar.offUi() obj = self.sel[0] - if hasattr(obj,'Shape'): + if hasattr(obj, "Shape"): if obj.Shape.Wires and (not obj.Shape.Solids) and (not obj.Shape.Shells): FreeCADGui.Control.closeDialog() host = None - if hasattr(obj,"AttachmentSupport"): + if hasattr(obj, "AttachmentSupport"): if obj.AttachmentSupport: - if isinstance(obj.AttachmentSupport,tuple): + if isinstance(obj.AttachmentSupport, tuple): host = obj.AttachmentSupport[0] - elif isinstance(obj.AttachmentSupport,list): + elif isinstance(obj.AttachmentSupport, list): host = obj.AttachmentSupport[0][0] else: host = obj.AttachmentSupport - obj.AttachmentSupport = None # remove - elif Draft.isClone(obj,"Window"): + obj.AttachmentSupport = None # remove + elif Draft.isClone(obj, "Window"): if obj.Objects[0].Inlist: host = obj.Objects[0].Inlist[0] - self.doc.openTransaction(translate("Arch","Create Window")) + self.doc.openTransaction(translate("Arch", "Create Window")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("win = Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") + FreeCADGui.doCommand( + "win = Arch.makeWindow(FreeCAD.ActiveDocument." + obj.Name + ")" + ) if host and self.Include: - FreeCADGui.doCommand("win.Hosts = [FreeCAD.ActiveDocument."+host.Name+"]") + FreeCADGui.doCommand( + "win.Hosts = [FreeCAD.ActiveDocument." + host.Name + "]" + ) siblings = host.Proxy.getSiblings(host) sibs = [host] for sibling in siblings: if not sibling in sibs: sibs.append(sibling) - FreeCADGui.doCommand("win.Hosts = win.Hosts+[FreeCAD.ActiveDocument."+sibling.Name+"]") + FreeCADGui.doCommand( + "win.Hosts = win.Hosts+[FreeCAD.ActiveDocument." + + sibling.Name + + "]" + ) self.doc.commitTransaction() self.doc.recompute() return # Try to detect an object to use as a window type - TODO we must make this safer - elif obj.Shape.Solids and (Draft.getType(obj) not in ["Wall","Structure","Roof"]): + elif obj.Shape.Solids and (Draft.getType(obj) not in ["Wall", "Structure", "Roof"]): # we consider the selected object as a type - self.doc.openTransaction(translate("Arch","Create Window")) + self.doc.openTransaction(translate("Arch", "Create Window")) FreeCADGui.addModule("Arch") - FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") + FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument." + obj.Name + ")") self.doc.commitTransaction() self.doc.recompute() return @@ -137,9 +149,13 @@ class Arch_Window: self.tracker.width(self.W1) self.tracker.height(self.Height) self.tracker.on() - FreeCAD.Console.PrintMessage(translate("Arch","Choose a face on an existing object or select a preset")+"\n") - FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox()) - #FreeCADGui.Snapper.setSelectMode(True) + FreeCAD.Console.PrintMessage( + translate("Arch", "Choose a face on an existing object or select a preset") + "\n" + ) + FreeCADGui.Snapper.getPoint( + callback=self.getPoint, movecallback=self.update, extradlg=self.taskbox() + ) + # FreeCADGui.Snapper.setSelectMode(True) def has_width_and_height_constraint(self, sketch): width_found = False @@ -153,10 +169,9 @@ class Arch_Window: elif width_found and height_found: break - return (width_found and height_found) - - def getPoint(self,point=None,obj=None): + return width_found and height_found + def getPoint(self, point=None, obj=None): "this function is called by the snapper when it has a 3D point" import Draft @@ -172,20 +187,34 @@ class Arch_Window: # if something was selected, override the underlying object if self.sel: obj = self.sel[0] - point = point.add(FreeCAD.Vector(0,0,self.Sill)) - self.doc.openTransaction(translate("Arch","Create Window")) + point = point.add(FreeCAD.Vector(0, 0, self.Sill)) + self.doc.openTransaction(translate("Arch", "Create Window")) FreeCADGui.doCommand("import FreeCAD, Arch, DraftGeomUtils, WorkingPlane") FreeCADGui.doCommand("wp = WorkingPlane.get_working_plane()") if self.baseFace is not None: - FreeCADGui.doCommand("face = FreeCAD.ActiveDocument." + self.baseFace[0].Name + ".Shape.Faces[" + str(self.baseFace[1]) + "]") + FreeCADGui.doCommand( + "face = FreeCAD.ActiveDocument." + + self.baseFace[0].Name + + ".Shape.Faces[" + + str(self.baseFace[1]) + + "]" + ) FreeCADGui.doCommand("pl = DraftGeomUtils.placement_from_face(face, vec_z = wp.axis)") else: FreeCADGui.doCommand("pl = FreeCAD.Placement()") FreeCADGui.doCommand("pl.Rotation = FreeCAD.Rotation(wp.u, wp.axis, -wp.v, 'XZY')") - FreeCADGui.doCommand("pl.Base = FreeCAD.Vector(" + str(point.x) + ", " + str(point.y) + ", " + str(point.z) + ")") + FreeCADGui.doCommand( + "pl.Base = FreeCAD.Vector(" + + str(point.x) + + ", " + + str(point.y) + + ", " + + str(point.z) + + ")" + ) if self.baseFace is not None: host = self.baseFace[0] @@ -202,10 +231,14 @@ class Arch_Window: FreeCADGui.doCommand("FreeCADGui.ActiveDocument.mergeProject('" + path + "')") # find the latest added window nol = self.doc.Objects - for o in nol[len(col):]: + for o in nol[len(col) :]: if Draft.getType(o) == "Window": if Draft.getType(o.Base) != "Sketcher::SketchObject": - _wrn(translate("Arch", "Window not based on sketch. Window not aligned or resized.")) + _wrn( + translate( + "Arch", "Window not based on sketch. Window not aligned or resized." + ) + ) self.Include = False break FreeCADGui.doCommand("win = FreeCAD.ActiveDocument.getObject('" + o.Name + "')") @@ -214,12 +247,17 @@ class Arch_Window: # Now with the new AutoNormalReversed property/flag, set True as default, the auto Normal previously in opposite direction to is now consistent with that previously hardcoded. # With the normal set to 'auto', window object would not suffer weird shape if the Base Sketch is rotated by some reason. # Keep the property be 'auto' (0,0,0) here. - #FreeCADGui.doCommand("win.Normal = pl.Rotation.multVec(FreeCAD.Vector(0, 0, -1))") + # FreeCADGui.doCommand("win.Normal = pl.Rotation.multVec(FreeCAD.Vector(0, 0, -1))") FreeCADGui.doCommand("win.Width = " + str(self.Width)) FreeCADGui.doCommand("win.Height = " + str(self.Height)) FreeCADGui.doCommand("win.Base.recompute()") if not self.has_width_and_height_constraint(o.Base): - _wrn(translate("Arch", "No Width and/or Height constraint in window sketch. Window not resized.")) + _wrn( + translate( + "Arch", + "No Width and/or Height constraint in window sketch. Window not resized.", + ) + ) break else: _wrn(translate("Arch", "No window found. Cannot continue.")) @@ -230,23 +268,40 @@ class Arch_Window: preset = True wp = "" for p in self.wparams: - wp += ", " + p.lower() + "=" + str(getattr(self,p)) + wp += ", " + p.lower() + "=" + str(getattr(self, p)) import ArchSketchObject - if (host and Draft.getType(host.Base) == "ArchSketch" and - hasattr(ArchSketchObject, 'attachToHost') and - hasattr(FreeCAD, 'ArchSketchLock') and - FreeCAD.ArchSketchLock): + if ( + host + and Draft.getType(host.Base) == "ArchSketch" + and hasattr(ArchSketchObject, "attachToHost") + and hasattr(FreeCAD, "ArchSketchLock") + and FreeCAD.ArchSketchLock + ): if self.Include: # Window base sketch's placement stay at origin is good if addon exists and self.Include # Window object triggers onChanged() upon setting/changing Window.Sill to move Window's z position # For Window with SketchArch add-on, attachToHost() is to be run below to set the 'initial' Window's placement prior to triggering onChanged() below, # so window_sill parameter is not used here at the moment, see 'if self.Include' below. - #FreeCADGui.doCommand("win = Arch.makeWindowPreset('" + WindowPresets[self.Preset] + "' " + wp + ", window_sill=" + str(self.Sill.Value) + ")") - FreeCADGui.doCommand("win = Arch.makeWindowPreset('" + WindowPresets[self.Preset] + "' " + wp + ")") + # FreeCADGui.doCommand("win = Arch.makeWindowPreset('" + WindowPresets[self.Preset] + "' " + wp + ", window_sill=" + str(self.Sill.Value) + ")") + FreeCADGui.doCommand( + "win = Arch.makeWindowPreset('" + + WindowPresets[self.Preset] + + "' " + + wp + + ")" + ) else: # Window base sketch's placement follow getPoint placement if addon exists but NOT self.Include - FreeCADGui.doCommand("win = Arch.makeWindowPreset('" + WindowPresets[self.Preset] + "' " + wp + ", placement=pl, window_sill=" + str(self.Sill.Value) + ")") + FreeCADGui.doCommand( + "win = Arch.makeWindowPreset('" + + WindowPresets[self.Preset] + + "' " + + wp + + ", placement=pl, window_sill=" + + str(self.Sill.Value) + + ")" + ) FreeCADGui.doCommand("win.AttachToAxisOrSketch = 'None'") FreeCADGui.doCommand("FreeCADGui.Selection.addSelection(win)") w = FreeCADGui.Selection.getSelection()[0] @@ -254,7 +309,15 @@ class Arch_Window: wPl = FreeCAD.SketchArchPl SketchArch = True else: - FreeCADGui.doCommand("win = Arch.makeWindowPreset('" + WindowPresets[self.Preset] + "' " + wp + ", placement=pl, window_sill=" + str(self.Sill.Value) + ")") + FreeCADGui.doCommand( + "win = Arch.makeWindowPreset('" + + WindowPresets[self.Preset] + + "' " + + wp + + ", placement=pl, window_sill=" + + str(self.Sill.Value) + + ")" + ) SketchArch = False if self.Include: @@ -262,11 +325,13 @@ class Arch_Window: FreeCADGui.doCommand("win.Hosts = [FreeCAD.ActiveDocument." + host.Name + "]") siblings = host.Proxy.getSiblings(host) for sibling in siblings: - FreeCADGui.doCommand("win.Hosts = win.Hosts + [FreeCAD.ActiveDocument." + sibling.Name + "]") + FreeCADGui.doCommand( + "win.Hosts = win.Hosts + [FreeCAD.ActiveDocument." + sibling.Name + "]" + ) if SketchArch: ArchSketchObject.attachToHost(w, target=host, pl=wPl) # Trigger onChanged() in the window object by setting Window.Sill, after setting the Window's 'initial' placement by attachToHost() above - FreeCADGui.doCommand("win.Sill = " + str(self.Sill.Value)) + FreeCADGui.doCommand("win.Sill = " + str(self.Sill.Value)) self.doc.commitTransaction() self.doc.recompute() @@ -274,57 +339,59 @@ class Arch_Window: self.tracker.finalize() return - def update(self,point,info): - + def update(self, point, info): "this function is called by the Snapper when the mouse is moved" - delta = FreeCAD.Vector(self.Width/2,self.W1/2,self.Height/2) - delta = delta.add(FreeCAD.Vector(0,0,self.Sill)) + delta = FreeCAD.Vector(self.Width / 2, self.W1 / 2, self.Height / 2) + delta = delta.add(FreeCAD.Vector(0, 0, self.Sill)) if self.baseFace is None: - rot = FreeCAD.Rotation(self.wp.u,self.wp.v,-self.wp.axis,"XZY") + rot = FreeCAD.Rotation(self.wp.u, self.wp.v, -self.wp.axis, "XZY") self.tracker.setRotation(rot) if info: - if "Face" in info['Component']: + if "Face" in info["Component"]: import DraftGeomUtils - o = self.doc.getObject(info['Object']) - self.baseFace = [o,int(info['Component'][4:])-1] - #print("switching to ",o.Label," face ",self.baseFace[1]) + + o = self.doc.getObject(info["Object"]) + self.baseFace = [o, int(info["Component"][4:]) - 1] + # print("switching to ",o.Label," face ",self.baseFace[1]) f = o.Shape.Faces[self.baseFace[1]] - p = DraftGeomUtils.placement_from_face(f,vec_z=self.wp.axis,rotated=True) + p = DraftGeomUtils.placement_from_face(f, vec_z=self.wp.axis, rotated=True) rot = p.Rotation self.tracker.setRotation(rot) r = self.tracker.trans.rotation.getValue().getValue() - if r != (0,0,0,1): - delta = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(delta.x,-delta.y,-delta.z)) + if r != (0, 0, 0, 1): + delta = FreeCAD.Rotation(r[0], r[1], r[2], r[3]).multVec( + FreeCAD.Vector(delta.x, -delta.y, -delta.z) + ) self.tracker.pos(point.add(delta)) def taskbox(self): - "sets up a taskbox widget" from draftutils import params from PySide import QtCore, QtGui, QtSvgWidgets from ArchWindowPresets import WindowPresets + w = QtGui.QWidget() ui = FreeCADGui.UiLoader() - w.setWindowTitle(translate("Arch","Window options")) + w.setWindowTitle(translate("Arch", "Window options")) grid = QtGui.QGridLayout(w) # include box - include = QtGui.QCheckBox(translate("Arch","Auto include in host object")) + include = QtGui.QCheckBox(translate("Arch", "Auto include in host object")) include.setChecked(True) - grid.addWidget(include,0,0,1,2) - if hasattr(include, "checkStateChanged"): # Qt version >= 6.7.0 + grid.addWidget(include, 0, 0, 1, 2) + if hasattr(include, "checkStateChanged"): # Qt version >= 6.7.0 include.checkStateChanged.connect(self.setInclude) - else: # Qt version < 6.7.0 + else: # Qt version < 6.7.0 include.stateChanged.connect(self.setInclude) # sill height - labels = QtGui.QLabel(translate("Arch","Sill height")) + labels = QtGui.QLabel(translate("Arch", "Sill height")) values = ui.createWidget("Gui::InputField") - grid.addWidget(labels,1,0,1,1) - grid.addWidget(values,1,1,1,1) + grid.addWidget(labels, 1, 0, 1, 1) + grid.addWidget(values, 1, 1, 1, 1) values.valueChanged.connect(self.setSill) # check for Parts library and Arch presets @@ -333,7 +400,9 @@ class Arch_Window: # paths in librarypresets need to be double escaped "\\\\", so let's # use forward slashes instead... self.librarypresets = [] - librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString("destination", "") + librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString( + "destination", "" + ) # librarypath should have only forward slashes already, but let's use replace() anyway just to be sure: librarypath = librarypath.replace("\\", "/") + "/Architectural Parts" presetdir = FreeCAD.getUserAppDataDir().replace("\\", "/") + "/Arch" @@ -346,27 +415,32 @@ class Arch_Window: subdir = wdir + "/" + subtype if os.path.isdir(subdir): for subfile in os.listdir(subdir): - if (os.path.isfile(subdir + "/" + subfile) - and subfile.lower().endswith(".fcstd")): - self.librarypresets.append([wtype + " - " + subtype + " - " + subfile[:-6], - subdir + "/" + subfile]) + if os.path.isfile( + subdir + "/" + subfile + ) and subfile.lower().endswith(".fcstd"): + self.librarypresets.append( + [ + wtype + " - " + subtype + " - " + subfile[:-6], + subdir + "/" + subfile, + ] + ) # presets box - labelp = QtGui.QLabel(translate("Arch","Preset")) + labelp = QtGui.QLabel(translate("Arch", "Preset")) valuep = QtGui.QComboBox() valuep.setMinimumContentsLength(6) valuep.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) valuep.addItems(WindowPresets) valuep.setCurrentIndex(self.Preset) - grid.addWidget(labelp,2,0,1,1) - grid.addWidget(valuep,2,1,1,1) + grid.addWidget(labelp, 2, 0, 1, 1) + grid.addWidget(valuep, 2, 1, 1, 1) valuep.currentIndexChanged.connect(self.setPreset) for it in self.librarypresets: valuep.addItem(it[0]) # image display self.pic = QtGui.QLabel() - grid.addWidget(self.pic,3,0,1,2) + grid.addWidget(self.pic, 3, 0, 1, 2) self.pic.setFixedHeight(128) self.pic.hide() @@ -374,31 +448,33 @@ class Arch_Window: self.im = QtSvgWidgets.QSvgWidget(":/ui/ParametersWindowFixed.svg") self.im.setMaximumWidth(200) self.im.setMinimumHeight(120) - grid.addWidget(self.im,4,0,1,2) - #self.im.hide() + grid.addWidget(self.im, 4, 0, 1, 2) + # self.im.hide() # parameters i = 5 for param in self.wparams: - lab = QtGui.QLabel(translate("Arch",param)) - setattr(self,"val"+param,ui.createWidget("Gui::InputField")) - wid = getattr(self,"val"+param) + lab = QtGui.QLabel(translate("Arch", param)) + setattr(self, "val" + param, ui.createWidget("Gui::InputField")) + wid = getattr(self, "val" + param) if param == "W1": - wid.setText(FreeCAD.Units.Quantity(self.W1,FreeCAD.Units.Length).UserString) + wid.setText(FreeCAD.Units.Quantity(self.W1, FreeCAD.Units.Length).UserString) elif param == "Width": - wid.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString) + wid.setText(FreeCAD.Units.Quantity(self.Width, FreeCAD.Units.Length).UserString) elif param == "Height": - wid.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString) + wid.setText(FreeCAD.Units.Quantity(self.Height, FreeCAD.Units.Length).UserString) else: - n = params.get_param_arch("Window"+param) - wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString) - setattr(self,param,n) - grid.addWidget(lab,i,0,1,1) - grid.addWidget(wid,i,1,1,1) + n = params.get_param_arch("Window" + param) + wid.setText(FreeCAD.Units.Quantity(n, FreeCAD.Units.Length).UserString) + setattr(self, param, n) + grid.addWidget(lab, i, 0, 1, 1) + grid.addWidget(wid, i, 1, 1, 1) i += 1 valueChanged = self.getValueChanged(param) FreeCAD.wid = wid - QtCore.QObject.connect(getattr(self,"val"+param),QtCore.SIGNAL("valueChanged(double)"), valueChanged) + QtCore.QObject.connect( + getattr(self, "val" + param), QtCore.SIGNAL("valueChanged(double)"), valueChanged + ) # restore saved states if self.doormode: @@ -409,47 +485,50 @@ class Arch_Window: d = params.get_param_arch("WindowSill") if i < valuep.count(): valuep.setCurrentIndex(i) - values.setText(FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).UserString) + values.setText(FreeCAD.Units.Quantity(d, FreeCAD.Units.Length).UserString) return w - def getValueChanged(self,p): + def getValueChanged(self, p): - return lambda d : self.setParams(p, d) + return lambda d: self.setParams(p, d) - def setSill(self,d): + def setSill(self, d): from draftutils import params + self.Sill = d if self.doormode: - params.set_param_arch("DoorSill",d) + params.set_param_arch("DoorSill", d) else: - params.set_param_arch("WindowSill",d) + params.set_param_arch("WindowSill", d) def setInclude(self, i): self.Include = bool(getattr(i, "value", i)) - def setParams(self,param,d): + def setParams(self, param, d): from draftutils import params - setattr(self,param,d) + + setattr(self, param, d) self.tracker.length(self.Width) self.tracker.height(self.Height) self.tracker.width(self.W1) - prefix = "Door" if self.doormode and param in ("Width","Height") else "Window" - params.set_param_arch(prefix+param,d) + prefix = "Door" if self.doormode and param in ("Width", "Height") else "Window" + params.set_param_arch(prefix + param, d) - def setPreset(self,i): + def setPreset(self, i): from PySide import QtGui from draftutils import params from ArchWindowPresets import WindowPresets + self.Preset = i if self.doormode: - params.set_param_arch("DoorPreset",i) + params.set_param_arch("DoorPreset", i) else: - params.set_param_arch("WindowPreset",i) + params.set_param_arch("WindowPreset", i) if i >= 0: FreeCADGui.Snapper.setSelectMode(False) self.tracker.length(self.Width) @@ -460,9 +539,9 @@ class Arch_Window: self.im.show() if i == 0: self.im.load(":/ui/ParametersWindowFixed.svg") - elif i in [1,8]: + elif i in [1, 8]: self.im.load(":/ui/ParametersWindowSimple.svg") - elif i in [2,4,7]: + elif i in [2, 4, 7]: self.im.load(":/ui/ParametersWindowDouble.svg") elif i == 3: self.im.load(":/ui/ParametersWindowStash.svg") @@ -475,7 +554,7 @@ class Arch_Window: else: # From Library self.im.hide() - path = self.librarypresets[i-len(WindowPresets)][1] + path = self.librarypresets[i - len(WindowPresets)][1] if path.lower().endswith(".fcstd"): try: import tempfile @@ -487,24 +566,24 @@ class Arch_Window: files = zfile.namelist() # check for meta-file if it's really a FreeCAD document if files[0] == "Document.xml": - image="thumbnails/Thumbnail.png" + image = "thumbnails/Thumbnail.png" if image in files: image = zfile.read(image) - thumbfile = tempfile.mkstemp(suffix='.png')[1] - thumb = open(thumbfile,"wb") + thumbfile = tempfile.mkstemp(suffix=".png")[1] + thumb = open(thumbfile, "wb") thumb.write(image) thumb.close() im = QtGui.QPixmap(thumbfile) self.pic.setPixmap(im) self.pic.show() - #for param in self.wparams: + # for param in self.wparams: # getattr(self,"val"+param).setEnabled(True) else: FreeCADGui.Snapper.setSelectMode(True) self.tracker.off() self.im.hide() for param in self.wparams: - getattr(self,"val"+param).setEnabled(False) + getattr(self, "val" + param).setEnabled(False) -FreeCADGui.addCommand('Arch_Window', Arch_Window()) +FreeCADGui.addCommand("Arch_Window", Arch_Window()) diff --git a/src/Mod/BIM/bimcommands/BimWindows.py b/src/Mod/BIM/bimcommands/BimWindows.py index 4fb31b6014..01c965b03e 100644 --- a/src/Mod/BIM/bimcommands/BimWindows.py +++ b/src/Mod/BIM/bimcommands/BimWindows.py @@ -84,9 +84,7 @@ class BIM_Windows_TaskPanel: from PySide import QtGui self.form.windows.clear() - windows = [ - o for o in FreeCAD.ActiveDocument.Objects if Draft.getType(o) == "Window" - ] + windows = [o for o in FreeCAD.ActiveDocument.Objects if Draft.getType(o) == "Window"] if self.form.groupMode.currentIndex() == 0: for window in windows: s1 = window.Label @@ -222,12 +220,8 @@ class BIM_Windows_TaskPanel: form = FreeCADGui.PySideUic.loadUi(":/ui/dialogMaterialChooser.ui") mw = FreeCADGui.getMainWindow() - form.move( - mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center() - ) - materials = [ - o for o in FreeCAD.ActiveDocument.Objects if Draft.getType(o) == "Material" - ] + form.move(mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center()) + materials = [o for o in FreeCAD.ActiveDocument.Objects if Draft.getType(o) == "Material"] it = QtGui.QListWidgetItem(translate("BIM", "None")) it.setIcon(QtGui.QIcon(":/icons/button_invalid.svg")) it.setToolTip("__None__") diff --git a/src/Mod/BIM/bimcommands/__init__.py b/src/Mod/BIM/bimcommands/__init__.py index d101870202..3032d1eaa5 100644 --- a/src/Mod/BIM/bimcommands/__init__.py +++ b/src/Mod/BIM/bimcommands/__init__.py @@ -8,6 +8,8 @@ import PartGui # populate the list of submodules modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py")) -__all__ = [os.path.basename(f)[:-3] for f in modules if os.path.isfile(f) and not f.endswith('__init__.py')] +__all__ = [ + os.path.basename(f)[:-3] for f in modules if os.path.isfile(f) and not f.endswith("__init__.py") +] from . import * diff --git a/src/Mod/BIM/bimtests/TestArchAxis.py b/src/Mod/BIM/bimtests/TestArchAxis.py index 5391713198..0df951b171 100644 --- a/src/Mod/BIM/bimtests/TestArchAxis.py +++ b/src/Mod/BIM/bimtests/TestArchAxis.py @@ -26,6 +26,7 @@ import FreeCAD as App import Arch from bimtests import TestArchBase + class TestArchAxis(TestArchBase.TestArchBase): def test_make_axis_default(self): @@ -59,4 +60,4 @@ class TestArchAxis(TestArchBase.TestArchBase): axis2 = Arch.makeAxis(num=1, size=2000) axis_system = Arch.makeAxisSystem([axis1, axis2], name="TestAxisSystem") self.assertIsNotNone(axis_system, "makeAxisSystem failed to create an axis system.") - self.assertEqual(axis_system.Label, "TestAxisSystem", "Axis system label is incorrect.") \ No newline at end of file + self.assertEqual(axis_system.Label, "TestAxisSystem", "Axis system label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchBase.py b/src/Mod/BIM/bimtests/TestArchBase.py index db997cdde5..d73be3bce2 100644 --- a/src/Mod/BIM/bimtests/TestArchBase.py +++ b/src/Mod/BIM/bimtests/TestArchBase.py @@ -27,6 +27,7 @@ import unittest import FreeCAD + class TestArchBase(unittest.TestCase): def setUp(self): @@ -54,11 +55,13 @@ class TestArchBase(unittest.TestCase): def tearDown(self): """Close the test document after all tests in the class are complete.""" - if hasattr(self, 'document') and self.document: + if hasattr(self, "document") and self.document: try: FreeCAD.closeDocument(self.document.Name) except Exception as e: - FreeCAD.Console.PrintError(f"Error during tearDown in {self.__class__.__name__}: {e}\n") + FreeCAD.Console.PrintError( + f"Error during tearDown in {self.__class__.__name__}: {e}\n" + ) def printTestMessage(self, text, prepend_text="Test ", end="\n"): """Write messages to the console including the line ending. @@ -67,4 +70,3 @@ class TestArchBase(unittest.TestCase): passed as the prepend_text argument """ FreeCAD.Console.PrintMessage(prepend_text + text + end) - diff --git a/src/Mod/BIM/bimtests/TestArchBaseGui.py b/src/Mod/BIM/bimtests/TestArchBaseGui.py index 066ec301d7..cb1adc7b03 100644 --- a/src/Mod/BIM/bimtests/TestArchBaseGui.py +++ b/src/Mod/BIM/bimtests/TestArchBaseGui.py @@ -27,6 +27,7 @@ import FreeCAD import FreeCADGui from bimtests.TestArchBase import TestArchBase + class TestArchBaseGui(TestArchBase): """ The base class for all Arch/BIM GUI unit tests. @@ -46,7 +47,7 @@ class TestArchBaseGui(TestArchBase): # Activating the workbench ensures all GUI commands are loaded and ready. # TODO: commenting out this line for now as it causes a timeout without further logging in # CI - #FreeCADGui.activateWorkbench("BIMWorkbench") + # FreeCADGui.activateWorkbench("BIMWorkbench") def setUp(self): """ @@ -64,10 +65,10 @@ class TestArchBaseGui(TestArchBase): """ try: from PySide import QtCore + loop = QtCore.QEventLoop() QtCore.QTimer.singleShot(int(timeout_ms), loop.quit) loop.exec_() except Exception: # Best-effort: if Qt isn't present or event pumping fails, continue. pass - diff --git a/src/Mod/BIM/bimtests/TestArchBuildingPart.py b/src/Mod/BIM/bimtests/TestArchBuildingPart.py index 6fb9f82c26..ad57e6ee2b 100644 --- a/src/Mod/BIM/bimtests/TestArchBuildingPart.py +++ b/src/Mod/BIM/bimtests/TestArchBuildingPart.py @@ -27,6 +27,7 @@ import FreeCAD as App import Arch from bimtests import TestArchBase + class TestArchBuildingPart(TestArchBase.TestArchBase): def testMakeFloorEmpty(self): @@ -43,20 +44,20 @@ class TestArchBuildingPart(TestArchBase.TestArchBase): self.assertEqual(floor.Label, "Level", "Default label is incorrect") def testFloor(self): - App.Console.PrintLog ('Checking Arch Floor...\n') + App.Console.PrintLog("Checking Arch Floor...\n") structure = Arch.makeStructure(length=2, width=3, height=5) floor = Arch.makeFloor([structure]) - self.assertTrue(floor,"Arch Floor failed") + self.assertTrue(floor, "Arch Floor failed") def testBuilding(self): - App.Console.PrintLog ('Checking Arch Building...\n') + App.Console.PrintLog("Checking Arch Building...\n") structure = Arch.makeStructure(length=2, width=3, height=5) floor = Arch.makeFloor([structure]) building = Arch.makeBuilding([floor]) self.assertTrue(building, "Arch Building failed") def testSite(self): - App.Console.PrintLog('Checking Arch Site...\n') + App.Console.PrintLog("Checking Arch Site...\n") structure = Arch.makeStructure(length=2, width=3, height=5) floor = Arch.makeFloor([structure]) building = Arch.makeBuilding([floor]) @@ -98,7 +99,9 @@ class TestArchBuildingPart(TestArchBase.TestArchBase): # Create a mock floor object floor = Arch.makeFloor() Arch.convertFloors(floor) - self.assertEqual(floor.IfcType, "Building Storey", "convertFloors failed to set IfcType correctly") + self.assertEqual( + floor.IfcType, "Building Storey", "convertFloors failed to set IfcType correctly" + ) def test_make2DDrawing(self): """Test the make2DDrawing function.""" diff --git a/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py b/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py index fb978c5766..40f19b857d 100644 --- a/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py +++ b/src/Mod/BIM/bimtests/TestArchBuildingPartGui.py @@ -6,28 +6,28 @@ import Part import Sketcher from bimtests.TestArchBaseGui import TestArchBaseGui + class TestArchBuildingPartGui(TestArchBaseGui): def testBuildingPart(self): - """Create a BuildingPart from a wall with a window and check its shape. - """ + """Create a BuildingPart from a wall with a window and check its shape.""" # Also regression test for: # https://github.com/FreeCAD/FreeCAD/issues/6178 - #operation = "Arch BuildingPart" - #_msg(" Test '{}'".format(operation)) + # operation = "Arch BuildingPart" + # _msg(" Test '{}'".format(operation)) # Most of the code below taken from testWindow function. line = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0)) wall = Arch.makeWall(line) sk = App.ActiveDocument.addObject("Sketcher::SketchObject", "Sketch001") sk.Placement.Rotation = App.Rotation(App.Vector(1, 0, 0), 90) - sk.addGeometry(Part.LineSegment(App.Vector( 500, 800, 0), App.Vector(1500, 800, 0))) - sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0))) - sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector( 500, 2000, 0))) - sk.addGeometry(Part.LineSegment(App.Vector( 500, 2000, 0), App.Vector( 500, 800, 0))) - sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + sk.addGeometry(Part.LineSegment(App.Vector(500, 800, 0), App.Vector(1500, 800, 0))) + sk.addGeometry(Part.LineSegment(App.Vector(1500, 800, 0), App.Vector(1500, 2000, 0))) + sk.addGeometry(Part.LineSegment(App.Vector(1500, 2000, 0), App.Vector(500, 2000, 0))) + sk.addGeometry(Part.LineSegment(App.Vector(500, 2000, 0), App.Vector(500, 800, 0))) + sk.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 1, 2, 2, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 2, 2, 3, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 3, 2, 0, 1)) App.ActiveDocument.recompute() win = Arch.makeWindow(sk) Arch.removeComponents(win, host=wall) @@ -36,12 +36,12 @@ class TestArchBuildingPartGui(TestArchBaseGui): # Wall visibility works when standalone FreeCADGui.Selection.clearSelection() - FreeCADGui.Selection.addSelection(self.doc_name,wall.Name) + FreeCADGui.Selection.addSelection(self.doc_name, wall.Name) assert wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) App.ActiveDocument.recompute() assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) assert wall.Visibility bp.Group = [wall] @@ -50,30 +50,30 @@ class TestArchBuildingPartGui(TestArchBaseGui): # self.assertTrue(len(bp.Shape.Faces) == 16, "'{}' failed".format(operation)) # Wall visibility works when inside a BuildingPart - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) App.ActiveDocument.recompute() assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) assert wall.Visibility # Wall visibility works when BuildingPart Toggled FreeCADGui.Selection.clearSelection() - FreeCADGui.Selection.addSelection(self.doc_name,bp.Name) - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.Selection.addSelection(self.doc_name, bp.Name) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) assert wall.Visibility # Wall visibiity works inside group inside BuildingPart Toggled - grp = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group") - grp.Label="Group" + grp = App.ActiveDocument.addObject("App::DocumentObjectGroup", "Group") + grp.Label = "Group" grp.Group = [wall] bp.Group = [grp] App.ActiveDocument.recompute() assert wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) App.ActiveDocument.recompute() assert not wall.Visibility - FreeCADGui.runCommand('Std_ToggleVisibility',0) + FreeCADGui.runCommand("Std_ToggleVisibility", 0) App.ActiveDocument.recompute() - assert wall.Visibility \ No newline at end of file + assert wall.Visibility diff --git a/src/Mod/BIM/bimtests/TestArchComponent.py b/src/Mod/BIM/bimtests/TestArchComponent.py index 81b50812bf..76a4bf17f4 100644 --- a/src/Mod/BIM/bimtests/TestArchComponent.py +++ b/src/Mod/BIM/bimtests/TestArchComponent.py @@ -33,33 +33,34 @@ from draftutils.messages import _msg from math import pi, cos, sin, radians + class TestArchComponent(TestArchBase.TestArchBase): def testAdd(self): - App.Console.PrintLog ('Checking Arch Add...\n') - l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0)) - w = Arch.makeWall(l,width=0.2,height=2) - sb = Part.makeBox(1,1,1) - b = App.ActiveDocument.addObject('Part::Feature','Box') + App.Console.PrintLog("Checking Arch Add...\n") + l = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(2, 0, 0)) + w = Arch.makeWall(l, width=0.2, height=2) + sb = Part.makeBox(1, 1, 1) + b = App.ActiveDocument.addObject("Part::Feature", "Box") b.Shape = sb App.ActiveDocument.recompute() - Arch.addComponents(b,w) + Arch.addComponents(b, w) App.ActiveDocument.recompute() - r = (w.Shape.Volume > 1.5) - self.assertTrue(r,"Arch Add failed") + r = w.Shape.Volume > 1.5 + self.assertTrue(r, "Arch Add failed") def testRemove(self): - App.Console.PrintLog ('Checking Arch Remove...\n') - l=Draft.makeLine(App.Vector(0,0,0),App.Vector(2,0,0)) - w = Arch.makeWall(l,width=0.2,height=2,align="Right") - sb = Part.makeBox(1,1,1) - b = App.ActiveDocument.addObject('Part::Feature','Box') + App.Console.PrintLog("Checking Arch Remove...\n") + l = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(2, 0, 0)) + w = Arch.makeWall(l, width=0.2, height=2, align="Right") + sb = Part.makeBox(1, 1, 1) + b = App.ActiveDocument.addObject("Part::Feature", "Box") b.Shape = sb App.ActiveDocument.recompute() - Arch.removeComponents(b,w) + Arch.removeComponents(b, w) App.ActiveDocument.recompute() - r = (w.Shape.Volume < 0.75) - self.assertTrue(r,"Arch Remove failed") + r = w.Shape.Volume < 0.75 + self.assertTrue(r, "Arch Remove failed") def testBsplineSlabAreas(self): """Test the HorizontalArea and VerticalArea properties of a Bspline-based slab. @@ -76,8 +77,8 @@ class TestArchComponent(TestArchBase.TestArchBase): radius = 10000 # 10 meters in mm extrusionLength = 100 # 10 cm in mm numPoints = 50 # Number of points for B-spline - startAngle = 0 # Start at 0 degrees (right) - endAngle = 180 # End at 180 degrees (left) + startAngle = 0 # Start at 0 degrees (right) + endAngle = 180 # End at 180 degrees (left) # Create points for semicircle points = [] @@ -97,9 +98,7 @@ class TestArchComponent(TestArchBase.TestArchBase): # Create sketch # We do this because Draft.make_wires does not support B-splines # and we need a closed wire for the slab - sketch = Draft.makeSketch([bspline, closingLine], - autoconstraints=True, - delete=True) + sketch = Draft.makeSketch([bspline, closingLine], autoconstraints=True, delete=True) if sketch is None: self.fail("Sketch creation failed") sketch.recompute() @@ -120,21 +119,25 @@ class TestArchComponent(TestArchBase.TestArchBase): # Optimally wrapped assertions self.assertAlmostEqual( - actualHorizontalArea, theoreticalHorizontalArea, places=3, + actualHorizontalArea, + theoreticalHorizontalArea, + places=3, msg=( "Horizontal area > 0.1% tolerance | " f"Exp: {theoreticalHorizontalArea:.3f} m² | " f"Got: {actualHorizontalArea:.3f} m²" - ) + ), ) self.assertAlmostEqual( - actualVerticalArea, theoreticalVerticalArea, places=3, + actualVerticalArea, + theoreticalVerticalArea, + places=3, msg=( "Vertical area > 0.1% tolerance | " f"Exp: {theoreticalVerticalArea:.3f} m² | " f"Got: {actualVerticalArea:.3f} m²" - ) + ), ) def testHouseSpaceAreas(self): @@ -150,9 +153,9 @@ class TestArchComponent(TestArchBase.TestArchBase): # Dimensional parameters (all in mm) baseLength = 5000 # 5m along X-axis - baseWidth = 5000 # 5m along Y-axis (extrusion depth) + baseWidth = 5000 # 5m along Y-axis (extrusion depth) rectangleHeight = 2500 # 2.5m lower rectangular portion - triangleHeight = 2500 # 2.5m upper triangular portion + triangleHeight = 2500 # 2.5m upper triangular portion totalHeight = rectangleHeight + triangleHeight # 5m total height # Create envelope profile points (XZ plane) @@ -160,8 +163,8 @@ class TestArchComponent(TestArchBase.TestArchBase): App.Vector(0, 0, 0), App.Vector(baseLength, 0, 0), App.Vector(baseLength, 0, rectangleHeight), - App.Vector(baseLength/2, 0, totalHeight), - App.Vector(0, 0, rectangleHeight) + App.Vector(baseLength / 2, 0, totalHeight), + App.Vector(0, 0, rectangleHeight), ] # Create wire with automatic face creation @@ -203,15 +206,19 @@ class TestArchComponent(TestArchBase.TestArchBase): actualVerticalArea = space.VerticalArea.getValueAs("m^2").Value self.assertAlmostEqual( - actualHorizontalArea, theoreticalHorizontalArea, places=3, + actualHorizontalArea, + theoreticalHorizontalArea, + places=3, msg=f"Horizontal area > 0.1% | Exp: {theoreticalHorizontalArea:.3f} | " - f"Got: {actualHorizontalArea:.3f}" + f"Got: {actualHorizontalArea:.3f}", ) self.assertAlmostEqual( - actualVerticalArea, theoreticalVerticalArea, places=3, + actualVerticalArea, + theoreticalVerticalArea, + places=3, msg=f"Vertical area > 0.1% | Exp: {theoreticalVerticalArea:.3f} | " - f"Got: {actualVerticalArea:.3f}" + f"Got: {actualVerticalArea:.3f}", ) def test_remove_single_window_from_wall_host_is_none(self): @@ -223,7 +230,7 @@ class TestArchComponent(TestArchBase.TestArchBase): """ # Create a basic wall - wall_base = Draft.makeLine(App.Vector(0,0,0), App.Vector(3000,0,0)) + wall_base = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(3000, 0, 0)) wall = Arch.makeWall(wall_base, width=200, height=2500) # Create a window to be added to the wall @@ -235,8 +242,8 @@ class TestArchComponent(TestArchBase.TestArchBase): window_base = Draft.makeRectangle(length=window_width, height=window_height) window = Arch.makeWindow(baseobj=window_base) - window.Width = window_width # Manually set as makeWindow(base) doesn't - window.Height = window_height # Manually set + window.Width = window_width # Manually set as makeWindow(base) doesn't + window.Height = window_height # Manually set self.document.recompute() @@ -251,7 +258,7 @@ class TestArchComponent(TestArchBase.TestArchBase): # Simulate the Arch_Remove command with the scenario where only the window # is selected and "Remove" is used. Arch.removeComponents([window], host=None) - self.document.recompute() # Important for Arch objects to update their state. + self.document.recompute() # Important for Arch objects to update their state. # Assert: the wall should no longer be in the window's Hosts list. self.assertNotIn(wall, window.Hosts, "Wall should not be in window.Hosts after removal.") diff --git a/src/Mod/BIM/bimtests/TestArchCurtainWall.py b/src/Mod/BIM/bimtests/TestArchCurtainWall.py index 6b1ad10f1b..431349bf82 100644 --- a/src/Mod/BIM/bimtests/TestArchCurtainWall.py +++ b/src/Mod/BIM/bimtests/TestArchCurtainWall.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchCurtainWall(TestArchBase.TestArchBase): def test_makeCurtainWall(self): @@ -33,5 +34,7 @@ class TestArchCurtainWall(TestArchBase.TestArchBase): self.printTestMessage(operation) curtain_wall = Arch.makeCurtainWall(name="TestCurtainWall") - self.assertIsNotNone(curtain_wall, "makeCurtainWall failed to create a curtain wall object.") - self.assertEqual(curtain_wall.Label, "TestCurtainWall", "Curtain wall label is incorrect.") \ No newline at end of file + self.assertIsNotNone( + curtain_wall, "makeCurtainWall failed to create a curtain wall object." + ) + self.assertEqual(curtain_wall.Label, "TestCurtainWall", "Curtain wall label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchEquipment.py b/src/Mod/BIM/bimtests/TestArchEquipment.py index 22d5f57f0f..5ba0b38d12 100644 --- a/src/Mod/BIM/bimtests/TestArchEquipment.py +++ b/src/Mod/BIM/bimtests/TestArchEquipment.py @@ -26,6 +26,7 @@ import Arch import FreeCAD as App from bimtests import TestArchBase + class TestArchEquipment(TestArchBase.TestArchBase): def test_makeEquipment(self): @@ -41,4 +42,4 @@ class TestArchEquipment(TestArchBase.TestArchBase): box.Width = 2000 box.Height = 600 equip = Arch.makeEquipment(box) - self.assertTrue(equip,"Arch Equipment failed") \ No newline at end of file + self.assertTrue(equip, "Arch Equipment failed") diff --git a/src/Mod/BIM/bimtests/TestArchFence.py b/src/Mod/BIM/bimtests/TestArchFence.py index d6f5d4d215..c44cc94806 100644 --- a/src/Mod/BIM/bimtests/TestArchFence.py +++ b/src/Mod/BIM/bimtests/TestArchFence.py @@ -27,6 +27,7 @@ import Arch import Draft from bimtests import TestArchBase + class TestArchFence(TestArchBase.TestArchBase): def test_makeFence(self): diff --git a/src/Mod/BIM/bimtests/TestArchFrame.py b/src/Mod/BIM/bimtests/TestArchFrame.py index ea83420334..fd57fa76e4 100644 --- a/src/Mod/BIM/bimtests/TestArchFrame.py +++ b/src/Mod/BIM/bimtests/TestArchFrame.py @@ -27,6 +27,7 @@ import Draft import FreeCAD as App from bimtests import TestArchBase + class TestArchFrame(TestArchBase.TestArchBase): def test_makeFrame(self): @@ -37,7 +38,7 @@ class TestArchFrame(TestArchBase.TestArchBase): self.assertEqual(obj.Label, "Frame", "Incorrect default label for Frame") def testFrame(self): - l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0)) - p = Draft.makeRectangle(length=.5,height=.5) - f = Arch.makeFrame(l,p) - self.assertTrue(f,"Arch Frame failed") \ No newline at end of file + l = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(-2, 0, 0)) + p = Draft.makeRectangle(length=0.5, height=0.5) + f = Arch.makeFrame(l, p) + self.assertTrue(f, "Arch Frame failed") diff --git a/src/Mod/BIM/bimtests/TestArchGrid.py b/src/Mod/BIM/bimtests/TestArchGrid.py index 1a2fa010f1..ff8ce453df 100644 --- a/src/Mod/BIM/bimtests/TestArchGrid.py +++ b/src/Mod/BIM/bimtests/TestArchGrid.py @@ -27,6 +27,7 @@ import Arch import Draft from bimtests import TestArchBase + class TestArchGrid(TestArchBase.TestArchBase): def test_makeGrid(self): @@ -37,4 +38,4 @@ class TestArchGrid(TestArchBase.TestArchBase): # Call makeGrid with only the name parameter grid = Arch.makeGrid(name="TestGrid") self.assertIsNotNone(grid, "makeGrid failed to create a grid object.") - self.assertEqual(grid.Label, "TestGrid", "Grid label is incorrect.") \ No newline at end of file + self.assertEqual(grid.Label, "TestGrid", "Grid label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchImportersGui.py b/src/Mod/BIM/bimtests/TestArchImportersGui.py index 7337972151..4dfe0a96e3 100644 --- a/src/Mod/BIM/bimtests/TestArchImportersGui.py +++ b/src/Mod/BIM/bimtests/TestArchImportersGui.py @@ -1,12 +1,11 @@ - import FreeCAD as App from bimtests.TestArchBaseGui import TestArchBaseGui + class TestArchImportersGui(TestArchBaseGui): def testImportSH3D(self): - """Import a SweetHome 3D file - """ + """Import a SweetHome 3D file""" import BIM.importers.importSH3DHelper importer = BIM.importers.importSH3DHelper.SH3DImporter(None) diff --git a/src/Mod/BIM/bimtests/TestArchMaterial.py b/src/Mod/BIM/bimtests/TestArchMaterial.py index 45b903ebb7..42ae59ffb9 100644 --- a/src/Mod/BIM/bimtests/TestArchMaterial.py +++ b/src/Mod/BIM/bimtests/TestArchMaterial.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchMaterial(TestArchBase.TestArchBase): def test_makeMaterial(self): @@ -42,8 +43,12 @@ class TestArchMaterial(TestArchBase.TestArchBase): self.printTestMessage(operation) multi_material = Arch.makeMultiMaterial(name="TestMultiMaterial") - self.assertIsNotNone(multi_material, "makeMultiMaterial failed to create a multi-material object.") - self.assertEqual(multi_material.Label, "TestMultiMaterial", "Multi-material label is incorrect.") + self.assertIsNotNone( + multi_material, "makeMultiMaterial failed to create a multi-material object." + ) + self.assertEqual( + multi_material.Label, "TestMultiMaterial", "Multi-material label is incorrect." + ) def test_getMaterialContainer(self): """Test the getMaterialContainer function.""" @@ -51,7 +56,9 @@ class TestArchMaterial(TestArchBase.TestArchBase): self.printTestMessage(operation) container = Arch.getMaterialContainer() - self.assertIsNotNone(container, "getMaterialContainer failed to retrieve or create a material container.") + self.assertIsNotNone( + container, "getMaterialContainer failed to retrieve or create a material container." + ) self.assertEqual(container.Label, "Materials", "Material container label is incorrect.") def test_getDocumentMaterials(self): @@ -60,4 +67,4 @@ class TestArchMaterial(TestArchBase.TestArchBase): self.printTestMessage(operation) materials = Arch.getDocumentMaterials() - self.assertIsInstance(materials, list, "getDocumentMaterials did not return a list.") \ No newline at end of file + self.assertIsInstance(materials, list, "getDocumentMaterials did not return a list.") diff --git a/src/Mod/BIM/bimtests/TestArchPanel.py b/src/Mod/BIM/bimtests/TestArchPanel.py index cab28a8177..638337cdeb 100644 --- a/src/Mod/BIM/bimtests/TestArchPanel.py +++ b/src/Mod/BIM/bimtests/TestArchPanel.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchPanel(TestArchBase.TestArchBase): def test_makePanel(self): @@ -55,4 +56,4 @@ class TestArchPanel(TestArchBase.TestArchBase): panel_sheet = Arch.makePanelSheet(name="TestPanelSheet") self.assertIsNotNone(panel_sheet, "makePanelSheet failed to create a panel sheet object.") - self.assertEqual(panel_sheet.Label, "TestPanelSheet", "Panel sheet label is incorrect.") \ No newline at end of file + self.assertEqual(panel_sheet.Label, "TestPanelSheet", "Panel sheet label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchPipe.py b/src/Mod/BIM/bimtests/TestArchPipe.py index 9371a0437c..b02096f1ea 100644 --- a/src/Mod/BIM/bimtests/TestArchPipe.py +++ b/src/Mod/BIM/bimtests/TestArchPipe.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchPipe(TestArchBase.TestArchBase): def test_makePipe(self): @@ -44,5 +45,7 @@ class TestArchPipe(TestArchBase.TestArchBase): pipe1 = Arch.makePipe(diameter=200, length=1000, name="Pipe1") pipe2 = Arch.makePipe(diameter=200, length=1000, name="Pipe2") connector = Arch.makePipeConnector([pipe1, pipe2], radius=100, name="TestConnector") - self.assertIsNotNone(connector, "makePipeConnector failed to create a pipe connector object.") - self.assertEqual(connector.Label, "TestConnector", "Pipe connector label is incorrect.") \ No newline at end of file + self.assertIsNotNone( + connector, "makePipeConnector failed to create a pipe connector object." + ) + self.assertEqual(connector.Label, "TestConnector", "Pipe connector label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchProfile.py b/src/Mod/BIM/bimtests/TestArchProfile.py index 5fff84b0c1..962baf0021 100644 --- a/src/Mod/BIM/bimtests/TestArchProfile.py +++ b/src/Mod/BIM/bimtests/TestArchProfile.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchProfile(TestArchBase.TestArchBase): def test_makeProfile(self): @@ -32,6 +33,6 @@ class TestArchProfile(TestArchBase.TestArchBase): operation = "Testing makeProfile function" self.printTestMessage(operation) - profile = Arch.makeProfile(profile=[0, 'REC', 'REC100x100', 'R', 100, 100]) + profile = Arch.makeProfile(profile=[0, "REC", "REC100x100", "R", 100, 100]) self.assertIsNotNone(profile, "makeProfile failed to create a profile object.") - self.assertEqual(profile.Label, "REC100x100_", "Profile label is incorrect.") \ No newline at end of file + self.assertEqual(profile.Label, "REC100x100_", "Profile label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchProject.py b/src/Mod/BIM/bimtests/TestArchProject.py index ae9d9ca246..e2384769a3 100644 --- a/src/Mod/BIM/bimtests/TestArchProject.py +++ b/src/Mod/BIM/bimtests/TestArchProject.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchProject(TestArchBase.TestArchBase): def test_makeProject(self): @@ -34,4 +35,4 @@ class TestArchProject(TestArchBase.TestArchBase): project = Arch.makeProject(name="TestProject") self.assertIsNotNone(project, "makeProject failed to create a project object.") - self.assertEqual(project.Label, "TestProject", "Project label is incorrect.") \ No newline at end of file + self.assertEqual(project.Label, "TestProject", "Project label is incorrect.") diff --git a/src/Mod/BIM/bimtests/TestArchRebar.py b/src/Mod/BIM/bimtests/TestArchRebar.py index 5253804f25..c08038db2a 100644 --- a/src/Mod/BIM/bimtests/TestArchRebar.py +++ b/src/Mod/BIM/bimtests/TestArchRebar.py @@ -27,14 +27,16 @@ import Arch import Part from bimtests import TestArchBase + class TestArchRebar(TestArchBase.TestArchBase): def _create_sketch(self, add_line=True, length=500.0): """Helper function to create a basic sketch.""" sketch = self.document.addObject("Sketcher::SketchObject") if add_line: - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), - FreeCAD.Vector(length,0,0)), False) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(length, 0, 0)), False + ) self.document.recompute() return sketch @@ -55,8 +57,9 @@ class TestArchRebar(TestArchBase.TestArchBase): self.assertIsNotNone(rebar, "makeRebar failed to create a rebar object.") self.assertEqual(rebar.Label, "Rebar", "Rebar default label should be 'Rebar'.") - self.assertTrue(rebar.Name.startswith("Rebar"), - "Rebar internal name should start with 'Rebar'.") + self.assertTrue( + rebar.Name.startswith("Rebar"), "Rebar internal name should start with 'Rebar'." + ) def test_makeRebar_custom_name(self): """Test makeRebar with a custom name sets Label and Mark correctly.""" @@ -67,8 +70,7 @@ class TestArchRebar(TestArchBase.TestArchBase): self.assertIsNotNone(rebar, "makeRebar failed to create a rebar object.") self.assertEqual(rebar.Label, custom_name, "Rebar label is incorrect.") - self.assertEqual(rebar.Mark, custom_name, - "Rebar mark should match label by default.") + self.assertEqual(rebar.Mark, custom_name, "Rebar mark should match label by default.") def test_makeRebar_with_sketch_and_host(self): """Test makeRebar with both sketch and host object links them correctly.""" @@ -91,8 +93,9 @@ class TestArchRebar(TestArchBase.TestArchBase): self.assertIsNotNone(rebar.Shape, "Rebar.Shape should not be None after recompute.") self.assertTrue(rebar.Shape.isValid(), "Rebar.Shape should be valid.") self.assertGreater(rebar.Shape.Length, 0, "Rebar shape seems to have no length.") - self.assertGreater(rebar.TotalLength.Value, 0, - "Rebar total length should be greater than 0.") + self.assertGreater( + rebar.TotalLength.Value, 0, "Rebar total length should be greater than 0." + ) def test_makeRebar_with_sketch_only(self): """Test makeRebar with only sketch results in no host and valid geometry.""" @@ -119,21 +122,14 @@ class TestArchRebar(TestArchBase.TestArchBase): self.assertIsNone(rebar.Base, "Rebar.Base should be None in this mode.") self.assertIsNotNone(rebar.Shape, "Rebar.Shape should be set from baseobj.") self.assertTrue(rebar.Shape.isValid()) - self.assertGreater(rebar.Shape.Volume, 0, - "Converted rebar shape should have volume.") + self.assertGreater(rebar.Shape.Volume, 0, "Converted rebar shape should have volume.") def test_makeRebar_properties_set_correctly(self): """Test makeRebar sets dimensional and calculated properties correctly.""" sketch = self._create_sketch(length=900) host = self._create_host_structure(length=1200) - rebar = Arch.makeRebar( - baseobj=host, - sketch=sketch, - diameter=12.5, - amount=7, - offset=30.0 - ) + rebar = Arch.makeRebar(baseobj=host, sketch=sketch, diameter=12.5, amount=7, offset=30.0) # Default label "Rebar" will be used, and Mark will also be "Rebar" self.document.recompute() @@ -147,19 +143,26 @@ class TestArchRebar(TestArchBase.TestArchBase): self.assertTrue(hasattr(rebar, "Length"), "Rebar should have Length property.") self.assertTrue(hasattr(rebar, "TotalLength"), "Rebar should have TotalLength property.") - self.assertAlmostEqual(rebar.Length.Value, sketch.Shape.Length, delta=1e-9, - msg="Single rebar length should match sketch length.") + self.assertAlmostEqual( + rebar.Length.Value, + sketch.Shape.Length, + delta=1e-9, + msg="Single rebar length should match sketch length.", + ) expected_total_length = rebar.Length.Value * rebar.Amount - self.assertAlmostEqual(rebar.TotalLength.Value, expected_total_length, delta=1e-9, - msg="TotalLength calculation is incorrect.") + self.assertAlmostEqual( + rebar.TotalLength.Value, + expected_total_length, + delta=1e-9, + msg="TotalLength calculation is incorrect.", + ) def test_makeRebar_no_sketch_no_baseobj(self): """Test makeRebar with no sketch/baseobj creates object with no meaningful shape.""" rebar = Arch.makeRebar(diameter=10, amount=1) self.document.recompute() - self.assertIsNotNone(rebar, - "makeRebar should create an object even with minimal inputs.") + self.assertIsNotNone(rebar, "makeRebar should create an object even with minimal inputs.") self.assertIsNone(rebar.Base, "Rebar.Base should be None.") self.assertIsNone(rebar.Host, "Rebar.Host should be None.") self.assertAlmostEqual(rebar.Diameter.Value, 10.0, delta=1e-9) @@ -169,28 +172,38 @@ class TestArchRebar(TestArchBase.TestArchBase): # For a rebar with no Base, its execute() returns early, so no geometry is made. # The Shape should be the default null/empty shape. self.assertIsNotNone(rebar.Shape, "Rebar object should always have a Shape attribute.") - self.assertTrue(rebar.Shape.isNull(), - "Rebar.Shape should be a null shape if no Base is provided.") + self.assertTrue( + rebar.Shape.isNull(), "Rebar.Shape should be a null shape if no Base is provided." + ) def test_RebarMe(self): """ Tests the creation of an Arch Rebar """ import Sketcher - self.printTestMessage('Checking Arch Rebar...\n') + + self.printTestMessage("Checking Arch Rebar...\n") s = Arch.makeStructure(length=2, width=3, height=5) - sk = self.document.addObject('Sketcher::SketchObject', 'Sketch') + sk = self.document.addObject("Sketcher::SketchObject", "Sketch") sk.AttachmentSupport = (s, ["Face6"]) - sk.addGeometry(Part.LineSegment(FreeCAD.Vector(-0.85, 1.25, 0), FreeCAD.Vector(0.75, 1.25, 0))) - sk.addGeometry(Part.LineSegment(FreeCAD.Vector(0.75, 1.25, 0), FreeCAD.Vector(0.75, -1.20, 0))) - sk.addGeometry(Part.LineSegment(FreeCAD.Vector(0.75, -1.20, 0), FreeCAD.Vector(-0.85, -1.20, 0))) - sk.addGeometry(Part.LineSegment(FreeCAD.Vector(-0.85, -1.20, 0), FreeCAD.Vector(-0.85, 1.25, 0))) - sk.addConstraint(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) - sk.addConstraint(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + sk.addGeometry( + Part.LineSegment(FreeCAD.Vector(-0.85, 1.25, 0), FreeCAD.Vector(0.75, 1.25, 0)) + ) + sk.addGeometry( + Part.LineSegment(FreeCAD.Vector(0.75, 1.25, 0), FreeCAD.Vector(0.75, -1.20, 0)) + ) + sk.addGeometry( + Part.LineSegment(FreeCAD.Vector(0.75, -1.20, 0), FreeCAD.Vector(-0.85, -1.20, 0)) + ) + sk.addGeometry( + Part.LineSegment(FreeCAD.Vector(-0.85, -1.20, 0), FreeCAD.Vector(-0.85, 1.25, 0)) + ) + sk.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 1, 2, 2, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 2, 2, 3, 1)) + sk.addConstraint(Sketcher.Constraint("Coincident", 3, 2, 0, 1)) self.document.recompute() - r = Arch.makeRebar(s, sk, diameter=.1, amount=2) + r = Arch.makeRebar(s, sk, diameter=0.1, amount=2) self.document.recompute() self.assertTrue(r, "Arch Rebar creation failed") - self.assertFalse(r.Shape.isNull(), "Rebar shape is null") \ No newline at end of file + self.assertFalse(r.Shape.isNull(), "Rebar shape is null") diff --git a/src/Mod/BIM/bimtests/TestArchReference.py b/src/Mod/BIM/bimtests/TestArchReference.py index ea7fef5c1b..e63f463846 100644 --- a/src/Mod/BIM/bimtests/TestArchReference.py +++ b/src/Mod/BIM/bimtests/TestArchReference.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchReference(TestArchBase.TestArchBase): def test_makeReference(self): @@ -34,4 +35,4 @@ class TestArchReference(TestArchBase.TestArchBase): obj = Arch.makeReference() self.assertIsNotNone(obj, "makeReference failed to create an object") - self.assertEqual(obj.Label, "External Reference", "Incorrect default label for Reference") \ No newline at end of file + self.assertEqual(obj.Label, "External Reference", "Incorrect default label for Reference") diff --git a/src/Mod/BIM/bimtests/TestArchRoof.py b/src/Mod/BIM/bimtests/TestArchRoof.py index b850f0bb00..ccac71fcc8 100644 --- a/src/Mod/BIM/bimtests/TestArchRoof.py +++ b/src/Mod/BIM/bimtests/TestArchRoof.py @@ -32,31 +32,31 @@ import FreeCAD as App from bimtests import TestArchBase from draftutils.messages import _msg + class TestArchRoof(TestArchBase.TestArchBase): def testRoof(self): operation = "Checking Arch Roof..." self.printTestMessage(operation) - r = Draft.makeRectangle(length=2,height=-1) - r.recompute() # required before calling Arch.makeRoof + r = Draft.makeRectangle(length=2, height=-1) + r.recompute() # required before calling Arch.makeRoof ro = Arch.makeRoof(r) - self.assertTrue(ro,"Arch Roof failed") + self.assertTrue(ro, "Arch Roof failed") def testRoof81Permutations(self): - """Create 81 roofs using a range of arguments. - """ + """Create 81 roofs using a range of arguments.""" operation = "Arch Roof testRoof81Permutations" self.printTestMessage(operation) - pts = [App.Vector( 0, 0, 0), - App.Vector(2000, 0, 0), - App.Vector(4000, 0, 0), - App.Vector(4000, 4000, 0), - App.Vector( 0, 4000, 0)] - ptsMod = [App.Vector(2000, 0, 0), - App.Vector(2000, -1000, 0), - App.Vector(2000, 1000, 0)] + pts = [ + App.Vector(0, 0, 0), + App.Vector(2000, 0, 0), + App.Vector(4000, 0, 0), + App.Vector(4000, 4000, 0), + App.Vector(0, 4000, 0), + ] + ptsMod = [App.Vector(2000, 0, 0), App.Vector(2000, -1000, 0), App.Vector(2000, 1000, 0)] angsMod = [[60, 60], [30, 60], [60, 30]] runsMod = [[500, 500], [400, 500], [500, 400]] overhangsMod = [[100, 100], [100, 200], [200, 100]] @@ -64,24 +64,19 @@ class TestArchRoof(TestArchBase.TestArchBase): pla = App.Placement() for iY in range(9): for iX in range(9): - pts[1] = ptsMod[iY % 3] # to get different edge angles + pts[1] = ptsMod[iY % 3] # to get different edge angles angsLst = angsMod[iY // 3] + [90, 90, 90] runsLst = runsMod[iX % 3] + [0, 0, 0] - overhangsLst = overhangsMod[iX // 3] + [0, 0, 0] + overhangsLst = overhangsMod[iX // 3] + [0, 0, 0] pla.Base = App.Vector(iX * delta, iY * delta, 0) - wire = Draft.makeWire(pts, closed = True) + wire = Draft.makeWire(pts, closed=True) wire.MakeFace = False wire.Placement = pla - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = angsLst, - run = runsLst, - overhang = overhangsLst) + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(wire, angles=angsLst, run=runsLst, overhang=overhangsLst) roof.recompute() - self.assertFalse(roof.Shape.isNull(), - "'{}' failed".format(operation)) - self.assertTrue(roof.Shape.isValid(), - "'{}' failed".format(operation)) + self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) + self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) def testRoofAllAngles90(self): """Create a roof with the angles of all segments set at 90 degrees. @@ -90,16 +85,17 @@ class TestArchRoof(TestArchBase.TestArchBase): operation = "Arch Roof testRoofAllAngles90" self.printTestMessage(operation) - pts = [App.Vector( 0, 0, 0), - App.Vector(2000, 0, 0), - App.Vector(2000, 2000, 0), - App.Vector( 0, 2000, 0)] + pts = [ + App.Vector(0, 0, 0), + App.Vector(2000, 0, 0), + App.Vector(2000, 2000, 0), + App.Vector(0, 2000, 0), + ] - wire = Draft.makeWire(pts, closed = True) + wire = Draft.makeWire(pts, closed=True) wire.MakeFace = False - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = [90, 90, 90, 90]) + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof(wire, angles=[90, 90, 90, 90]) roof.recompute() self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) @@ -111,16 +107,16 @@ class TestArchRoof(TestArchBase.TestArchBase): operation = "Arch Roof testRoofApex" self.printTestMessage(operation) - rec = Draft.makeRectangle(length = 4000, - height = 3000, - face = False) - rec.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(rec, - angles = [30, 40, 50, 60], - run = [2000, 0, 2000, 0], - idrel = [-1, 0, -1, 0], - thickness = [50.0], - overhang = [100.0]) + rec = Draft.makeRectangle(length=4000, height=3000, face=False) + rec.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof( + rec, + angles=[30, 40, 50, 60], + run=[2000, 0, 2000, 0], + idrel=[-1, 0, -1, 0], + thickness=[50.0], + overhang=[100.0], + ) roof.recompute() self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) @@ -132,17 +128,18 @@ class TestArchRoof(TestArchBase.TestArchBase): operation = "Arch Roof testRoofSingleEavePoint" self.printTestMessage(operation) - pts = [App.Vector( 0, 0, 0), - App.Vector( 2000, 0, 0), - App.Vector( 4000, 2000, 0), - App.Vector(-2000, 2000, 0)] - wire = Draft.makeWire(pts, closed = True) + pts = [ + App.Vector(0, 0, 0), + App.Vector(2000, 0, 0), + App.Vector(4000, 2000, 0), + App.Vector(-2000, 2000, 0), + ] + wire = Draft.makeWire(pts, closed=True) wire.MakeFace = False - wire.recompute() # required before calling Arch.makeRoof - roof = Arch.makeRoof(wire, - angles = [45, 90, 90, 90], - run = [1000, 0, 0, 0], - overhang = [1000, 0, 0, 0]) + wire.recompute() # required before calling Arch.makeRoof + roof = Arch.makeRoof( + wire, angles=[45, 90, 90, 90], run=[1000, 0, 0, 0], overhang=[1000, 0, 0, 0] + ) roof.recompute() self.assertFalse(roof.Shape.isNull(), "'{}' failed".format(operation)) self.assertTrue(roof.Shape.isValid(), "'{}' failed".format(operation)) diff --git a/src/Mod/BIM/bimtests/TestArchSchedule.py b/src/Mod/BIM/bimtests/TestArchSchedule.py index 7de65076ca..3999cc9b55 100644 --- a/src/Mod/BIM/bimtests/TestArchSchedule.py +++ b/src/Mod/BIM/bimtests/TestArchSchedule.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchSchedule(TestArchBase.TestArchBase): def test_makeSchedule(self): @@ -34,4 +35,4 @@ class TestArchSchedule(TestArchBase.TestArchBase): obj = Arch.makeSchedule() self.assertIsNotNone(obj, "makeSchedule failed to create an object") - self.assertEqual(obj.Label, "Schedule", "Incorrect default label for Schedule") \ No newline at end of file + self.assertEqual(obj.Label, "Schedule", "Incorrect default label for Schedule") diff --git a/src/Mod/BIM/bimtests/TestArchSectionPlane.py b/src/Mod/BIM/bimtests/TestArchSectionPlane.py index d40aa5c2c3..3abb0e716e 100644 --- a/src/Mod/BIM/bimtests/TestArchSectionPlane.py +++ b/src/Mod/BIM/bimtests/TestArchSectionPlane.py @@ -28,6 +28,7 @@ import os import FreeCAD as App from bimtests import TestArchBase + class TestArchSectionPlane(TestArchBase.TestArchBase): def test_makeSectionPlane(self): @@ -36,8 +37,12 @@ class TestArchSectionPlane(TestArchBase.TestArchBase): self.printTestMessage(operation) section_plane = Arch.makeSectionPlane(name="TestSectionPlane") - self.assertIsNotNone(section_plane, "makeSectionPlane failed to create a section plane object.") - self.assertEqual(section_plane.Label, "TestSectionPlane", "Section plane label is incorrect.") + self.assertIsNotNone( + section_plane, "makeSectionPlane failed to create a section plane object." + ) + self.assertEqual( + section_plane.Label, "TestSectionPlane", "Section plane label is incorrect." + ) def testViewGeneration(self): """Tests the whole TD view generation workflow""" @@ -46,10 +51,22 @@ class TestArchSectionPlane(TestArchBase.TestArchBase): points = [App.Vector(0.0, 0.0, 0.0), App.Vector(2000.0, 0.0, 0.0)] line = Draft.make_wire(points) wall = Arch.makeWall(line, height=2000) - wpl = App.Placement(App.Vector(500,0,1500), App.Vector(1,0,0),-90) - win = Arch.makeWindowPreset('Fixed', width=1000.0, height=1000.0, h1=50.0, h2=50.0, h3=50.0, w1=100.0, w2=50.0, o1=0.0, o2=50.0, placement=wpl) + wpl = App.Placement(App.Vector(500, 0, 1500), App.Vector(1, 0, 0), -90) + win = Arch.makeWindowPreset( + "Fixed", + width=1000.0, + height=1000.0, + h1=50.0, + h2=50.0, + h3=50.0, + w1=100.0, + w2=50.0, + o1=0.0, + o2=50.0, + placement=wpl, + ) win.Hosts = [wall] - profile = Arch.makeProfile([169, 'HEA', 'HEA100', 'H', 100.0, 96.0, 5.0, 8.0]) + profile = Arch.makeProfile([169, "HEA", "HEA100", "H", 100.0, 96.0, 5.0, 8.0]) column = Arch.makeStructure(profile, height=2000.0) column.Profile = "HEA100" column.Placement.Base = App.Vector(500.0, 600.0, 0.0) @@ -68,7 +85,9 @@ class TestArchSectionPlane(TestArchBase.TestArchBase): App.ActiveDocument.recompute() # Create a TD page - tpath = os.path.join(App.getResourceDir(),"Mod","TechDraw","Templates","ISO","A3_Landscape_blank.svg") + tpath = os.path.join( + App.getResourceDir(), "Mod", "TechDraw", "Templates", "ISO", "A3_Landscape_blank.svg" + ) page = App.ActiveDocument.addObject("TechDraw::DrawPage", "Page") template = App.ActiveDocument.addObject("TechDraw::DrawSVGTemplate", "Template") template.Template = tpath @@ -80,4 +99,4 @@ class TestArchSectionPlane(TestArchBase.TestArchBase): view.X = "20cm" view.Y = "15cm" App.ActiveDocument.recompute() - assert True \ No newline at end of file + assert True diff --git a/src/Mod/BIM/bimtests/TestArchSiteGui.py b/src/Mod/BIM/bimtests/TestArchSiteGui.py index efc175b47e..0274ef79b2 100644 --- a/src/Mod/BIM/bimtests/TestArchSiteGui.py +++ b/src/Mod/BIM/bimtests/TestArchSiteGui.py @@ -46,30 +46,35 @@ class TestArchSiteGui(TestArchBaseGui.TestArchBaseGui): vobj = site.ViewObject props = vobj.PropertiesList expected = [ - 'ShowSunPosition', 'SunDateMonth', 'SunDateDay', 'SunTimeHour', - 'SolarDiagramScale', 'SolarDiagramPosition', 'ShowHourLabels' + "ShowSunPosition", + "SunDateMonth", + "SunDateDay", + "SunTimeHour", + "SolarDiagramScale", + "SolarDiagramPosition", + "ShowHourLabels", ] for p in expected: self.assertIn(p, props, f"Property '{p}' missing from ViewObject") # Check defaults where applicable - if hasattr(vobj, 'SunDateMonth'): + if hasattr(vobj, "SunDateMonth"): self.assertEqual(vobj.SunDateMonth, 6) - if hasattr(vobj, 'SunDateDay'): + if hasattr(vobj, "SunDateDay"): self.assertEqual(vobj.SunDateDay, 21) - if hasattr(vobj, 'SunTimeHour'): + if hasattr(vobj, "SunTimeHour"): self.assertAlmostEqual(vobj.SunTimeHour, 12.0) - if hasattr(vobj, 'SolarDiagramScale'): + if hasattr(vobj, "SolarDiagramScale"): self.assertAlmostEqual(vobj.SolarDiagramScale, 20000.0) def test_new_site_save_and_reopen(self): """Test: save document and reopen; view properties must be present and constrained.""" - self.printTestMessage('Save and reopen new Site...') + self.printTestMessage("Save and reopen new Site...") site = Arch.makeSite() FreeCAD.ActiveDocument.recompute() # Save to a temporary file - tf = tempfile.NamedTemporaryFile(delete=False, suffix='.FCStd') + tf = tempfile.NamedTemporaryFile(delete=False, suffix=".FCStd") tf.close() path = tf.name try: @@ -81,10 +86,10 @@ class TestArchSiteGui(TestArchBaseGui.TestArchBaseGui): # Find the Site object in the reopened document by checking proxy type found = None for o in reopened.Objects: - if hasattr(o, 'Proxy') and getattr(o.Proxy, 'Type', None) == 'Site': + if hasattr(o, "Proxy") and getattr(o.Proxy, "Type", None) == "Site": found = o break - self.assertIsNotNone(found, 'Site object not found after reopen') + self.assertIsNotNone(found, "Site object not found after reopen") # Ensure async GUI setup completes self.pump_gui_events() @@ -95,10 +100,14 @@ class TestArchSiteGui(TestArchBaseGui.TestArchBaseGui): # Setting an out-of-bounds value should not raise an exception but # should coerce the value to the nearest limit. vobj.SunDateMonth = 13 - self.assertEqual(vobj.SunDateMonth, 12, "Property should be clamped to its max value") + self.assertEqual( + vobj.SunDateMonth, 12, "Property should be clamped to its max value" + ) vobj.SunDateMonth = 0 - self.assertEqual(vobj.SunDateMonth, 1, "Property should be clamped to its min value") + self.assertEqual( + vobj.SunDateMonth, 1, "Property should be clamped to its min value" + ) finally: # Close reopened document to keep test isolation @@ -124,11 +133,11 @@ class TestArchSiteGui(TestArchBaseGui.TestArchBaseGui): The test waits briefly for the loader's deferred binding and normalization callbacks to run before asserting the migrated value. """ - self.printTestMessage('Save and reopen legacy Site...') - fixtures_dir = os.path.join(os.path.dirname(__file__), 'fixtures') - fname = os.path.join(fixtures_dir, 'FC_site_simple-102.FCStd') + self.printTestMessage("Save and reopen legacy Site...") + fixtures_dir = os.path.join(os.path.dirname(__file__), "fixtures") + fname = os.path.join(fixtures_dir, "FC_site_simple-102.FCStd") if not os.path.exists(fname): - raise unittest.SkipTest('Legacy migration fixture not found; skipping test.') + raise unittest.SkipTest("Legacy migration fixture not found; skipping test.") # If fixture exists, open and validate migration d = FreeCAD.openDocument(fname) @@ -139,12 +148,12 @@ class TestArchSiteGui(TestArchBaseGui.TestArchBaseGui): self.pump_gui_events() site = None for o in d.Objects: - if hasattr(o, 'Proxy') and getattr(o.Proxy, 'Type', None) == 'Site': + if hasattr(o, "Proxy") and getattr(o.Proxy, "Type", None) == "Site": site = o break - assert site is not None, 'No Site found in fixture document' + assert site is not None, "No Site found in fixture document" vobj = site.ViewObject # Example assertion: SolarDiagramScale should be normalized to 20000 - self.assertAlmostEqual(getattr(vobj, 'SolarDiagramScale', 1.0), 20000.0) + self.assertAlmostEqual(getattr(vobj, "SolarDiagramScale", 1.0), 20000.0) finally: FreeCAD.closeDocument(d.Name) diff --git a/src/Mod/BIM/bimtests/TestArchSpace.py b/src/Mod/BIM/bimtests/TestArchSpace.py index a4fe5761c7..64f3bbbbcc 100644 --- a/src/Mod/BIM/bimtests/TestArchSpace.py +++ b/src/Mod/BIM/bimtests/TestArchSpace.py @@ -34,11 +34,21 @@ from FreeCAD import Units from bimtests import TestArchBase import WorkingPlane + def like(a, b): - return abs(a-b) < 0.001 + return abs(a - b) < 0.001 + def checkBB(a, b): - return like(a.XMin, b.XMin) and like(a.YMin, b.YMin) and like(a.ZMin, b.ZMin) and like(a.XMax, b.XMax) and like(a.YMax, b.YMax) and like(a.ZMax, b.ZMax) + return ( + like(a.XMin, b.XMin) + and like(a.YMin, b.YMin) + and like(a.ZMin, b.ZMin) + and like(a.XMax, b.XMax) + and like(a.YMax, b.YMax) + and like(a.ZMax, b.ZMax) + ) + class TestArchSpace(TestArchBase.TestArchBase): @@ -46,11 +56,11 @@ class TestArchSpace(TestArchBase.TestArchBase): operation = "Checking Arch Space..." self.printTestMessage(operation) - sb = Part.makeBox(1,1,1) - b = App.ActiveDocument.addObject('Part::Feature','Box') + sb = Part.makeBox(1, 1, 1) + b = App.ActiveDocument.addObject("Part::Feature", "Box") b.Shape = sb s = Arch.makeSpace([b]) - self.assertTrue(s,"Arch Space failed") + self.assertTrue(s, "Arch Space failed") def testSpaceBBox(self): operation = "Checking Arch Space bound box..." @@ -59,15 +69,14 @@ class TestArchSpace(TestArchBase.TestArchBase): shape = Part.Shape() shape.importBrepFromString(brepArchiCAD) bborig = shape.BoundBox - App.Console.PrintLog ("Original BB: "+str(bborig)) - baseobj = App.ActiveDocument.addObject("Part::Feature","brepArchiCAD_body") + App.Console.PrintLog("Original BB: " + str(bborig)) + baseobj = App.ActiveDocument.addObject("Part::Feature", "brepArchiCAD_body") baseobj.Shape = shape space = Arch.makeSpace(baseobj) space.recompute() bbnew = space.Shape.BoundBox - App.Console.PrintLog ("New BB: "+str(bbnew)) - self.assertTrue(checkBB(bborig,bbnew),"Arch Space has wrong Placement") - + App.Console.PrintLog("New BB: " + str(bbnew)) + self.assertTrue(checkBB(bborig, bbnew), "Arch Space has wrong Placement") def test_addSpaceBoundaries(self): """Test the Arch.addSpaceBoundaries method. @@ -82,21 +91,21 @@ class TestArchSpace(TestArchBase.TestArchBase): pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) pl.Base = App.Vector(-2000.0, -2000.0, 0.0) rectangleBase = Draft.make_rectangle( - length=4000.0, height=4000.0, placement=pl, face=True, support=None) + length=4000.0, height=4000.0, placement=pl, face=True, support=None + ) App.ActiveDocument.recompute() - extr = rectangleBase.Shape.extrude(App.Vector(0,0,2000)) - Part.show(extr, 'Extrusion') - space = Arch.makeSpace(App.activeDocument().getObject('Extrusion')) + extr = rectangleBase.Shape.extrude(App.Vector(0, 0, 2000)) + Part.show(extr, "Extrusion") + space = Arch.makeSpace(App.activeDocument().getObject("Extrusion")) App.ActiveDocument.recompute() # To calculate area # Create the wall - trace = Part.LineSegment(App.Vector (3000.0, 1000.0, 0.0), - App.Vector (-3000.0, 1000.0, 0.0)) + trace = Part.LineSegment(App.Vector(3000.0, 1000.0, 0.0), App.Vector(-3000.0, 1000.0, 0.0)) wp = WorkingPlane.get_working_plane() - base = App.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") + base = App.ActiveDocument.addObject("Sketcher::SketchObject", "WallTrace") base.Placement = wp.get_placement() base.addGeometry(trace) - wall = Arch.makeWall(base,width=200.0,height=3000.0,align="Left") + wall = Arch.makeWall(base, width=200.0, height=3000.0, align="Left") wall.Normal = wp.axis # Add the boundary @@ -105,18 +114,20 @@ class TestArchSpace(TestArchBase.TestArchBase): App.ActiveDocument.recompute() # To recalculate area # Assert if area is as expected - expectedArea = Units.parseQuantity('12 m^2') + expectedArea = Units.parseQuantity("12 m^2") actualArea = Units.parseQuantity(str(space.Area)) self.assertAlmostEqual( expectedArea.Value, actualArea.Value, - msg = (f"Invalid area value. " + - f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}")) + msg=( + f"Invalid area value. " + + f"Expected: {expectedArea.UserString}, actual: {actualArea.UserString}" + ), + ) def test_SpaceFromSingleWall(self): - """Create a space from boundaries of a single wall. - """ + """Create a space from boundaries of a single wall.""" operation = "Arch Space from single wall" self.printTestMessage(operation) @@ -128,19 +139,24 @@ class TestArchSpace(TestArchBase.TestArchBase): pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) pl.Base = App.Vector(0.0, 0.0, 0.0) rectangleBase = Draft.make_rectangle( - length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None) - App.ActiveDocument.recompute() # To calculate rectangle area + length=wallInnerLength, height=wallInnerLength, placement=pl, face=True, support=None + ) + App.ActiveDocument.recompute() # To calculate rectangle area rectangleArea = rectangleBase.Area App.ActiveDocument.getObject(rectangleBase.Name).MakeFace = False wall = Arch.makeWall(baseobj=rectangleBase, height=wallHeight, align="Left") - App.ActiveDocument.recompute() # To calculate face areas + App.ActiveDocument.recompute() # To calculate face areas # Create a space from the wall's inner faces - boundaries = [f"Face{ind+1}" for ind, face in enumerate(wall.Shape.Faces) - if round(face.Area) == round(wallInnerFaceArea)] + boundaries = [ + f"Face{ind+1}" + for ind, face in enumerate(wall.Shape.Faces) + if round(face.Area) == round(wallInnerFaceArea) + ] if App.GuiUp: import FreeCADGui + FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(wall, boundaries) @@ -154,7 +170,7 @@ class TestArchSpace(TestArchBase.TestArchBase): # [ (, ["Face1", ...]), ... ] space = Arch.makeSpace([(wall, boundaries)]) - App.ActiveDocument.recompute() # To calculate space area + App.ActiveDocument.recompute() # To calculate space area # Assert if area is as expected expectedArea = Units.parseQuantity(str(rectangleArea)) @@ -163,7 +179,8 @@ class TestArchSpace(TestArchBase.TestArchBase): self.assertAlmostEqual( expectedArea.Value, actualArea.Value, - msg = f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}") + msg=f"Invalid area value. Expected: {expectedArea.UserString}, actual: {actualArea.UserString}", + ) brepArchiCAD = """ diff --git a/src/Mod/BIM/bimtests/TestArchStairs.py b/src/Mod/BIM/bimtests/TestArchStairs.py index 729e681218..53533f9b03 100644 --- a/src/Mod/BIM/bimtests/TestArchStairs.py +++ b/src/Mod/BIM/bimtests/TestArchStairs.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchStairs(TestArchBase.TestArchBase): def test_makeStairs(self): @@ -67,7 +68,8 @@ class TestArchStairs(TestArchBase.TestArchBase): # Find new railings by name comparison and type checking new_railings = [ - obj for obj in self.document.Objects + obj + for obj in self.document.Objects if obj.Name not in pre_creation_names and hasattr(obj, "Proxy") and getattr(obj.Proxy, "Type", "") == "Pipe" @@ -80,4 +82,4 @@ class TestArchStairs(TestArchBase.TestArchBase): for railing in new_railings: self.assertTrue(hasattr(railing, "Height")) self.assertTrue(hasattr(railing, "Diameter")) - self.assertTrue(hasattr(railing, "Placement")) \ No newline at end of file + self.assertTrue(hasattr(railing, "Placement")) diff --git a/src/Mod/BIM/bimtests/TestArchStructure.py b/src/Mod/BIM/bimtests/TestArchStructure.py index 488b3fb973..a75bf58327 100644 --- a/src/Mod/BIM/bimtests/TestArchStructure.py +++ b/src/Mod/BIM/bimtests/TestArchStructure.py @@ -26,9 +26,10 @@ import FreeCAD as App import Arch from bimtests import TestArchBase + class TestArchStructure(TestArchBase.TestArchBase): def testStructure(self): - App.Console.PrintLog ('Checking BIM Structure...\n') - s = Arch.makeStructure(length=2,width=3,height=5) - self.assertTrue(s,"BIM Structure failed") \ No newline at end of file + App.Console.PrintLog("Checking BIM Structure...\n") + s = Arch.makeStructure(length=2, width=3, height=5) + self.assertTrue(s, "BIM Structure failed") diff --git a/src/Mod/BIM/bimtests/TestArchTruss.py b/src/Mod/BIM/bimtests/TestArchTruss.py index 80a0716f5e..d295a96a29 100644 --- a/src/Mod/BIM/bimtests/TestArchTruss.py +++ b/src/Mod/BIM/bimtests/TestArchTruss.py @@ -25,6 +25,7 @@ import Arch from bimtests import TestArchBase + class TestArchTruss(TestArchBase.TestArchBase): def test_makeTruss(self): @@ -34,4 +35,4 @@ class TestArchTruss(TestArchBase.TestArchBase): obj = Arch.makeTruss() self.assertIsNotNone(obj, "makeTruss failed to create an object") - self.assertEqual(obj.Label, "Truss", "Incorrect default label for Truss") \ No newline at end of file + self.assertEqual(obj.Label, "Truss", "Incorrect default label for Truss") diff --git a/src/Mod/BIM/bimtests/TestArchWall.py b/src/Mod/BIM/bimtests/TestArchWall.py index 9b3ba22636..3bb8429554 100644 --- a/src/Mod/BIM/bimtests/TestArchWall.py +++ b/src/Mod/BIM/bimtests/TestArchWall.py @@ -38,9 +38,9 @@ class TestArchWall(TestArchBase.TestArchBase): operation = "Checking Arch Wall..." self.printTestMessage(operation) - l=Draft.makeLine(App.Vector(0,0,0),App.Vector(-2,0,0)) + l = Draft.makeLine(App.Vector(0, 0, 0), App.Vector(-2, 0, 0)) w = Arch.makeWall(l) - self.assertTrue(w,"Arch Wall failed") + self.assertTrue(w, "Arch Wall failed") def testWallMultiMatAlign(self): operation = "Checking Arch Wall with MultiMaterial and 3 alignments..." @@ -50,38 +50,50 @@ class TestArchWall(TestArchBase.TestArchBase): matB = Arch.makeMaterial() matMulti = Arch.makeMultiMaterial() matMulti.Materials = [matA, matB] - matMulti.Thicknesses = [100, 200] # total width different from default 200 - pts = [App.Vector( 0, 0, 0), - App.Vector(1000, 0, 0), - App.Vector(1000, 1000, 0), - App.Vector(2000, 1000, 0)] + matMulti.Thicknesses = [100, 200] # total width different from default 200 + pts = [ + App.Vector(0, 0, 0), + App.Vector(1000, 0, 0), + App.Vector(1000, 1000, 0), + App.Vector(2000, 1000, 0), + ] # wall based on wire: wire = Draft.makeWire(pts) wallWire = Arch.makeWall(wire) wallWire.Material = matMulti # wall based on sketch: - sketch = App.activeDocument().addObject('Sketcher::SketchObject','Sketch') - sketch.addGeometry([Part.LineSegment(pts[0], pts[1]), - Part.LineSegment(pts[1], pts[2]), - Part.LineSegment(pts[2], pts[3])]) + sketch = App.activeDocument().addObject("Sketcher::SketchObject", "Sketch") + sketch.addGeometry( + [ + Part.LineSegment(pts[0], pts[1]), + Part.LineSegment(pts[1], pts[2]), + Part.LineSegment(pts[2], pts[3]), + ] + ) wallSketch = Arch.makeWall(sketch) wallSketch.Material = matMulti alignLst = ["Left", "Center", "Right"] - checkLst = [[App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)], - [App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)], - [App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)]] + checkLst = [ + [App.Vector(0, -300, 0), App.Vector(2000, 1000, 0)], + [App.Vector(0, -150, 0), App.Vector(2000, 1150, 0)], + [App.Vector(0, 0, 0), App.Vector(2000, 1300, 0)], + ] for i in range(3): wallWire.Align = alignLst[i] wallSketch.Align = alignLst[i] App.ActiveDocument.recompute() for box in [wallWire.Shape.BoundBox, wallSketch.Shape.BoundBox]: ptMin = App.Vector(box.XMin, box.YMin, 0) - self.assertTrue(ptMin.isEqual(checkLst[i][0], 1e-8), - "Arch Wall with MultiMaterial and 3 alignments failed") + self.assertTrue( + ptMin.isEqual(checkLst[i][0], 1e-8), + "Arch Wall with MultiMaterial and 3 alignments failed", + ) ptMax = App.Vector(box.XMax, box.YMax, 0) - self.assertTrue(ptMax.isEqual(checkLst[i][1], 1e-8), - "Arch Wall with MultiMaterial and 3 alignments failed") + self.assertTrue( + ptMax.isEqual(checkLst[i][1], 1e-8), + "Arch Wall with MultiMaterial and 3 alignments failed", + ) def test_makeWall(self): """Test the makeWall function.""" diff --git a/src/Mod/BIM/bimtests/TestArchWindow.py b/src/Mod/BIM/bimtests/TestArchWindow.py index 1a6f3ab96a..66c0cbf24b 100644 --- a/src/Mod/BIM/bimtests/TestArchWindow.py +++ b/src/Mod/BIM/bimtests/TestArchWindow.py @@ -25,19 +25,22 @@ import FreeCAD from bimtests import TestArchBase import Arch -import ArchWindow # For ArchWindow._Window proxy class +import ArchWindow # For ArchWindow._Window proxy class import Part import Draft import Sketcher + class TestArchWindow(TestArchBase.TestArchBase): def test_create_no_args(self): """Test creating a window with no arguments.""" window = Arch.makeWindow(name="Window_NoArgs") self.assertIsNotNone(window) - self.assertTrue(hasattr(window, "Proxy") and isinstance(window.Proxy, ArchWindow._Window), - "Window proxy is not of expected ArchWindow._Window type") + self.assertTrue( + hasattr(window, "Proxy") and isinstance(window.Proxy, ArchWindow._Window), + "Window proxy is not of expected ArchWindow._Window type", + ) self.assertEqual(window.Label, "Window_NoArgs") self.assertEqual(window.IfcType, "Window") self.assertIsNone(window.Base) @@ -53,23 +56,33 @@ class TestArchWindow(TestArchBase.TestArchBase): self.document.recompute() self.assertEqual(window.Base, sketch) - self.assertTrue(len(window.WindowParts) >= 5, "Should have at least one default part (5 elements).") + self.assertTrue( + len(window.WindowParts) >= 5, "Should have at least one default part (5 elements)." + ) self.assertEqual(window.WindowParts[0], "Default") - self.assertEqual(window.WindowParts[1], "Solid panel", "Default part type incorrect for single-wire sketch.") + self.assertEqual( + window.WindowParts[1], + "Solid panel", + "Default part type incorrect for single-wire sketch.", + ) self.assertIn("Wire0", window.WindowParts[2]) self.assertFalse(window.Shape.isNull()) self.assertGreater(len(window.Shape.Solids), 0) def test_create_from_sketch_two_wires_default_parts(self): """Test creating a window from two-wire sketch (concentric), relying on default parts.""" - sketch = self._create_sketch_with_wires("SketchTwoWires", [(0, 0, 1000, 1200), (100, 100, 800, 1000)]) + sketch = self._create_sketch_with_wires( + "SketchTwoWires", [(0, 0, 1000, 1200), (100, 100, 800, 1000)] + ) window = Arch.makeWindow(baseobj=sketch, name="Window_SketchTwo_Default") self.document.recompute() self.assertEqual(window.Base, sketch) self.assertGreaterEqual(len(window.WindowParts), 5) self.assertEqual(window.WindowParts[0], "Default") - self.assertEqual(window.WindowParts[1], "Frame", "Default type for multi-wire should be Frame.") + self.assertEqual( + window.WindowParts[1], "Frame", "Default type for multi-wire should be Frame." + ) self.assertIn("Wire0", window.WindowParts[2]) self.assertIn("Wire1", window.WindowParts[2]) self.assertFalse(window.Shape.isNull()) @@ -78,7 +91,9 @@ class TestArchWindow(TestArchBase.TestArchBase): def test_sketch_named_constraints_driven_by_window_props(self): """Test that window Width/Height properties drive sketch's named constraints.""" sketch_width, sketch_height = 800.0, 1000.0 - sketch = self._create_sketch_with_named_constraints("SketchNamed", sketch_width, sketch_height) + sketch = self._create_sketch_with_named_constraints( + "SketchNamed", sketch_width, sketch_height + ) window = Arch.makeWindow(baseobj=sketch, name="Window_NamedSketch") # Set window Width/Height to ensure they become the drivers if sketch also has the constraints. @@ -94,19 +109,37 @@ class TestArchWindow(TestArchBase.TestArchBase): new_win_width = 1200.0 window.Width = new_win_width self.document.recompute() - self.assertEqual(sketch.getDatum("Width").Value, new_win_width, "Window.Width should drive sketch 'Width' constraint.") + self.assertEqual( + sketch.getDatum("Width").Value, + new_win_width, + "Window.Width should drive sketch 'Width' constraint.", + ) new_win_height = 1500.0 window.Height = new_win_height self.document.recompute() - self.assertEqual(sketch.getDatum("Height").Value, new_win_height, "Window.Height should drive sketch 'Height' constraint.") + self.assertEqual( + sketch.getDatum("Height").Value, + new_win_height, + "Window.Height should drive sketch 'Height' constraint.", + ) def test_create_from_sketch_with_custom_parts(self): """Test creating a window from sketch with explicit custom parts.""" - sketch = self._create_sketch_with_wires("SketchCustom", [(0,0,1200,1000), (100,100,1000,800)]) + sketch = self._create_sketch_with_wires( + "SketchCustom", [(0, 0, 1200, 1000), (100, 100, 1000, 800)] + ) custom_parts = [ - "OuterFrame", "Frame", "Wire0,Wire1", "70", "0", - "GlassPane", "Glass panel", "Wire1", "20", "25" + "OuterFrame", + "Frame", + "Wire0,Wire1", + "70", + "0", + "GlassPane", + "Glass panel", + "Wire1", + "20", + "25", ] window = Arch.makeWindow(baseobj=sketch, parts=custom_parts, name="Window_Custom") self.document.recompute() @@ -118,13 +151,11 @@ class TestArchWindow(TestArchBase.TestArchBase): def test_custom_parts_with_plus_v_references(self): """Test creating a window with custom parts using '+V' to reference window.Frame and window.Offset.""" - sketch = self._create_sketch_with_wires("SketchPlusV", [(0,0,1000,800)]) + sketch = self._create_sketch_with_wires("SketchPlusV", [(0, 0, 1000, 800)]) frame_val = 60.0 offset_val = 10.0 - custom_parts_plus_v = [ - "MainFrame", "Frame", "Wire0", "50+V", "5+V" - ] + custom_parts_plus_v = ["MainFrame", "Frame", "Wire0", "50+V", "5+V"] window = Arch.makeWindow(baseobj=sketch, parts=custom_parts_plus_v, name="Window_PlusV") window.Frame = frame_val window.Offset = offset_val @@ -145,14 +176,24 @@ class TestArchWindow(TestArchBase.TestArchBase): # 1. Check initial state after makeWindow call self.assertEqual(window.Label, window_name) - self.assertIsNone(window.Base, - "Immediately after makeWindow(W,H), window.Base should be None.") - self.assertTrue(window.Shape.isNull(), - "Initially, window.Shape should be null as no Base or Parts yet.") - self.assertAlmostEqual(window.Width.Value, win_w, places=5, - msg="Window.Width property not correctly set by makeWindow.") - self.assertAlmostEqual(window.Height.Value, win_h, places=5, - msg="Window.Height property not correctly set by makeWindow.") + self.assertIsNone( + window.Base, "Immediately after makeWindow(W,H), window.Base should be None." + ) + self.assertTrue( + window.Shape.isNull(), "Initially, window.Shape should be null as no Base or Parts yet." + ) + self.assertAlmostEqual( + window.Width.Value, + win_w, + places=5, + msg="Window.Width property not correctly set by makeWindow.", + ) + self.assertAlmostEqual( + window.Height.Value, + win_h, + places=5, + msg="Window.Height property not correctly set by makeWindow.", + ) # 2. Perform a document recompute # This should trigger window.execute() -> ArchComponent.ensureBase() @@ -161,19 +202,24 @@ class TestArchWindow(TestArchBase.TestArchBase): # 3. Assert the CURRENT BEHAVIOR: window.Base remains None # The original ArchComponent.ensureBase does not create a sketch if Base is None # and only Width/Height are provided to the object. - self.assertIsNone(window.Base, - "Current Behavior: window.Base should remain None even after recomputes for makeWindow(W,H).") + self.assertIsNone( + window.Base, + "Current Behavior: window.Base should remain None even after recomputes for makeWindow(W,H).", + ) # 4. Consequently, window.Shape should still be null. - self.assertTrue(window.Shape.isNull(), - "window.Shape should remain null if window.Base was not created.") + self.assertTrue( + window.Shape.isNull(), "window.Shape should remain null if window.Base was not created." + ) # 5. Attempting to set parts that rely on a Base sketch (e.g., "Wire0") # should not produce a shape if Base is None. window.WindowParts = ["Default", "Frame", "Wire0", "50", "0"] self.document.recompute() - self.assertTrue(window.Shape.isNull(), - "window.Shape should still be null if WindowParts reference Wire0 from a non-existent Base.") + self.assertTrue( + window.Shape.isNull(), + "window.Shape should still be null if WindowParts reference Wire0 from a non-existent Base.", + ) def test_window_in_wall_creates_opening(self): """ @@ -191,11 +237,13 @@ class TestArchWindow(TestArchBase.TestArchBase): line = Draft.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(wall_length, 0, 0)) self.document.recompute() - wall = Arch.makeWall(line, - width=wall_thickness, - height=wall_height, - align="Left", - name="TestWall_ForOpening_Args") + wall = Arch.makeWall( + line, + width=wall_thickness, + height=wall_height, + align="Left", + name="TestWall_ForOpening_Args", + ) self.document.recompute() initial_wall_volume = wall.Shape.Volume @@ -204,7 +252,9 @@ class TestArchWindow(TestArchBase.TestArchBase): # 2. Create Sketch for Window profile sketch_profile_width = 800.0 sketch_profile_height = 1000.0 - sk = self._create_sketch_with_wires("WindowSketch_Args", [(0, 0, sketch_profile_width, sketch_profile_height)]) + sk = self._create_sketch_with_wires( + "WindowSketch_Args", [(0, 0, sketch_profile_width, sketch_profile_height)] + ) # Position and orient the sketch in 3D space sk.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90) @@ -221,19 +271,21 @@ class TestArchWindow(TestArchBase.TestArchBase): win.Height = sketch_profile_height # win.Placement remains identity - win.HoleDepth = 0 # Use "smart" hole depth calculation + win.HoleDepth = 0 # Use "smart" hole depth calculation win.WindowParts = ["DefaultFrame", "Frame", "Wire0", "60", "0"] win.recompute() self.document.recompute() self.assertFalse(win.Shape.isNull(), "Window must have a valid shape.") - self.assertEqual(win.Placement, FreeCAD.Placement(), - f"Window.Placement should be identity; got {win.Placement}") + self.assertEqual( + win.Placement, + FreeCAD.Placement(), + f"Window.Placement should be identity; got {win.Placement}", + ) self.assertAlmostEqual(win.Width.Value, sketch_profile_width, 5) self.assertAlmostEqual(win.Height.Value, sketch_profile_height, 5) - # 4. Add Window to Wall Arch.addComponents(win, host=wall) self.document.recompute() @@ -242,24 +294,28 @@ class TestArchWindow(TestArchBase.TestArchBase): # Wall.Subtractions PropertyLinkList check (informational, expected to be empty) wall_subtractions_prop = wall.Subtractions if hasattr(wall, "Subtractions") else [] self.assertEqual( - len(wall_subtractions_prop), 0, + len(wall_subtractions_prop), + 0, f"Wall.Subtractions property list expected to be empty, but: " - f"{wall_subtractions_prop}" + f"{wall_subtractions_prop}", ) # Geometric checks current_wall_volume = wall.Shape.Volume self.assertLess( - current_wall_volume, initial_wall_volume, + current_wall_volume, + initial_wall_volume, f"Wall volume should decrease. Before: {initial_wall_volume}, " - f"After: {current_wall_volume}" + f"After: {current_wall_volume}", ) ideal_removed_volume = win.Width.Value * win.Height.Value * wall.Width.Value self.assertAlmostEqual( - current_wall_volume, initial_wall_volume - ideal_removed_volume, places=3, + current_wall_volume, + initial_wall_volume - ideal_removed_volume, + places=3, msg=f"Wall volume cut not as expected. Initial: {initial_wall_volume}, " - f"Current: {current_wall_volume}, IdealRemoved: {ideal_removed_volume}" + f"Current: {current_wall_volume}, IdealRemoved: {ideal_removed_volume}", ) # Check VerticalArea @@ -267,14 +323,19 @@ class TestArchWindow(TestArchBase.TestArchBase): window_opening_area_on_one_face = win.Width.Value * win.Height.Value area_of_side_reveals = 2 * win.Height.Value * wall.Width.Value - expected_wall_vertical_area_after = (initial_wall_vertical_area - - (2 * window_opening_area_on_one_face) + area_of_side_reveals) + expected_wall_vertical_area_after = ( + initial_wall_vertical_area + - (2 * window_opening_area_on_one_face) + + area_of_side_reveals + ) self.assertAlmostEqual( - current_wall_vertical_area, expected_wall_vertical_area_after, places=3, + current_wall_vertical_area, + expected_wall_vertical_area_after, + places=3, msg=f"Wall VerticalArea change not as expected. Initial: " - f"{initial_wall_vertical_area}, Current: {current_wall_vertical_area}, " - f"ExpectedAfter: {expected_wall_vertical_area_after}" + f"{initial_wall_vertical_area}, Current: {current_wall_vertical_area}, " + f"ExpectedAfter: {expected_wall_vertical_area_after}", ) def test_clone_window(self): @@ -287,7 +348,9 @@ class TestArchWindow(TestArchBase.TestArchBase): """ sketch = self._create_sketch_with_wires("OriginalSketch", [(0, 0, 600, 800)]) original_parts = ["MainFrame", "Frame", "Wire0", "50", "10"] - original_window = Arch.makeWindow(baseobj=sketch, parts=original_parts, name="OriginalWindow") + original_window = Arch.makeWindow( + baseobj=sketch, parts=original_parts, name="OriginalWindow" + ) original_window.Frame = 60.0 original_window.Sill = 100.0 self.document.recompute() @@ -304,15 +367,20 @@ class TestArchWindow(TestArchBase.TestArchBase): self.assertEqual(cloned_window.IfcType, original_window.IfcType) self.assertFalse(cloned_window.Shape.isNull()) - self.assertAlmostEqual(cloned_window.Shape.Volume, original_window.Shape.Volume, delta=1e-5, - msg="Cloned window volume should match original.") + self.assertAlmostEqual( + cloned_window.Shape.Volume, + original_window.Shape.Volume, + delta=1e-5, + msg="Cloned window volume should match original.", + ) def test_create_window_on_xz_plane(self): """Test creating a window oriented on the XZ (vertical) plane.""" # Create a a frame-like profile. - sketch = self._create_sketch_with_wires("Sketch_XZ_Plane", - [(0, 0, 1000, 1200), (100, 100, 800, 1000)]) + sketch = self._create_sketch_with_wires( + "Sketch_XZ_Plane", [(0, 0, 1000, 1200), (100, 100, 800, 1000)] + ) # Orient the sketch to the XZ plane. sketch.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90) @@ -321,33 +389,51 @@ class TestArchWindow(TestArchBase.TestArchBase): # Create the window using explicit parts. window = Arch.makeWindow(baseobj=sketch, name="Window_XZ_Plane") window.WindowParts = [ - "Frame", "Frame", "Wire0,Wire1", "60", "0", # A 60mm thick frame - "Glass", "Glass panel", "Wire1", "10", "25" # A 10mm thick glass pane, offset by 25mm + "Frame", + "Frame", + "Wire0,Wire1", + "60", + "0", # A 60mm thick frame + "Glass", + "Glass panel", + "Wire1", + "10", + "25", # A 10mm thick glass pane, offset by 25mm ] self.document.recompute() # Check the resulting geometry's orientation and dimensions. self.assertFalse(window.Shape.isNull(), "Window shape should not be null.") - self.assertEqual(len(window.Shape.Solids), 2, "Window should contain two solids (frame and glass).") + self.assertEqual( + len(window.Shape.Solids), 2, "Window should contain two solids (frame and glass)." + ) bb = window.Shape.BoundBox # The window's overall "thickness" is determined by the frame (60mm). # Its "width" (X) and "height" (Z) should be larger. - self.assertGreater(bb.XLength, bb.YLength, - "Window XLength (width) should be greater than YLength (thickness).") - self.assertGreater(bb.ZLength, bb.YLength, - "Window ZLength (height) should be greater than YLength (thickness).") + self.assertGreater( + bb.XLength, + bb.YLength, + "Window XLength (width) should be greater than YLength (thickness).", + ) + self.assertGreater( + bb.ZLength, + bb.YLength, + "Window ZLength (height) should be greater than YLength (thickness).", + ) # Verify the overall thickness is correct (60mm, from the Frame component). - self.assertAlmostEqual(bb.YLength, 60.0, places=5, - msg="Window thickness (YLength) is incorrect.") + self.assertAlmostEqual( + bb.YLength, 60.0, places=5, msg="Window thickness (YLength) is incorrect." + ) def test_opening_property_rotates_component(self): """Test that setting the Opening property rotates a hinged component.""" # Create a window from a preset with a hinge - window = Arch.makeWindowPreset("Open 1-pane", width=1000, height=1200, - h1=50, h2=50, h3=0, w1=100, w2=50, o1=0, o2=50) + window = Arch.makeWindowPreset( + "Open 1-pane", width=1000, height=1200, h1=50, h2=50, h3=0, w1=100, w2=50, o1=0, o2=50 + ) self.document.recompute() # The preset creates an outer frame, an inner opening frame, and glass. @@ -369,16 +455,21 @@ class TestArchWindow(TestArchBase.TestArchBase): new_center = new_opening_pane.CenterOfMass # Assert that the Z-coordinate has changed, indicating rotation around a horizontal hinge. - self.assertNotAlmostEqual(initial_center.z, new_center.z, places=3, - msg="The Z-coordinate of the opening pane's center should change upon rotation.") + self.assertNotAlmostEqual( + initial_center.z, + new_center.z, + places=3, + msg="The Z-coordinate of the opening pane's center should change upon rotation.", + ) # The Y-coordinate should remain largely unchanged for a bottom-hinged (awning-style) window. self.assertAlmostEqual(initial_center.y, new_center.y, places=3) def test_symbol_plan_creates_wire_geometry(self): """Test that enabling SymbolPlan adds 2D wire geometry to the window's shape.""" # Create a window with an opening mode - window = Arch.makeWindowPreset("Open 1-pane", width=1000, height=1200, - h1=50, h2=50, h3=0, w1=100, w2=50, o1=0, o2=50) + window = Arch.makeWindowPreset( + "Open 1-pane", width=1000, height=1200, h1=50, h2=50, h3=0, w1=100, w2=50, o1=0, o2=50 + ) # SymbolPlan is True by default for presets with hinges, but we set it explicitly window.SymbolPlan = True @@ -394,25 +485,29 @@ class TestArchWindow(TestArchBase.TestArchBase): symbol_edge_hashes = all_edge_hashes - face_edge_hashes self.assertEqual(len(window.Shape.Solids), 3, "Window shape should contain 3 solids.") - self.assertEqual(len(symbol_edge_hashes), 2, "Expected 2 edges for the plan symbol (arc and line).") + self.assertEqual( + len(symbol_edge_hashes), 2, "Expected 2 edges for the plan symbol (arc and line)." + ) def test_custom_subvolume_creates_opening(self): """Test that a custom Subvolume shape correctly creates an opening in a host wall.""" # Create a wall and store its initial state - wall_base = Draft.makeLine(FreeCAD.Vector(0,0,0), FreeCAD.Vector(4000,0,0)) + wall_base = Draft.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(4000, 0, 0)) wall = Arch.makeWall(wall_base, width=200, height=3000, align="Left") self.document.recompute() initial_wall_volume = wall.Shape.Volume - initial_wall_shape = wall.Shape.copy() # Store initial shape for accurate intersection calculation + initial_wall_shape = ( + wall.Shape.copy() + ) # Store initial shape for accurate intersection calculation # Create a simple window - window_sketch = self._create_sketch_with_wires("WinSketch", [(0,0,10,10)]) + window_sketch = self._create_sketch_with_wires("WinSketch", [(0, 0, 10, 10)]) window = Arch.makeWindow(window_sketch) # Create a custom cutting box as the window's Subvolume cutting_box = self.document.addObject("Part::Box", "CuttingBox") cutting_box.Length = 800 - cutting_box.Width = 300 # Wider than wall + cutting_box.Width = 300 # Wider than wall cutting_box.Height = 1000 cutting_box.Placement.Base = FreeCAD.Vector(1600, -50, 800) self.document.recompute() @@ -426,14 +521,19 @@ class TestArchWindow(TestArchBase.TestArchBase): # Assert volume has decreased by the intersecting part of the box's volume expected_volume_change = cutting_box.Shape.common(initial_wall_shape).Volume final_wall_volume = wall.Shape.Volume - self.assertAlmostEqual(final_wall_volume, initial_wall_volume - expected_volume_change, places=3, - msg="Wall volume did not decrease correctly after applying custom Subvolume.") + self.assertAlmostEqual( + final_wall_volume, + initial_wall_volume - expected_volume_change, + places=3, + msg="Wall volume did not decrease correctly after applying custom Subvolume.", + ) def test_door_preset_sets_ifctype_door(self): """Test that creating a window from a 'door' preset correctly sets its IfcType to 'Door'.""" # Create a door using a preset with non-zero values for all required parameters - door = Arch.makeWindowPreset("Simple door", width=900, height=2100, - h1=50, h2=50, h3=0, w1=100, w2=40, o1=0, o2=0) + door = Arch.makeWindowPreset( + "Simple door", width=900, height=2100, h1=50, h2=50, h3=0, w1=100, w2=40, o1=0, o2=0 + ) self.assertIsNotNone(door, "makeWindowPreset for a door should create an object.") self.document.recompute() @@ -442,7 +542,11 @@ class TestArchWindow(TestArchBase.TestArchBase): # Assert component count # A simple door preset has an outer frame and a solid door panel - self.assertEqual(len(door.Shape.Solids), 2, "A simple door should have 2 solid components (frame and panel).") + self.assertEqual( + len(door.Shape.Solids), + 2, + "A simple door should have 2 solid components (frame and panel).", + ) def test_cloned_window_in_wall_creates_opening(self): """Tests if a cloned Arch.Window, when hosted in a wall, creates a geometric opening.""" @@ -452,7 +556,9 @@ class TestArchWindow(TestArchBase.TestArchBase): wall_thickness = 200.0 wall_height = 2500.0 wall_base = Draft.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(wall_length, 0, 0)) - wall = Arch.makeWall(wall_base, width=wall_thickness, height=wall_height, name="WallForClonedWindow") + wall = Arch.makeWall( + wall_base, width=wall_thickness, height=wall_height, name="WallForClonedWindow" + ) self.document.recompute() initial_wall_volume = wall.Shape.Volume self.assertGreater(initial_wall_volume, 0, "Wall should have a positive volume initially.") @@ -460,7 +566,9 @@ class TestArchWindow(TestArchBase.TestArchBase): # Create the original window's sketch window_width = 800.0 window_height = 1200.0 - original_sketch = self._create_sketch_with_wires("OriginalWinSketch", [(0, 0, window_width, window_height)]) + original_sketch = self._create_sketch_with_wires( + "OriginalWinSketch", [(0, 0, window_width, window_height)] + ) # Orient the sketch to be vertical before creating the window # This mimics the behavior of the interactive Arch_Window tool. @@ -469,10 +577,12 @@ class TestArchWindow(TestArchBase.TestArchBase): # Create the original window from the now-oriented sketch original_window = Arch.makeWindow(baseobj=original_sketch, name="OriginalWindow") - original_window.Width = window_width # Explicitly set properties + original_window.Width = window_width # Explicitly set properties original_window.Height = window_height self.document.recompute() - self.assertFalse(original_window.Shape.isNull(), "Original window shape should not be null.") + self.assertFalse( + original_window.Shape.isNull(), "Original window shape should not be null." + ) # Create the clone cloned_window = Draft.clone(original_window) @@ -481,27 +591,40 @@ class TestArchWindow(TestArchBase.TestArchBase): # Position the clone inside the wall clone_x = 1100.0 # Center of the clone in the wall's length clone_y = wall_thickness / 2 # Center of the clone in the wall's thickness - clone_z = 800.0 # Sill height + clone_z = 800.0 # Sill height cloned_window.Placement.Base = FreeCAD.Vector(clone_x, clone_y, clone_z) self.document.recompute() - self.assertIsNotNone(cloned_window.CloneOf, "Cloned window should have CloneOf property set.") - self.assertEqual(cloned_window.CloneOf, original_window, "CloneOf should point to the original window.") + self.assertIsNotNone( + cloned_window.CloneOf, "Cloned window should have CloneOf property set." + ) + self.assertEqual( + cloned_window.CloneOf, original_window, "CloneOf should point to the original window." + ) self.assertAlmostEqual(cloned_window.Shape.Volume, original_window.Shape.Volume, delta=1e-5) # Add the clone to the wall's hosts Arch.addComponents(cloned_window, host=wall) self.document.recompute() - self.assertIn(wall, cloned_window.Hosts, "Wall should be in the cloned window's Hosts list.") + self.assertIn( + wall, cloned_window.Hosts, "Wall should be in the cloned window's Hosts list." + ) final_wall_volume = wall.Shape.Volume - self.assertLess(final_wall_volume, initial_wall_volume, - "Wall volume should have decreased after hosting the cloned window.") + self.assertLess( + final_wall_volume, + initial_wall_volume, + "Wall volume should have decreased after hosting the cloned window.", + ) expected_removed_volume = window_width * window_height * wall.Width.Value - self.assertAlmostEqual(initial_wall_volume - final_wall_volume, expected_removed_volume, delta=1e-5, - msg="The volume removed from the wall by the cloned window is incorrect.") + self.assertAlmostEqual( + initial_wall_volume - final_wall_volume, + expected_removed_volume, + delta=1e-5, + msg="The volume removed from the wall by the cloned window is incorrect.", + ) def test_addComponents_window_to_wall(self): """ @@ -519,9 +642,11 @@ class TestArchWindow(TestArchBase.TestArchBase): window_width = 800.0 window_height = 1200.0 - window_sketch = self._create_sketch_with_wires("WindowSketchForAdd", [(0, 0, window_width, window_height)]) + window_sketch = self._create_sketch_with_wires( + "WindowSketchForAdd", [(0, 0, window_width, window_height)] + ) window_sketch.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90) - window_sketch.Placement.Base = FreeCAD.Vector(1100, 100, 800) # Position it within the wall + window_sketch.Placement.Base = FreeCAD.Vector(1100, 100, 800) # Position it within the wall self.document.recompute() window = Arch.makeWindow(baseobj=window_sketch, name="HostedWindow") @@ -547,19 +672,31 @@ class TestArchWindow(TestArchBase.TestArchBase): self.assertIn(wall, window.OutList, "Wall should be in window's OutList after hosting.") # Negative assertion - self.assertNotIn(window, wall.Subtractions, - "Window should not be in wall's Subtractions list for this hosting mechanism.") + self.assertNotIn( + window, + wall.Subtractions, + "Window should not be in wall's Subtractions list for this hosting mechanism.", + ) # Geometric assertion final_wall_volume = wall.Shape.Volume - self.assertLess(final_wall_volume, initial_wall_volume, - "Wall volume should decrease after hosting the window.") + self.assertLess( + final_wall_volume, + initial_wall_volume, + "Wall volume should decrease after hosting the window.", + ) expected_removed_volume = window_width * window_height * wall.Width.Value - self.assertAlmostEqual(initial_wall_volume - final_wall_volume, expected_removed_volume, delta=1e-5, - msg="The volume removed from the wall is incorrect.") + self.assertAlmostEqual( + initial_wall_volume - final_wall_volume, + expected_removed_volume, + delta=1e-5, + msg="The volume removed from the wall is incorrect.", + ) - def _create_sketch_with_wires(self, name: str, wire_definitions: list[tuple[float, float, float, float]]) -> "FreeCAD.DocumentObject": + def _create_sketch_with_wires( + self, name: str, wire_definitions: list[tuple[float, float, float, float]] + ) -> "FreeCAD.DocumentObject": """ Helper to create a sketch with one or more specified rectangular wires. @@ -595,21 +732,35 @@ class TestArchWindow(TestArchBase.TestArchBase): sketch: FreeCAD.DocumentObject = self.document.addObject("Sketcher::SketchObject", name) for i, (x, y, w, h) in enumerate(wire_definitions): - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(x, y, 0), FreeCAD.Vector(x + w, y, 0))) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(x + w, y, 0), FreeCAD.Vector(x + w, y + h, 0))) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(x + w, y + h, 0), FreeCAD.Vector(x, y + h, 0))) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(x, y + h, 0), FreeCAD.Vector(x, y, 0))) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(x, y, 0), FreeCAD.Vector(x + w, y, 0)) + ) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(x + w, y, 0), FreeCAD.Vector(x + w, y + h, 0)) + ) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(x + w, y + h, 0), FreeCAD.Vector(x, y + h, 0)) + ) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(x, y + h, 0), FreeCAD.Vector(x, y, 0)) + ) base_idx = i * 4 - sketch.addConstraint(Sketcher.Constraint('Coincident', base_idx, 2, base_idx + 1, 1)) - sketch.addConstraint(Sketcher.Constraint('Coincident', base_idx + 1, 2, base_idx + 2, 1)) - sketch.addConstraint(Sketcher.Constraint('Coincident', base_idx + 2, 2, base_idx + 3, 1)) - sketch.addConstraint(Sketcher.Constraint('Coincident', base_idx + 3, 2, base_idx, 1)) + sketch.addConstraint(Sketcher.Constraint("Coincident", base_idx, 2, base_idx + 1, 1)) + sketch.addConstraint( + Sketcher.Constraint("Coincident", base_idx + 1, 2, base_idx + 2, 1) + ) + sketch.addConstraint( + Sketcher.Constraint("Coincident", base_idx + 2, 2, base_idx + 3, 1) + ) + sketch.addConstraint(Sketcher.Constraint("Coincident", base_idx + 3, 2, base_idx, 1)) self.document.recompute() return sketch - def _create_sketch_with_named_constraints(self, name: str, initial_width: float, initial_height: float) -> "FreeCAD.DocumentObject": + def _create_sketch_with_named_constraints( + self, name: str, initial_width: float, initial_height: float + ) -> "FreeCAD.DocumentObject": """ Helper to create a rectangular sketch with "Width" and "Height" named constraints. @@ -634,20 +785,38 @@ class TestArchWindow(TestArchBase.TestArchBase): """ sketch: FreeCAD.DocumentObject = self.document.addObject("Sketcher::SketchObject", name) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), FreeCAD.Vector(initial_width,0,0)), False) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(initial_width,0,0), FreeCAD.Vector(initial_width,initial_height,0)), False) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(initial_width,initial_height,0), FreeCAD.Vector(0,initial_height,0)), False) - sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,initial_height,0), FreeCAD.Vector(0,0,0)), False) - sketch.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) - sketch.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) - sketch.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) - sketch.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) - sketch.addConstraint(Sketcher.Constraint('Horizontal',0)); sketch.addConstraint(Sketcher.Constraint('Vertical',1)) - sketch.addConstraint(Sketcher.Constraint('Horizontal',2)); sketch.addConstraint(Sketcher.Constraint('Vertical',3)) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(initial_width, 0, 0)), False + ) + sketch.addGeometry( + Part.LineSegment( + FreeCAD.Vector(initial_width, 0, 0), + FreeCAD.Vector(initial_width, initial_height, 0), + ), + False, + ) + sketch.addGeometry( + Part.LineSegment( + FreeCAD.Vector(initial_width, initial_height, 0), + FreeCAD.Vector(0, initial_height, 0), + ), + False, + ) + sketch.addGeometry( + Part.LineSegment(FreeCAD.Vector(0, initial_height, 0), FreeCAD.Vector(0, 0, 0)), False + ) + sketch.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1)) + sketch.addConstraint(Sketcher.Constraint("Coincident", 1, 2, 2, 1)) + sketch.addConstraint(Sketcher.Constraint("Coincident", 2, 2, 3, 1)) + sketch.addConstraint(Sketcher.Constraint("Coincident", 3, 2, 0, 1)) + sketch.addConstraint(Sketcher.Constraint("Horizontal", 0)) + sketch.addConstraint(Sketcher.Constraint("Vertical", 1)) + sketch.addConstraint(Sketcher.Constraint("Horizontal", 2)) + sketch.addConstraint(Sketcher.Constraint("Vertical", 3)) - sketch.addConstraint(Sketcher.Constraint('DistanceX',0,1,0,2,initial_width)) + sketch.addConstraint(Sketcher.Constraint("DistanceX", 0, 1, 0, 2, initial_width)) sketch.renameConstraint(sketch.ConstraintCount - 1, "Width") - sketch.addConstraint(Sketcher.Constraint('DistanceY',1,1,1,2,initial_height)) + sketch.addConstraint(Sketcher.Constraint("DistanceY", 1, 1, 1, 2, initial_height)) sketch.renameConstraint(sketch.ConstraintCount - 1, "Height") self.document.recompute() diff --git a/src/Mod/BIM/bimtests/TestWebGLExport.py b/src/Mod/BIM/bimtests/TestWebGLExport.py index 29c6a3c135..00b4515738 100644 --- a/src/Mod/BIM/bimtests/TestWebGLExport.py +++ b/src/Mod/BIM/bimtests/TestWebGLExport.py @@ -85,9 +85,7 @@ class TestWebGLExport(TestArchBase): with open(custom_path, "w", encoding="utf-8") as f: f.write(self.test_template_content) - with patch( - "BIM.importers.importWebGL.params.get_param" - ) as mock_params: + with patch("BIM.importers.importWebGL.params.get_param") as mock_params: mock_params.side_effect = lambda param, path=None: { "useCustomWebGLExportTemplate": True, "WebGLTemplateCustomPath": custom_path, @@ -95,17 +93,13 @@ class TestWebGLExport(TestArchBase): result = importWebGL.getHTMLTemplate() self.assertIsNotNone(result) - self.assertEqual( - result.strip(), self.test_template_content.strip() - ) + self.assertEqual(result.strip(), self.test_template_content.strip()) def test_default_template_logic_when_custom_disabled(self): """Test code path when custom template is disabled""" operation = "Testing default template logic when custom is disabled" self.printTestMessage(operation) - with patch( - "BIM.importers.importWebGL.params.get_param", return_value=False - ): + with patch("BIM.importers.importWebGL.params.get_param", return_value=False): with patch("os.path.isfile", return_value=True): with patch( "builtins.open", @@ -120,17 +114,13 @@ class TestWebGLExport(TestArchBase): """Test behavior when custom template not found in headless mode""" operation = "Testing custom template not found in headless mode" self.printTestMessage(operation) - with patch( - "BIM.importers.importWebGL.params.get_param" - ) as mock_params: + with patch("BIM.importers.importWebGL.params.get_param") as mock_params: mock_params.side_effect = lambda param, path=None: { "useCustomWebGLExportTemplate": True, "WebGLTemplateCustomPath": "/nonexistent/template.html", }.get(param, False) - with patch( - "BIM.importers.importWebGL.FreeCADGui", None - ): # Simulate headless mode + with patch("BIM.importers.importWebGL.FreeCADGui", None): # Simulate headless mode result = importWebGL.getHTMLTemplate() self.assertIsNone(result) diff --git a/src/Mod/BIM/bimtests/TestWebGLExportGui.py b/src/Mod/BIM/bimtests/TestWebGLExportGui.py index 1fc5b0647a..b88d9d8070 100644 --- a/src/Mod/BIM/bimtests/TestWebGLExportGui.py +++ b/src/Mod/BIM/bimtests/TestWebGLExportGui.py @@ -59,9 +59,7 @@ class TestWebGLExportGui(TestArchBase): super().tearDown() @patch("BIM.importers.importWebGL.FreeCADGui") - def test_custom_template_not_found_gui_user_accepts_fallback( - self, mock_gui - ): + def test_custom_template_not_found_gui_user_accepts_fallback(self, mock_gui): """Test GUI dialog when custom template not found - user accepts fallback""" operation = "Testing GUI custom template not found - user accepts fallback" @@ -70,9 +68,7 @@ class TestWebGLExportGui(TestArchBase): mock_gui.getMainWindow.return_value = MagicMock() - with patch( - "BIM.importers.importWebGL.params.get_param" - ) as mock_params: + with patch("BIM.importers.importWebGL.params.get_param") as mock_params: mock_params.side_effect = lambda param, path=None: { "useCustomWebGLExportTemplate": True, "WebGLTemplateCustomPath": "/nonexistent/template.html", @@ -91,9 +87,7 @@ class TestWebGLExportGui(TestArchBase): self.assertIsNotNone(result) @patch("BIM.importers.importWebGL.FreeCADGui") - def test_custom_template_not_found_gui_user_rejects_fallback( - self, mock_gui - ): + def test_custom_template_not_found_gui_user_rejects_fallback(self, mock_gui): """Test GUI dialog when custom template not found - user rejects fallback""" operation = "Testing GUI custom template not found - user rejects fallback" @@ -102,9 +96,7 @@ class TestWebGLExportGui(TestArchBase): mock_gui.getMainWindow.return_value = MagicMock() - with patch( - "BIM.importers.importWebGL.params.get_param" - ) as mock_params: + with patch("BIM.importers.importWebGL.params.get_param") as mock_params: mock_params.side_effect = lambda param, path=None: { "useCustomWebGLExportTemplate": True, "WebGLTemplateCustomPath": "/nonexistent/template.html", @@ -122,18 +114,14 @@ class TestWebGLExportGui(TestArchBase): no template is available""" operation = "Testing export returns False when no template available" self.printTestMessage(operation) - with patch( - "BIM.importers.importWebGL.getHTMLTemplate", return_value=None - ): + with patch("BIM.importers.importWebGL.getHTMLTemplate", return_value=None): with patch("BIM.importers.importWebGL.FreeCADGui") as mock_gui: # Mock the GUI components that might be accessed mock_active_doc = MagicMock() mock_active_doc.ActiveView = MagicMock() mock_gui.ActiveDocument = mock_active_doc - result = importWebGL.export( - [], os.path.join(self.test_dir, "test.html") - ) + result = importWebGL.export([], os.path.join(self.test_dir, "test.html")) self.assertFalse(result) def test_export_returns_true_when_template_available(self): @@ -148,9 +136,7 @@ class TestWebGLExportGui(TestArchBase): "BIM.importers.importWebGL.getHTMLTemplate", return_value=mock_template, ): - with patch( - "BIM.importers.importWebGL.FreeCAD.ActiveDocument" - ) as mock_doc: + with patch("BIM.importers.importWebGL.FreeCAD.ActiveDocument") as mock_doc: mock_doc.Label = "Test Document" with patch("BIM.importers.importWebGL.FreeCADGui") as mock_gui: # Mock the GUI components that might be accessed @@ -159,16 +145,12 @@ class TestWebGLExportGui(TestArchBase): mock_gui.ActiveDocument = mock_active_doc # Mock the functions that populate data to return JSON-serializable values - with patch( - "BIM.importers.importWebGL.populate_camera" - ) as mock_populate_camera: + with patch("BIM.importers.importWebGL.populate_camera") as mock_populate_camera: with patch( "BIM.importers.importWebGL.Draft.get_group_contents", return_value=[], ): - mock_populate_camera.return_value = ( - None # Modifies data dict in place - ) + mock_populate_camera.return_value = None # Modifies data dict in place result = importWebGL.export( [], os.path.join(self.test_dir, "test.html") diff --git a/src/Mod/BIM/ifc_objects.py b/src/Mod/BIM/ifc_objects.py index a077f87f73..2f249cb0e4 100644 --- a/src/Mod/BIM/ifc_objects.py +++ b/src/Mod/BIM/ifc_objects.py @@ -22,14 +22,19 @@ # * * # *************************************************************************** + class ifc_object: """NativeIFC class placeholder""" + def __init__(self): pass + def onDocumentRestored(self, obj): obj.Type = [obj.IfcType] obj.Type = obj.IfcType + def dumps(self): return None + def loads(self, state): return None diff --git a/src/Mod/BIM/ifc_viewproviders.py b/src/Mod/BIM/ifc_viewproviders.py index 973a1ca517..a6cc22f7ab 100644 --- a/src/Mod/BIM/ifc_viewproviders.py +++ b/src/Mod/BIM/ifc_viewproviders.py @@ -26,25 +26,38 @@ import FreeCAD + class ifc_vp_object: """NativeIFC class placeholder""" + def __init__(self): pass + class ifc_vp_document: """NativeIFC class placeholder""" + def __init__(self): pass + def attach(self, vobj): - FreeCAD.Console.PrintWarning("Warning: Object "+vobj.Object.Label+" depends on the NativeIFC addon which is not installed, and might not display correctly in the 3D view\n") + FreeCAD.Console.PrintWarning( + "Warning: Object " + + vobj.Object.Label + + " depends on the NativeIFC addon which is not installed, and might not display correctly in the 3D view\n" + ) return + class ifc_vp_group: """NativeIFC class placeholder""" + def __init__(self): pass + class ifc_vp_material: """NativeIFC class placeholder""" + def __init__(self): pass diff --git a/src/Mod/BIM/importers/exportIFC.py b/src/Mod/BIM/importers/exportIFC.py index 54746cce72..89be0fcad2 100644 --- a/src/Mod/BIM/importers/exportIFC.py +++ b/src/Mod/BIM/importers/exportIFC.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD IFC export" +__title__ = "FreeCAD IFC export" __author__ = ("Yorik van Havre", "Jonathan Wiedemann", "Bernd Hahnebach") -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package exportIFC # \ingroup ARCH @@ -109,6 +109,7 @@ ENDSEC; END-ISO-10303-21; """ + def _prepare_export_list_skipping_std_groups(initial_export_list, preferences_dict): """ Builds the list of objects for IFC export. This function is called when the preference to skip @@ -138,7 +139,7 @@ def _prepare_export_list_skipping_std_groups(initial_export_list, preferences_di recursive=True, discover_hosted_elements=True, include_components_from_additions=True, - include_initial_objects_in_result=True + include_initial_objects_in_result=True, ) final_objects_for_processing = [] @@ -153,14 +154,17 @@ def _prepare_export_list_skipping_std_groups(initial_export_list, preferences_di is_std_group_to_skip = True if not is_std_group_to_skip: - if obj not in final_objects_for_processing: # Ensure uniqueness + if obj not in final_objects_for_processing: # Ensure uniqueness final_objects_for_processing.append(obj) - elif preferences_dict['DEBUG']: - print(f"DEBUG: IFC Exporter: StdGroup '{obj.Label}' ({obj.Name}) " - "was identified by get_architectural_contents but is now being filtered out.") + elif preferences_dict["DEBUG"]: + print( + f"DEBUG: IFC Exporter: StdGroup '{obj.Label}' ({obj.Name}) " + "was identified by get_architectural_contents but is now being filtered out." + ) return final_objects_for_processing + def getPreferences(): """Retrieve the IFC preferences available in import and export.""" @@ -198,22 +202,22 @@ def getPreferences(): # some objects may be "unreferenced" and won't belong to the `IfcProject`. # Some applications may fail at importing these unreferenced objects. preferences = { - 'DEBUG': params.get_param_arch("ifcDebug"), - 'CREATE_CLONES': params.get_param_arch("ifcCreateClones"), - 'FORCE_BREP': params.get_param_arch("ifcExportAsBrep"), - 'STORE_UID': params.get_param_arch("ifcStoreUid"), - 'SERIALIZE': params.get_param_arch("ifcSerialize"), - 'EXPORT_2D': params.get_param_arch("ifcExport2D"), - 'FULL_PARAMETRIC': params.get_param_arch("IfcExportFreeCADProperties"), - 'ADD_DEFAULT_SITE': params.get_param_arch("IfcAddDefaultSite"), - 'ADD_DEFAULT_BUILDING': params.get_param_arch("IfcAddDefaultBuilding"), - 'ADD_DEFAULT_STOREY': params.get_param_arch("IfcAddDefaultStorey"), - 'IFC_UNIT': u, - 'SCALE_FACTOR': f, - 'GET_STANDARD': params.get_param_arch("getStandardType"), - 'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][params.get_param_arch("ifcExportModel")], - 'GROUPS_AS_ASSEMBLIES': params.get_param_arch("IfcGroupsAsAssemblies"), - 'IGNORE_STD_GROUPS': not params.get_param_arch("IfcExportStdGroups"), + "DEBUG": params.get_param_arch("ifcDebug"), + "CREATE_CLONES": params.get_param_arch("ifcCreateClones"), + "FORCE_BREP": params.get_param_arch("ifcExportAsBrep"), + "STORE_UID": params.get_param_arch("ifcStoreUid"), + "SERIALIZE": params.get_param_arch("ifcSerialize"), + "EXPORT_2D": params.get_param_arch("ifcExport2D"), + "FULL_PARAMETRIC": params.get_param_arch("IfcExportFreeCADProperties"), + "ADD_DEFAULT_SITE": params.get_param_arch("IfcAddDefaultSite"), + "ADD_DEFAULT_BUILDING": params.get_param_arch("IfcAddDefaultBuilding"), + "ADD_DEFAULT_STOREY": params.get_param_arch("IfcAddDefaultStorey"), + "IFC_UNIT": u, + "SCALE_FACTOR": f, + "GET_STANDARD": params.get_param_arch("getStandardType"), + "EXPORT_MODEL": ["arch", "struct", "hybrid"][params.get_param_arch("ifcExportModel")], + "GROUPS_AS_ASSEMBLIES": params.get_param_arch("IfcGroupsAsAssemblies"), + "IGNORE_STD_GROUPS": not params.get_param_arch("IfcExportStdGroups"), } # get ifcopenshell version @@ -224,7 +228,11 @@ def getPreferences(): elif ifcopenshell.version.startswith("v"): ifcos_version = float(ifcopenshell.version[1:4]) # 0.7 else: - print("Could not retrieve IfcOpenShell version. Version is set to {}".format(ifcos_version)) + print( + "Could not retrieve IfcOpenShell version. Version is set to {}".format( + ifcos_version + ) + ) else: print("Could not retrieve IfcOpenShell version. Version is set to {}".format(ifcos_version)) @@ -254,14 +262,18 @@ def export(exportList, filename, colors=None, preferences=None): global ifcopenshell import ifcopenshell except ModuleNotFoundError: - _err("IfcOpenShell was not found on this system. " - "IFC support is disabled.\n" - "Visit https://wiki.freecad.org/IfcOpenShell " - "to learn about installing it.") + _err( + "IfcOpenShell was not found on this system. " + "IFC support is disabled.\n" + "Visit https://wiki.freecad.org/IfcOpenShell " + "to learn about installing it." + ) return from ifcopenshell import guid + if str(filename).lower().endswith("json"): import json + try: from ifcjson import ifc2json5a except Exception: @@ -287,27 +299,28 @@ def export(exportList, filename, colors=None, preferences=None): version = FreeCAD.Version() owner = FreeCAD.ActiveDocument.CreatedBy - email = '' + email = "" if ("@" in owner) and ("<" in owner): s = owner.split("<") owner = s[0].strip() email = s[1].strip(">") - template = ifctemplate.replace("$version", - version[0] + "." - + version[1] + " build " + version[2]) - if preferences['DEBUG']: print("Exporting an", preferences['SCHEMA'], "file...") - template = template.replace("$ifcschema", preferences['SCHEMA']) + template = ifctemplate.replace( + "$version", version[0] + "." + version[1] + " build " + version[2] + ) + if preferences["DEBUG"]: + print("Exporting an", preferences["SCHEMA"], "file...") + template = template.replace("$ifcschema", preferences["SCHEMA"]) template = template.replace("$owner", owner) template = template.replace("$company", FreeCAD.ActiveDocument.Company) template = template.replace("$email", email) template = template.replace("$now", str(int(time.time()))) template = template.replace("$filename", os.path.basename(filename)) - template = template.replace("$timestamp", - str(time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()))) + template = template.replace( + "$timestamp", str(time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())) + ) if hasattr(ifcopenshell, "version"): - template = template.replace("IfcOpenShell", - "IfcOpenShell " + ifcopenshell.version) + template = template.replace("IfcOpenShell", "IfcOpenShell " + ifcopenshell.version) templatefilehandle, templatefile = tempfile.mkstemp(suffix=".ifc") of = pyopen(templatefile, "w") @@ -318,7 +331,7 @@ def export(exportList, filename, colors=None, preferences=None): # create IFC file ifcfile = ifcopenshell.open(templatefile) - ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"]) + ifcfile = exportIFCHelper.writeUnits(ifcfile, preferences["IFC_UNIT"]) history = ifcfile.by_type("IfcOwnerHistory") if history: @@ -327,8 +340,8 @@ def export(exportList, filename, colors=None, preferences=None): # IFC4 allows one to not write any history history = None - if preferences['IGNORE_STD_GROUPS']: - if preferences['DEBUG']: + if preferences["IGNORE_STD_GROUPS"]: + if preferences["DEBUG"]: print("IFC Export: Skipping standard FreeCAD groups and processing their children.") objectslist = _prepare_export_list_skipping_std_groups(exportList, preferences) else: @@ -339,12 +352,21 @@ def export(exportList, filename, colors=None, preferences=None): annotations = [] specials = [] for obj in objectslist: - if obj.isDerivedFrom("Part::Part2DObject") \ - or obj.isDerivedFrom("App::Annotation") \ - or Draft.getType(obj) in [ - "BezCurve", "BSpline", "Wire", - "DraftText", "Text", "Dimension", "LinearDimension", "AngularDimension" - ]: + if ( + obj.isDerivedFrom("Part::Part2DObject") + or obj.isDerivedFrom("App::Annotation") + or Draft.getType(obj) + in [ + "BezCurve", + "BSpline", + "Wire", + "DraftText", + "Text", + "Dimension", + "LinearDimension", + "AngularDimension", + ] + ): annotations.append(obj) elif hasattr(obj, "Proxy") and hasattr(obj.Proxy, "export_ifc"): specials.append(obj) @@ -352,34 +374,64 @@ def export(exportList, filename, colors=None, preferences=None): if obj.Shape and (not obj.Shape.Solids) and obj.Shape.Edges: if not obj.Shape.Faces: annotations.append(obj) - elif (obj.Shape.BoundBox.XLength < 0.0001) or (obj.Shape.BoundBox.YLength < 0.0001) or (obj.Shape.BoundBox.ZLength < 0.0001): + elif ( + (obj.Shape.BoundBox.XLength < 0.0001) + or (obj.Shape.BoundBox.YLength < 0.0001) + or (obj.Shape.BoundBox.ZLength < 0.0001) + ): annotations.append(obj) # clean objects list of unwanted types objectslist = [obj for obj in objectslist if obj not in annotations] objectslist = [obj for obj in objectslist if obj not in specials] - objectslist = Arch.pruneIncluded(objectslist,strict=True) - objectslist = [obj for obj in objectslist if Draft.getType(obj) not in ["Dimension","Material","MaterialContainer","WorkingPlaneProxy"]] - if preferences['FULL_PARAMETRIC']: + objectslist = Arch.pruneIncluded(objectslist, strict=True) + objectslist = [ + obj + for obj in objectslist + if Draft.getType(obj) + not in ["Dimension", "Material", "MaterialContainer", "WorkingPlaneProxy"] + ] + if preferences["FULL_PARAMETRIC"]: objectslist = Arch.getAllChildren(objectslist) # create project, context and geodata settings if existing_file: project = ifcfile.by_type("IfcProject")[0] - body_contexts = [c for c in ifcfile.by_type("IfcGeometricRepresentationSubContext") if c.ContextIdentifier in ["Body", "Facetation"]] - body_contexts.extend([c for c in ifcfile.by_type("IfcGeometricRepresentationContext", include_subtypes=False) if c.ContextType == "Model"]) - context = body_contexts[0] # we take the first one (subcontext if existing, or context if not) + body_contexts = [ + c + for c in ifcfile.by_type("IfcGeometricRepresentationSubContext") + if c.ContextIdentifier in ["Body", "Facetation"] + ] + body_contexts.extend( + [ + c + for c in ifcfile.by_type( + "IfcGeometricRepresentationContext", include_subtypes=False + ) + if c.ContextType == "Model" + ] + ) + context = body_contexts[ + 0 + ] # we take the first one (subcontext if existing, or context if not) else: contextCreator = exportIFCHelper.ContextCreator(ifcfile, objectslist) context = contextCreator.model_view_subcontext project = contextCreator.project objectslist = [obj for obj in objectslist if obj != contextCreator.project_object] - if Draft.getObjectsOfType(objectslist, "Site"): # we assume one site and one representation context only - decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian) - contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2)) + if Draft.getObjectsOfType( + objectslist, "Site" + ): # we assume one site and one representation context only + decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs( + FreeCAD.Units.Radian + ) + contextCreator.model_context.TrueNorth.DirectionRatios = ( + math.cos(decl + math.pi / 2), + math.sin(decl + math.pi / 2), + ) # reusable entity system @@ -388,41 +440,47 @@ def export(exportList, filename, colors=None, preferences=None): # setup analytic model - if preferences['EXPORT_MODEL'] in ['struct','hybrid']: - exportIFCStructuralTools.setup(ifcfile,ifcbin,preferences['SCALE_FACTOR']) + if preferences["EXPORT_MODEL"] in ["struct", "hybrid"]: + exportIFCStructuralTools.setup(ifcfile, ifcbin, preferences["SCALE_FACTOR"]) # define holders for the different types we create - products = {} # { Name: IfcEntity, ... } - subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product - surfstyles = {} # { (r,g,b): IfcEntity, ... } - clones = {} # { Basename:[Clonename1,Clonename2,...] } - sharedobjects = {} # { BaseName: IfcRepresentationMap } + products = {} # { Name: IfcEntity, ... } + subproducts = ( + {} + ) # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product + surfstyles = {} # { (r,g,b): IfcEntity, ... } + clones = {} # { Basename:[Clonename1,Clonename2,...] } + sharedobjects = {} # { BaseName: IfcRepresentationMap } count = 1 - groups = {} # { Host: [Child,Child,...] } - profiledefs = {} # { ProfileDefString:profiledef,...} - shapedefs = {} # { ShapeDefString:[shapes],... } - spatialelements = {} # {Name:IfcEntity, ... } - uids = [] # store used UIDs to avoid reuse (some FreeCAD objects might have same IFC UID, ex. copy/pasted objects - classifications = {} # {Name:IfcEntity, ... } + groups = {} # { Host: [Child,Child,...] } + profiledefs = {} # { ProfileDefString:profiledef,...} + shapedefs = {} # { ShapeDefString:[shapes],... } + spatialelements = {} # {Name:IfcEntity, ... } + uids = ( + [] + ) # store used UIDs to avoid reuse (some FreeCAD objects might have same IFC UID, ex. copy/pasted objects + classifications = {} # {Name:IfcEntity, ... } curvestyles = {} # build clones table - if preferences['CREATE_CLONES']: + if preferences["CREATE_CLONES"]: for o in objectslist: - b = Draft.getCloneBase(o,strict=True) + b = Draft.getCloneBase(o, strict=True) if b: - clones.setdefault(b.Name,[]).append(o.Name) + clones.setdefault(b.Name, []).append(o.Name) - #print("clones table: ",clones) - #print(objectslist) + # print("clones table: ",clones) + # print(objectslist) # testing if more than one site selected (forbidden in IFC) # TODO: Moult: This is not forbidden in IFC. - if len(Draft.getObjectsOfType(objectslist,"Site")) > 1: - FreeCAD.Console.PrintError("More than one site is selected, which is forbidden by IFC standards. Please export only one site by IFC file.\n") + if len(Draft.getObjectsOfType(objectslist, "Site")) > 1: + FreeCAD.Console.PrintError( + "More than one site is selected, which is forbidden by IFC standards. Please export only one site by IFC file.\n" + ) return # products @@ -436,25 +494,25 @@ def export(exportList, filename, colors=None, preferences=None): # structural analysis object structobj = None - if preferences['EXPORT_MODEL'] in ['struct','hybrid']: - structobj = exportIFCStructuralTools.createStructuralMember(ifcfile,ifcbin,obj) - if preferences['EXPORT_MODEL'] == 'struct': + if preferences["EXPORT_MODEL"] in ["struct", "hybrid"]: + structobj = exportIFCStructuralTools.createStructuralMember(ifcfile, ifcbin, obj) + if preferences["EXPORT_MODEL"] == "struct": continue # getting generic data - name = getText("Name",obj) - description = getText("Description",obj) - uid = getUID(obj,preferences) + name = getText("Name", obj) + description = getText("Description", obj) + uid = getUID(obj, preferences) ifctype = getIfcTypeFromObj(obj) # print(ifctype) # handle assemblies (arrays, app::parts, references, etc...) assemblyElements = [] - assemblyTypes = ["IfcApp::Part","IfcPart::Compound","IfcElementAssembly"] + assemblyTypes = ["IfcApp::Part", "IfcPart::Compound", "IfcElementAssembly"] is_nested_group = False - if preferences['GROUPS_AS_ASSEMBLIES'] and ifctype == "IfcGroup": + if preferences["GROUPS_AS_ASSEMBLIES"] and ifctype == "IfcGroup": for p in obj.InListRecursive: if not p.isDerivedFrom("App::DocumentObjectGroup"): is_nested_group = True @@ -463,51 +521,55 @@ def export(exportList, filename, colors=None, preferences=None): clonedeltas = [] if obj.ArrayType == "ortho": for i in range(obj.NumberX): - clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)) + clonedeltas.append(obj.Placement.Base + (i * obj.IntervalX)) for j in range(obj.NumberY): if j > 0: - clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY)) + clonedeltas.append( + obj.Placement.Base + (i * obj.IntervalX) + (j * obj.IntervalY) + ) for k in range(obj.NumberZ): if k > 0: - clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY)+(k*obj.IntervalZ)) + clonedeltas.append( + obj.Placement.Base + + (i * obj.IntervalX) + + (j * obj.IntervalY) + + (k * obj.IntervalZ) + ) if clonedeltas: ifctype = "IfcElementAssembly" for delta in clonedeltas: # print("delta: {}".format(delta)) - representation,placement,shapetype = getRepresentation( + representation, placement, shapetype = getRepresentation( ifcfile, context, obj.Base, - forcebrep=(getBrepFlag(obj.Base,preferences)), + forcebrep=(getBrepFlag(obj.Base, preferences)), colors=colors, preferences=preferences, - forceclone=delta + forceclone=delta, ) subproduct = createProduct( ifcfile, obj.Base, getIfcTypeFromObj(obj.Base), - getUID(obj.Base,preferences), + getUID(obj.Base, preferences), history, - getText("Name",obj.Base), - getText("Description",obj.Base), + getText("Name", obj.Base), + getText("Description", obj.Base), placement, representation, - preferences + preferences, ) products[obj.Base.Name] = subproduct assemblyElements.append(subproduct) - exportIFCHelper.writeQuantities(ifcfile, - obj.Base, - subproduct, - history, - preferences['SCALE_FACTOR'] + exportIFCHelper.writeQuantities( + ifcfile, obj.Base, subproduct, history, preferences["SCALE_FACTOR"] ) elif ifctype in assemblyTypes or is_nested_group: - if hasattr(obj,"Group"): + if hasattr(obj, "Group"): group = obj.Group - elif hasattr(obj,"Links"): + elif hasattr(obj, "Links"): group = obj.Links else: group = [FreeCAD.ActiveDocument.getObject(n[:-1]) for n in obj.getSubObjects()] @@ -515,39 +577,40 @@ def export(exportList, filename, colors=None, preferences=None): if subobj.Name in products: subproduct = products[subobj.Name] else: - representation,placement,shapetype = getRepresentation( + representation, placement, shapetype = getRepresentation( ifcfile, context, subobj, - forcebrep=(getBrepFlag(subobj,preferences)), + forcebrep=(getBrepFlag(subobj, preferences)), colors=colors, - preferences=preferences + preferences=preferences, ) subproduct = createProduct( ifcfile, subobj, getIfcTypeFromObj(subobj), - getUID(subobj,preferences), + getUID(subobj, preferences), history, - getText("Name",subobj), - getText("Description",subobj), + getText("Name", subobj), + getText("Description", subobj), placement, representation, - preferences) + preferences, + ) products[subobj.Name] = subproduct assemblyElements.append(subproduct) ifctype = "IfcElementAssembly" # export grids - if ifctype in ["IfcAxis","IfcAxisSystem","IfcGrid"]: + if ifctype in ["IfcAxis", "IfcAxisSystem", "IfcGrid"]: ifcaxes = [] ifcpols = [] if ifctype == "IfcAxis": # make sure this axis is not included in something else already standalone = True for p in obj.InList: - if hasattr(p,"Axes") and (obj in p.Axes): + if hasattr(p, "Axes") and (obj in p.Axes): if p in objectslist: axgroups = [] standalone = False @@ -557,22 +620,31 @@ def export(exportList, filename, colors=None, preferences=None): else: axgroups = obj.Proxy.getAxisData(obj) if not axgroups: - if preferences["DEBUG"]: print("Warning! Axis system object found '{}', but no axis data found.".format(obj.Label)) + if preferences["DEBUG"]: + print( + "Warning! Axis system object found '{}', but no axis data found.".format( + obj.Label + ) + ) continue ifctype = "IfcGrid" for axg in axgroups: ifcaxg = [] for ax in axg: - p1 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[0]).multiply(preferences['SCALE_FACTOR'])[:2])) - p2 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[1]).multiply(preferences['SCALE_FACTOR'])[:2])) - pol = ifcbin.createIfcPolyline([p1,p2]) + p1 = ifcbin.createIfcCartesianPoint( + tuple(FreeCAD.Vector(ax[0]).multiply(preferences["SCALE_FACTOR"])[:2]) + ) + p2 = ifcbin.createIfcCartesianPoint( + tuple(FreeCAD.Vector(ax[1]).multiply(preferences["SCALE_FACTOR"])[:2]) + ) + pol = ifcbin.createIfcPolyline([p1, p2]) ifcpols.append(pol) - axis = ifcfile.createIfcGridAxis(ax[2],pol,True) + axis = ifcfile.createIfcGridAxis(ax[2], pol, True) ifcaxg.append(axis) if len(ifcaxes) < 3: ifcaxes.append(ifcaxg) else: - ifcaxes[2] = ifcaxes[2]+ifcaxg # IfcGrid can have max 3 axes systems + ifcaxes[2] = ifcaxes[2] + ifcaxg # IfcGrid can have max 3 axes systems u = None v = None w = None @@ -583,21 +655,39 @@ def export(exportList, filename, colors=None, preferences=None): if len(ifcaxes) > 2: w = ifcaxes[2] if u and v: - if preferences['DEBUG']: print(str(count).ljust(3)," : ", ifctype, " (",str(len(ifcpols)),"axes ) : ",name) - xvc = ifcbin.createIfcDirection((1.0,0.0,0.0)) - zvc = ifcbin.createIfcDirection((0.0,0.0,1.0)) - ovc = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0)) - gpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc) + if preferences["DEBUG"]: + print( + str(count).ljust(3), + " : ", + ifctype, + " (", + str(len(ifcpols)), + "axes ) : ", + name, + ) + xvc = ifcbin.createIfcDirection((1.0, 0.0, 0.0)) + zvc = ifcbin.createIfcDirection((0.0, 0.0, 1.0)) + ovc = ifcbin.createIfcCartesianPoint((0.0, 0.0, 0.0)) + gpl = ifcbin.createIfcAxis2Placement3D(ovc, zvc, xvc) plac = ifcbin.createIfcLocalPlacement(gpl) cset = ifcfile.createIfcGeometricCurveSet(ifcpols) - #subc = ifcfile.createIfcGeometricRepresentationSubContext('FootPrint','Model',context,None,"MODEL_VIEW",None,None,None,None,None) - srep = ifcfile.createIfcShapeRepresentation(context,'FootPrint',"GeometricCurveSet",ifcpols) - pdef = ifcfile.createIfcProductDefinitionShape(None,None,[srep]) - grid = ifcfile.createIfcGrid(uid,history,name,description,None,plac,pdef,u,v,w) + # subc = ifcfile.createIfcGeometricRepresentationSubContext('FootPrint','Model',context,None,"MODEL_VIEW",None,None,None,None,None) + srep = ifcfile.createIfcShapeRepresentation( + context, "FootPrint", "GeometricCurveSet", ifcpols + ) + pdef = ifcfile.createIfcProductDefinitionShape(None, None, [srep]) + grid = ifcfile.createIfcGrid( + uid, history, name, description, None, plac, pdef, u, v, w + ) products[obj.Name] = grid count += 1 else: - if preferences["DEBUG"]: print("Warning! Axis system object '{}' only contains one set of axis but at least two are needed for a IfcGrid to be added to IFC.".format(obj.Label)) + if preferences["DEBUG"]: + print( + "Warning! Axis system object '{}' only contains one set of axis but at least two are needed for a IfcGrid to be added to IFC.".format( + obj.Label + ) + ) continue # gather groups @@ -617,21 +707,21 @@ def export(exportList, filename, colors=None, preferences=None): # print("Assembly object: {}, thus own Shape will have no representation.".format(obj.Name)) skipshape = True - representation,placement,shapetype = getRepresentation( + representation, placement, shapetype = getRepresentation( ifcfile, context, obj, - forcebrep=(getBrepFlag(obj,preferences)), + forcebrep=(getBrepFlag(obj, preferences)), colors=colors, preferences=preferences, - skipshape=skipshape + skipshape=skipshape, ) - if preferences['GET_STANDARD']: - if isStandardCase(obj,ifctype): + if preferences["GET_STANDARD"]: + if isStandardCase(obj, ifctype): ifctype += "StandardCase" - if preferences['DEBUG']: - print(str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name) + if preferences["DEBUG"]: + print(str(count).ljust(3), " : ", ifctype, " (", shapetype, ") : ", name) # creating the product @@ -645,16 +735,17 @@ def export(exportList, filename, colors=None, preferences=None): description, placement, representation, - preferences) + preferences, + ) products[obj.Name] = product - if ifctype in ["IfcBuilding","IfcBuildingStorey","IfcSite","IfcSpace"]: + if ifctype in ["IfcBuilding", "IfcBuildingStorey", "IfcSite", "IfcSpace"]: spatialelements[obj.Name] = product # associate with structural analysis object if any if structobj: - exportIFCStructuralTools.associates(ifcfile,product,structobj) + exportIFCStructuralTools.associates(ifcfile, product, structobj) # gather assembly subelements @@ -664,116 +755,85 @@ def export(exportList, filename, colors=None, preferences=None): else: aname = "Assembly" ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - aname, - '', - products[obj.Name], - assemblyElements + ifcopenshell.guid.new(), history, aname, "", products[obj.Name], assemblyElements ) - if preferences['DEBUG']: print(" aggregating",len(assemblyElements),"object(s)") + if preferences["DEBUG"]: + print(" aggregating", len(assemblyElements), "object(s)") # additions - if hasattr(obj,"Additions") and (shapetype in ["extrusion","no shape"]): + if hasattr(obj, "Additions") and (shapetype in ["extrusion", "no shape"]): for o in obj.Additions: - r2,p2,c2 = getRepresentation(ifcfile,context,o,colors=colors,preferences=preferences) - if preferences['DEBUG']: print(" adding ",c2," : ",o.Label) + r2, p2, c2 = getRepresentation( + ifcfile, context, o, colors=colors, preferences=preferences + ) + if preferences["DEBUG"]: + print(" adding ", c2, " : ", o.Label) l = o.Label prod2 = ifcfile.createIfcBuildingElementProxy( - ifcopenshell.guid.new(), - history, - l, - None, - None, - p2, - r2, - None, - "ELEMENT" + ifcopenshell.guid.new(), history, l, None, None, p2, r2, None, "ELEMENT" ) subproducts[o.Name] = prod2 ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'Addition', - '', - product, - [prod2] + ifcopenshell.guid.new(), history, "Addition", "", product, [prod2] ) # subtractions guests = [] for o in obj.InList: - if hasattr(o,"Hosts"): + if hasattr(o, "Hosts"): for co in o.Hosts: if co == obj: if o not in guests: guests.append(o) - if hasattr(obj,"Subtractions") and (shapetype in ["extrusion","no shape"]): + if hasattr(obj, "Subtractions") and (shapetype in ["extrusion", "no shape"]): for o in obj.Subtractions + guests: - r2,p2,c2 = getRepresentation(ifcfile,context,o,subtraction=True,colors=colors,preferences=preferences) - if preferences['DEBUG']: print(" subtracting ",c2," : ",o.Label) + r2, p2, c2 = getRepresentation( + ifcfile, context, o, subtraction=True, colors=colors, preferences=preferences + ) + if preferences["DEBUG"]: + print(" subtracting ", c2, " : ", o.Label) l = o.Label prod2 = ifcfile.createIfcOpeningElement( - ifcopenshell.guid.new(), - history, - l, - None, - None, - p2, - r2, - None + ifcopenshell.guid.new(), history, l, None, None, p2, r2, None ) subproducts[o.Name] = prod2 ifcfile.createIfcRelVoidsElement( - ifcopenshell.guid.new(), - history, - 'Subtraction', - '', - product, - prod2 + ifcopenshell.guid.new(), history, "Subtraction", "", product, prod2 ) # properties ifcprop = False - if hasattr(obj,"IfcProperties"): + if hasattr(obj, "IfcProperties"): if obj.IfcProperties: ifcprop = True - if isinstance(obj.IfcProperties,dict): + if isinstance(obj.IfcProperties, dict): # IfcProperties is a dictionary (FreeCAD 0.18) psets = {} - for key,value in obj.IfcProperties.items(): - pset, pname, ptype, pvalue = getPropertyData(key,value,preferences) + for key, value in obj.IfcProperties.items(): + pset, pname, ptype, pvalue = getPropertyData(key, value, preferences) if pvalue is None: - if preferences['DEBUG']: print(" property ", pname," ignored because no value found.") + if preferences["DEBUG"]: + print(" property ", pname, " ignored because no value found.") continue - p = ifcbin.createIfcPropertySingleValue(str(pname),str(ptype),pvalue) - psets.setdefault(pset,[]).append(p) - for pname,props in psets.items(): + p = ifcbin.createIfcPropertySingleValue(str(pname), str(ptype), pvalue) + psets.setdefault(pset, []).append(p) + for pname, props in psets.items(): pset = ifcfile.createIfcPropertySet( - ifcopenshell.guid.new(), - history, - pname, - None, - props + ifcopenshell.guid.new(), history, pname, None, props ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product], - pset + ifcopenshell.guid.new(), history, None, None, [product], pset ) - elif obj.IfcProperties.TypeId == 'Spreadsheet::Sheet': + elif obj.IfcProperties.TypeId == "Spreadsheet::Sheet": # IfcProperties is a spreadsheet (deprecated) @@ -783,20 +843,25 @@ def export(exportList, filename, colors=None, preferences=None): n = 2 cell = True while cell is True: - if hasattr(sheet,'A'+str(n)): - cat = sheet.get('A'+str(n)) - key = sheet.get('B'+str(n)) - tp = sheet.get('C'+str(n)) - if hasattr(sheet,'D'+str(n)): - val = sheet.get('D'+str(n)) + if hasattr(sheet, "A" + str(n)): + cat = sheet.get("A" + str(n)) + key = sheet.get("B" + str(n)) + tp = sheet.get("C" + str(n)) + if hasattr(sheet, "D" + str(n)): + val = sheet.get("D" + str(n)) else: - val = '' + val = "" key = str(key) tp = str(tp) - if tp in ["IfcLabel","IfcText","IfcIdentifier",'IfcDescriptiveMeasure']: + if tp in [ + "IfcLabel", + "IfcText", + "IfcIdentifier", + "IfcDescriptiveMeasure", + ]: val = val.encode("utf8") elif tp == "IfcBoolean": - if val == 'True': + if val == "True": val = True else: val = False @@ -805,11 +870,15 @@ def export(exportList, filename, colors=None, preferences=None): else: val = float(val) unit = None - #unit = sheet.get('E'+str(n)) + # unit = sheet.get('E'+str(n)) if cat in categories: - propertiesDic[cat].append({"key":key,"tp":tp,"val":val,"unit":unit}) + propertiesDic[cat].append( + {"key": key, "tp": tp, "val": val, "unit": unit} + ) else: - propertiesDic[cat] = [{"key":key,"tp":tp,"val":val,"unit":unit}] + propertiesDic[cat] = [ + {"key": key, "tp": tp, "val": val, "unit": unit} + ] categories.append(cat) n += 1 else: @@ -817,40 +886,48 @@ def export(exportList, filename, colors=None, preferences=None): for cat in propertiesDic: props = [] for prop in propertiesDic[cat]: - if preferences['DEBUG']: - print("key",prop["key"],type(prop["key"])) - print("tp",prop["tp"],type(prop["tp"])) - print("val",prop["val"],type(prop["val"])) + if preferences["DEBUG"]: + print("key", prop["key"], type(prop["key"])) + print("tp", prop["tp"], type(prop["tp"])) + print("val", prop["val"], type(prop["val"])) if tp.lower().startswith("ifc"): - props.append(ifcbin.createIfcPropertySingleValue(prop["key"],prop["tp"],prop["val"])) + props.append( + ifcbin.createIfcPropertySingleValue( + prop["key"], prop["tp"], prop["val"] + ) + ) else: - print("Unable to create a property of type:",tp) + print("Unable to create a property of type:", tp) if props: pset = ifcfile.createIfcPropertySet( - ifcopenshell.guid.new(), - history,cat, - None, - props + ifcopenshell.guid.new(), history, cat, None, props ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product], - pset + ifcopenshell.guid.new(), history, None, None, [product], pset ) - if hasattr(obj,"IfcData"): + if hasattr(obj, "IfcData"): if obj.IfcData: ifcprop = True - #if preferences['DEBUG'] : print(" adding ifc attributes") + # if preferences['DEBUG'] : print(" adding ifc attributes") props = [] for key in obj.IfcData: - if not (key in ["attributes", "complex_attributes", "IfcUID", "FlagForceBrep", - "ExportHeight", "ExportWidth", "ExportLength", "ExportHorizontalArea", - "ExportVerticalArea", "ExportVolume"]): + if not ( + key + in [ + "attributes", + "complex_attributes", + "IfcUID", + "FlagForceBrep", + "ExportHeight", + "ExportWidth", + "ExportLength", + "ExportHorizontalArea", + "ExportVerticalArea", + "ExportVolume", + ] + ): # (deprecated) properties in IfcData dict are stored as "key":"type(value)" @@ -863,8 +940,13 @@ def export(exportList, filename, colors=None, preferences=None): val = "(".join(r[1:]) val = val.strip("'") val = val.strip('"') - #if preferences['DEBUG']: print(" property ",key," : ",val.encode("utf8"), " (", str(tp), ")") - if tp in ["IfcLabel","IfcText","IfcIdentifier",'IfcDescriptiveMeasure']: + # if preferences['DEBUG']: print(" property ",key," : ",val.encode("utf8"), " (", str(tp), ")") + if tp in [ + "IfcLabel", + "IfcText", + "IfcIdentifier", + "IfcDescriptiveMeasure", + ]: pass elif tp == "IfcBoolean": if val == ".T.": @@ -875,130 +957,132 @@ def export(exportList, filename, colors=None, preferences=None): val = int(val) else: val = float(val) - props.append(ifcbin.createIfcPropertySingleValue(str(key),str(tp),val)) + props.append(ifcbin.createIfcPropertySingleValue(str(key), str(tp), val)) if props: pset = ifcfile.createIfcPropertySet( - ifcopenshell.guid.new(), - history, - 'PropertySet', - None, - props + ifcopenshell.guid.new(), history, "PropertySet", None, props ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product], - pset + ifcopenshell.guid.new(), history, None, None, [product], pset ) if not ifcprop: - #if preferences['DEBUG'] : print("no ifc properties to export") + # if preferences['DEBUG'] : print("no ifc properties to export") pass # Quantities - exportIFCHelper.writeQuantities(ifcfile, obj, product, history, preferences['SCALE_FACTOR']) + exportIFCHelper.writeQuantities(ifcfile, obj, product, history, preferences["SCALE_FACTOR"]) - if preferences['FULL_PARAMETRIC']: + if preferences["FULL_PARAMETRIC"]: # exporting all the object properties FreeCADProps = [] FreeCADGuiProps = [] - FreeCADProps.append(ifcbin.createIfcPropertySingleValue("FreeCADType","IfcText",obj.TypeId)) - FreeCADProps.append(ifcbin.createIfcPropertySingleValue("FreeCADName","IfcText",obj.Name)) - sets = [("App",obj)] - if hasattr(obj,"Proxy"): + FreeCADProps.append( + ifcbin.createIfcPropertySingleValue("FreeCADType", "IfcText", obj.TypeId) + ) + FreeCADProps.append( + ifcbin.createIfcPropertySingleValue("FreeCADName", "IfcText", obj.Name) + ) + sets = [("App", obj)] + if hasattr(obj, "Proxy"): if obj.Proxy: - FreeCADProps.append(ifcbin.createIfcPropertySingleValue("FreeCADAppObject","IfcText",str(obj.Proxy.__class__))) + FreeCADProps.append( + ifcbin.createIfcPropertySingleValue( + "FreeCADAppObject", "IfcText", str(obj.Proxy.__class__) + ) + ) if FreeCAD.GuiUp: if obj.ViewObject: - sets.append(("Gui",obj.ViewObject)) - if hasattr(obj.ViewObject,"Proxy"): + sets.append(("Gui", obj.ViewObject)) + if hasattr(obj.ViewObject, "Proxy"): if obj.ViewObject.Proxy: FreeCADGuiProps.append( ifcbin.createIfcPropertySingleValue( "FreeCADGuiObject", "IfcText", - str(obj.ViewObject.Proxy.__class__) + str(obj.ViewObject.Proxy.__class__), ) ) - for realm,ctx in sets: + for realm, ctx in sets: if ctx: for prop in ctx.PropertiesList: - if not(prop in ["IfcProperties","IfcData","Shape","Proxy","ExpressionEngine","AngularDeflection","BoundingBox"]): + if not ( + prop + in [ + "IfcProperties", + "IfcData", + "Shape", + "Proxy", + "ExpressionEngine", + "AngularDeflection", + "BoundingBox", + ] + ): try: ptype = ctx.getTypeIdOfProperty(prop) except AttributeError: ptype = "Unknown" itype = None ivalue = None - if ptype in ["App::PropertyString","App::PropertyEnumeration"]: + if ptype in ["App::PropertyString", "App::PropertyEnumeration"]: itype = "IfcText" - ivalue = getattr(ctx,prop) + ivalue = getattr(ctx, prop) elif ptype == "App::PropertyInteger": itype = "IfcInteger" - ivalue = getattr(ctx,prop) + ivalue = getattr(ctx, prop) elif ptype == "App::PropertyFloat": itype = "IfcReal" - ivalue = float(getattr(ctx,prop)) + ivalue = float(getattr(ctx, prop)) elif ptype == "App::PropertyBool": itype = "IfcBoolean" - ivalue = getattr(ctx,prop) - elif ptype in ["App::PropertyVector","App::PropertyPlacement"]: + ivalue = getattr(ctx, prop) + elif ptype in ["App::PropertyVector", "App::PropertyPlacement"]: itype = "IfcText" - ivalue = str(getattr(ctx,prop)) - elif ptype in ["App::PropertyLength","App::PropertyDistance"]: + ivalue = str(getattr(ctx, prop)) + elif ptype in ["App::PropertyLength", "App::PropertyDistance"]: itype = "IfcReal" - ivalue = float(getattr(ctx,prop).getValueAs("m")) + ivalue = float(getattr(ctx, prop).getValueAs("m")) elif ptype == "App::PropertyArea": itype = "IfcReal" - ivalue = float(getattr(ctx,prop).getValueAs("m^2")) + ivalue = float(getattr(ctx, prop).getValueAs("m^2")) elif ptype == "App::PropertyLink": - t = getattr(ctx,prop) + t = getattr(ctx, prop) if t: itype = "IfcText" ivalue = "FreeCADLink_" + t.Name else: - if preferences['DEBUG']: print("Unable to encode property ",prop," of type ",ptype) + if preferences["DEBUG"]: + print("Unable to encode property ", prop, " of type ", ptype) if itype: # TODO add description if realm == "Gui": - FreeCADGuiProps.append(ifcbin.createIfcPropertySingleValue("FreeCADGui_"+prop,itype,ivalue)) + FreeCADGuiProps.append( + ifcbin.createIfcPropertySingleValue( + "FreeCADGui_" + prop, itype, ivalue + ) + ) else: - FreeCADProps.append(ifcbin.createIfcPropertySingleValue("FreeCAD_"+prop,itype,ivalue)) + FreeCADProps.append( + ifcbin.createIfcPropertySingleValue( + "FreeCAD_" + prop, itype, ivalue + ) + ) if FreeCADProps: pset = ifcfile.createIfcPropertySet( - ifcopenshell.guid.new(), - history,'FreeCADPropertySet', - None, - FreeCADProps + ifcopenshell.guid.new(), history, "FreeCADPropertySet", None, FreeCADProps ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product], - pset + ifcopenshell.guid.new(), history, None, None, [product], pset ) if FreeCADGuiProps: pset = ifcfile.createIfcPropertySet( - ifcopenshell.guid.new(), - history, - 'FreeCADGuiPropertySet', - None, - FreeCADGuiProps + ifcopenshell.guid.new(), history, "FreeCADGuiPropertySet", None, FreeCADGuiProps ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product], - pset + ifcopenshell.guid.new(), history, None, None, [product], pset ) # Classifications @@ -1022,17 +1106,18 @@ def export(exportList, filename, colors=None, preferences=None): else: rel = ifcfile.createIfcRelAssociatesClassification( ifcopenshell.guid.new(), - history,'FreeCADClassificationRel', + history, + "FreeCADClassificationRel", None, [product], - ref + ref, ) count += 1 # relate structural analysis objects to the struct model - if preferences['EXPORT_MODEL'] in ['struct','hybrid']: + if preferences["EXPORT_MODEL"] in ["struct", "hybrid"]: exportIFCStructuralTools.createStructuralGroup(ifcfile) # relationships @@ -1046,8 +1131,8 @@ def export(exportList, filename, colors=None, preferences=None): # buildingParts can be exported as any "normal" IFC type. In that case, gather their elements first # if ifc type is "Undefined" gather elements too - for bp in Draft.getObjectsOfType(objectslist,"BuildingPart"): - if bp.IfcType not in ["Site","Building","Building Storey","Space"]: + for bp in Draft.getObjectsOfType(objectslist, "BuildingPart"): + if bp.IfcType not in ["Site", "Building", "Building Storey", "Space"]: if bp.Name in products: subs = [] for c in bp.Group: @@ -1056,26 +1141,25 @@ def export(exportList, filename, colors=None, preferences=None): treated.append(c.Name) if subs: ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'Assembly', - '', - products[bp.Name], - subs + ifcopenshell.guid.new(), history, "Assembly", "", products[bp.Name], subs ) # storeys - for floor in Draft.getObjectsOfType(objectslist,"Floor")+Draft.getObjectsOfType(objectslist,"BuildingPart"): - if (Draft.getType(floor) == "Floor") or (hasattr(floor,"IfcType") and floor.IfcType == "Building Storey"): + for floor in Draft.getObjectsOfType(objectslist, "Floor") + Draft.getObjectsOfType( + objectslist, "BuildingPart" + ): + if (Draft.getType(floor) == "Floor") or ( + hasattr(floor, "IfcType") and floor.IfcType == "Building Storey" + ): objs = Draft.get_group_contents(floor, walls=True, addgroups=True) objs = Arch.pruneIncluded(objs) - objs.remove(floor) # get_group_contents + addgroups will include the floor itself + objs.remove(floor) # get_group_contents + addgroups will include the floor itself buildingelements, spaces = [], [] for c in objs: if c.Name in products and c.Name not in treated: prod = products[c.Name] - if prod.is_a() == 'IfcSpace': + if prod.is_a() == "IfcSpace": spaces.append(prod) else: buildingelements.append(prod) @@ -1083,39 +1167,34 @@ def export(exportList, filename, colors=None, preferences=None): f = products[floor.Name] if buildingelements: ifcfile.createIfcRelContainedInSpatialStructure( - ifcopenshell.guid.new(), - history, - 'StoreyLink', - '', - buildingelements, - f + ifcopenshell.guid.new(), history, "StoreyLink", "", buildingelements, f ) if spaces: ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'StoreyLink', - '', - f, - spaces + ifcopenshell.guid.new(), history, "StoreyLink", "", f, spaces ) floors.append(f) defaulthost = f # buildings - for building in Draft.getObjectsOfType(objectslist,"Building")+Draft.getObjectsOfType(objectslist,"BuildingPart"): - if (Draft.getType(building) == "Building") or (hasattr(building,"IfcType") and building.IfcType == "Building"): - objs = Draft.get_group_contents(building, walls=True, - addgroups=True) + for building in Draft.getObjectsOfType(objectslist, "Building") + Draft.getObjectsOfType( + objectslist, "BuildingPart" + ): + if (Draft.getType(building) == "Building") or ( + hasattr(building, "IfcType") and building.IfcType == "Building" + ): + objs = Draft.get_group_contents(building, walls=True, addgroups=True) objs = Arch.pruneIncluded(objs) children = [] childfloors = [] for c in objs: if not (c.Name in treated): - if c.Name != building.Name: # get_group_contents + addgroups will include the building itself + if ( + c.Name != building.Name + ): # get_group_contents + addgroups will include the building itself if c.Name in products: - if Draft.getType(c) in ["Floor","BuildingPart","Space"]: + if Draft.getType(c) in ["Floor", "BuildingPart", "Space"]: childfloors.append(products[c.Name]) treated.append(c.Name) elif not (c.Name in treated): @@ -1124,24 +1203,14 @@ def export(exportList, filename, colors=None, preferences=None): b = products[building.Name] if children: ifcfile.createIfcRelContainedInSpatialStructure( - ifcopenshell.guid.new(), - history, - 'BuildingLink', - '', - children, - b + ifcopenshell.guid.new(), history, "BuildingLink", "", children, b ) if childfloors: ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'BuildingLink', - '', - b, - childfloors + ifcopenshell.guid.new(), history, "BuildingLink", "", b, childfloors ) buildings.append(b) - if not defaulthost and not preferences['ADD_DEFAULT_STOREY']: + if not defaulthost and not preferences["ADD_DEFAULT_STOREY"]: defaulthost = b # sites @@ -1152,7 +1221,9 @@ def export(exportList, filename, colors=None, preferences=None): children = [] childbuildings = [] for c in objs: - if c.Name != site.Name: # get_group_contents + addgroups will include the building itself + if ( + c.Name != site.Name + ): # get_group_contents + addgroups will include the building itself if c.Name in products: if not (c.Name in treated): if Draft.getType(c) == "Building": @@ -1163,149 +1234,140 @@ def export(exportList, filename, colors=None, preferences=None): # add default site, building and storey as required if not sites: - if preferences['ADD_DEFAULT_SITE'] and not existing_file: - if preferences['DEBUG']: print("No site found. Adding default site") - sites = [ifcfile.createIfcSite( - ifcopenshell.guid.new(), - history,"Default Site", - '', - None, - None, - None, - None, - "ELEMENT", - None, - None, - None, - None, - None - )] - if sites: - ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'ProjectLink', - '', - project,sites - ) - if not buildings: - if preferences['ADD_DEFAULT_BUILDING'] and not existing_file: - if preferences['DEBUG']: print("No building found. Adding default building") - buildings = [ifcfile.createIfcBuilding( - ifcopenshell.guid.new(), - history, - "Default Building", - '', - None, - None, - None, - None, - "ELEMENT", - None, - None, - None - )] - if buildings and (not sites): - ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'ProjectLink', - '', - project,buildings - ) - if floors and buildings: - ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'BuildingLink', - '', - buildings[0],floors - ) - if sites and buildings: - ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'SiteLink', - '', - sites[0], - buildings - ) - - # treat objects that are not related to any site, building or storey - - untreated = [] - for k,v in products.items(): - if not(k in treated): - if (not buildings) or (k != buildings[0].Name): - if not(Draft.getType(FreeCAD.ActiveDocument.getObject(k)) in ["Site","Building","Floor","BuildingPart"]): - untreated.append(v) - elif Draft.getType(FreeCAD.ActiveDocument.getObject(k)) == "BuildingPart": - if not(FreeCAD.ActiveDocument.getObject(k).IfcType in ["Building","Building Storey","Site","Space"]): - # if ifc type is "Undefined" the object is added to untreated - untreated.append(v) - if untreated: - if not defaulthost: - if preferences['ADD_DEFAULT_STOREY'] and not existing_file: - if preferences['DEBUG']: print("No floor found. Adding default floor") - defaulthost = ifcfile.createIfcBuildingStorey( + if preferences["ADD_DEFAULT_SITE"] and not existing_file: + if preferences["DEBUG"]: + print("No site found. Adding default site") + sites = [ + ifcfile.createIfcSite( ifcopenshell.guid.new(), history, - "Default Storey", - '', + "Default Site", + "", None, None, None, None, "ELEMENT", - None + None, + None, + None, + None, + None, + ) + ] + if sites: + ifcfile.createIfcRelAggregates( + ifcopenshell.guid.new(), history, "ProjectLink", "", project, sites + ) + if not buildings: + if preferences["ADD_DEFAULT_BUILDING"] and not existing_file: + if preferences["DEBUG"]: + print("No building found. Adding default building") + buildings = [ + ifcfile.createIfcBuilding( + ifcopenshell.guid.new(), + history, + "Default Building", + "", + None, + None, + None, + None, + "ELEMENT", + None, + None, + None, + ) + ] + if buildings and (not sites): + ifcfile.createIfcRelAggregates( + ifcopenshell.guid.new(), history, "ProjectLink", "", project, buildings + ) + if floors and buildings: + ifcfile.createIfcRelAggregates( + ifcopenshell.guid.new(), history, "BuildingLink", "", buildings[0], floors + ) + if sites and buildings: + ifcfile.createIfcRelAggregates( + ifcopenshell.guid.new(), history, "SiteLink", "", sites[0], buildings + ) + + # treat objects that are not related to any site, building or storey + + untreated = [] + for k, v in products.items(): + if not (k in treated): + if (not buildings) or (k != buildings[0].Name): + if not ( + Draft.getType(FreeCAD.ActiveDocument.getObject(k)) + in ["Site", "Building", "Floor", "BuildingPart"] + ): + untreated.append(v) + elif Draft.getType(FreeCAD.ActiveDocument.getObject(k)) == "BuildingPart": + if not ( + FreeCAD.ActiveDocument.getObject(k).IfcType + in ["Building", "Building Storey", "Site", "Space"] + ): + # if ifc type is "Undefined" the object is added to untreated + untreated.append(v) + if untreated: + if not defaulthost: + if preferences["ADD_DEFAULT_STOREY"] and not existing_file: + if preferences["DEBUG"]: + print("No floor found. Adding default floor") + defaulthost = ifcfile.createIfcBuildingStorey( + ifcopenshell.guid.new(), + history, + "Default Storey", + "", + None, + None, + None, + None, + "ELEMENT", + None, ) # if preferences['ADD_DEFAULT_STOREY'] is on, we need a building # to host it, regardless of preferences['ADD_DEFAULT_BUILDING'] if not buildings: - if preferences['DEBUG']: print("No building found. Adding default building") - buildings = [ifcfile.createIfcBuilding( - ifcopenshell.guid.new(), - history, - "Default Building", - '', - None, - None, - None, - None, - "ELEMENT", - None, - None, - None - )] - if sites: - ifcfile.createIfcRelAggregates( + if preferences["DEBUG"]: + print("No building found. Adding default building") + buildings = [ + ifcfile.createIfcBuilding( ifcopenshell.guid.new(), history, - 'SiteLink', - '', - sites[0], - buildings + "Default Building", + "", + None, + None, + None, + None, + "ELEMENT", + None, + None, + None, + ) + ] + if sites: + ifcfile.createIfcRelAggregates( + ifcopenshell.guid.new(), history, "SiteLink", "", sites[0], buildings ) else: ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'ProjectLink', - '', - project,buildings + ifcopenshell.guid.new(), history, "ProjectLink", "", project, buildings ) ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'DefaultStoreyLink', - '', + "DefaultStoreyLink", + "", buildings[0], - [defaulthost] + [defaulthost], ) elif buildings: defaulthost = buildings[0] if defaulthost: - spaces, buildingelements = [],[] + spaces, buildingelements = [], [] for entity in untreated: if entity.is_a() == "IfcSpace": spaces.append(entity) @@ -1315,29 +1377,28 @@ def export(exportList, filename, colors=None, preferences=None): ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'UnassignedObjectsLink', - '', + "UnassignedObjectsLink", + "", defaulthost, - spaces + spaces, ) if buildingelements: ifcfile.createIfcRelContainedInSpatialStructure( ifcopenshell.guid.new(), history, - 'UnassignedObjectsLink', - '', + "UnassignedObjectsLink", + "", buildingelements, - defaulthost + defaulthost, ) else: # no default host: aggregate unassigned objects directly under the IfcProject - WARNING: NON STANDARD - if preferences['DEBUG']: print("WARNING - Default building generation is disabled. You are producing a non-standard file.") + if preferences["DEBUG"]: + print( + "WARNING - Default building generation is disabled. You are producing a non-standard file." + ) ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'ProjectLink', - '', - project,untreated + ifcopenshell.guid.new(), history, "ProjectLink", "", project, untreated ) # materials @@ -1346,7 +1407,7 @@ def export(exportList, filename, colors=None, preferences=None): for m in Arch.getDocumentMaterials(): relobjs = [] for o in m.InList: - if hasattr(o,"Material"): + if hasattr(o, "Material"): if o.Material: if o.Material.isDerivedFrom("App::MaterialObject"): # TODO : support multimaterials too @@ -1360,35 +1421,35 @@ def export(exportList, filename, colors=None, preferences=None): mat = ifcfile.createIfcMaterial(l) materials[m.Label] = mat rgb = None - if hasattr(m,"Color"): + if hasattr(m, "Color"): rgb = m.Color[:3] else: - for colorslot in ["Color","DiffuseColor","ViewColor"]: + for colorslot in ["Color", "DiffuseColor", "ViewColor"]: if colorslot in m.Material: if m.Material[colorslot]: if m.Material[colorslot][0] == "(": - rgb = tuple([float(f) for f in m.Material[colorslot].strip("()").split(",")]) + rgb = tuple( + [float(f) for f in m.Material[colorslot].strip("()").split(",")] + ) break if rgb: - psa = ifcbin.createIfcPresentationStyleAssignment(l,rgb[0],rgb[1],rgb[2],ifc4=(preferences["SCHEMA"] == "IFC4")) - isi = ifcfile.createIfcStyledItem(None,[psa],None) - isr = ifcfile.createIfcStyledRepresentation(context,"Style","Material",[isi]) - imd = ifcfile.createIfcMaterialDefinitionRepresentation(None,None,[isr],mat) + psa = ifcbin.createIfcPresentationStyleAssignment( + l, rgb[0], rgb[1], rgb[2], ifc4=(preferences["SCHEMA"] == "IFC4") + ) + isi = ifcfile.createIfcStyledItem(None, [psa], None) + isr = ifcfile.createIfcStyledRepresentation(context, "Style", "Material", [isi]) + imd = ifcfile.createIfcMaterialDefinitionRepresentation(None, None, [isr], mat) ifcfile.createIfcRelAssociatesMaterial( - ifcopenshell.guid.new(), - history, - 'MaterialLink', - '', - relobjs, - mat + ifcopenshell.guid.new(), history, "MaterialLink", "", relobjs, mat ) # 2D objects annos = {} - if preferences['EXPORT_2D']: + if preferences["EXPORT_2D"]: curvestyles = {} - if annotations and preferences['DEBUG']: print("exporting 2D objects...") + if annotations and preferences["DEBUG"]: + print("exporting 2D objects...") for anno in annotations: ann = create_annotation(anno, ifcfile, context, history, preferences) annos[anno.Name] = ann @@ -1397,7 +1458,8 @@ def export(exportList, filename, colors=None, preferences=None): specs = {} for spec in specials: - if preferences['DEBUG']: print("exporting special object:",spec.Label) + if preferences["DEBUG"]: + print("exporting special object:", spec.Label) elt = spec.Proxy.export_ifc(spec, ifcfile) specs[spec.Name] = elt @@ -1409,17 +1471,17 @@ def export(exportList, filename, colors=None, preferences=None): for g in groups.keys(): okay = True for c in groups[g]: - if Draft.getType(FreeCAD.ActiveDocument.getObject(c)) in ["Group","VisGroup"]: + if Draft.getType(FreeCAD.ActiveDocument.getObject(c)) in ["Group", "VisGroup"]: okay = False for s in sortedgroups: if s[0] == c: okay = True if okay: - sortedgroups.append([g,groups[g]]) + sortedgroups.append([g, groups[g]]) for g in sortedgroups: if g[0] in groups: del groups[g[0]] - #print("sorted groups:",sortedgroups) + # print("sorted groups:",sortedgroups) containers = {} for g in sortedgroups: if g[1]: @@ -1432,23 +1494,11 @@ def export(exportList, filename, colors=None, preferences=None): swallowed.append(annos[o]) if children: name = FreeCAD.ActiveDocument.getObject(g[0]).Label - grp = ifcfile.createIfcGroup( - ifcopenshell.guid.new(), - history, - name, - '', - None - ) + grp = ifcfile.createIfcGroup(ifcopenshell.guid.new(), history, name, "", None) products[g[0]] = grp spatialelements[g[0]] = grp ass = ifcfile.createIfcRelAssignsToGroup( - ifcopenshell.guid.new(), - history, - 'GroupLink', - '', - children, - None, - grp + ifcopenshell.guid.new(), history, "GroupLink", "", children, None, grp ) # stack groups inside containers @@ -1457,17 +1507,12 @@ def export(exportList, filename, colors=None, preferences=None): for g in sortedgroups: go = FreeCAD.ActiveDocument.getObject(g[0]) for parent in go.InList: - if hasattr(parent,"Group") and (go in parent.Group): + if hasattr(parent, "Group") and (go in parent.Group): if (parent.Name in spatialelements) and (g[0] in spatialelements): - stack.setdefault(parent.Name,[]).append(spatialelements[g[0]]) - for k,v in stack.items(): + stack.setdefault(parent.Name, []).append(spatialelements[g[0]]) + for k, v in stack.items(): ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'GroupStackLink', - '', - spatialelements[k], - v + ifcopenshell.guid.new(), history, "GroupStackLink", "", spatialelements[k], v ) # add remaining 2D objects to default host @@ -1476,70 +1521,74 @@ def export(exportList, filename, colors=None, preferences=None): remaining = [anno for anno in annos.values() if anno not in swallowed] if remaining: if not defaulthost: - if preferences['ADD_DEFAULT_STOREY']: - if preferences['DEBUG']: print("No floor found. Adding default floor") + if preferences["ADD_DEFAULT_STOREY"]: + if preferences["DEBUG"]: + print("No floor found. Adding default floor") defaulthost = ifcfile.createIfcBuildingStorey( ifcopenshell.guid.new(), history, "Default Storey", - '', + "", None, None, None, None, "ELEMENT", - None + None, ) # if preferences['ADD_DEFAULT_STOREY'] is on, we need a # building to host it, regardless of # preferences['ADD_DEFAULT_BUILDING'] if not buildings: - buildings = [ifcfile.createIfcBuilding( - ifcopenshell.guid.new(), - history, - "Default Building", - '', - None, - None, - None, - None, - "ELEMENT", - None, - None, - None - )] + buildings = [ + ifcfile.createIfcBuilding( + ifcopenshell.guid.new(), + history, + "Default Building", + "", + None, + None, + None, + None, + "ELEMENT", + None, + None, + None, + ) + ] if sites: ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'SiteLink', - '', + "SiteLink", + "", sites[0], - buildings + buildings, ) else: ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'ProjectLink', - '', - project,buildings + "ProjectLink", + "", + project, + buildings, ) ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'DefaultStoreyLink', - '', + "DefaultStoreyLink", + "", buildings[0], - [defaulthost] + [defaulthost], ) - elif preferences['ADD_DEFAULT_BUILDING']: + elif preferences["ADD_DEFAULT_BUILDING"]: if not buildings: defaulthost = ifcfile.createIfcBuilding( ifcopenshell.guid.new(), history, "Default Building", - '', + "", None, None, None, @@ -1547,65 +1596,57 @@ def export(exportList, filename, colors=None, preferences=None): "ELEMENT", None, None, - None + None, ) if sites: ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'SiteLink', - '', + "SiteLink", + "", sites[0], - [defaulthost] + [defaulthost], ) else: ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'ProjectLink', - '', + "ProjectLink", + "", project, - [defaulthost] + [defaulthost], ) if defaulthost: ifcfile.createIfcRelContainedInSpatialStructure( - ifcopenshell.guid.new(), - history, - 'AnnotationsLink', - '', - remaining, - defaulthost + ifcopenshell.guid.new(), history, "AnnotationsLink", "", remaining, defaulthost ) else: ifcfile.createIfcRelAggregates( - ifcopenshell.guid.new(), - history, - 'ProjectLink', - '', - project, - remaining + ifcopenshell.guid.new(), history, "ProjectLink", "", project, remaining ) if not existing_file: - if preferences['DEBUG']: - print("writing ",filename,"...") + if preferences["DEBUG"]: + print("writing ", filename, "...") if filename.lower().endswith("json"): - writeJson(filename,ifcfile) + writeJson(filename, ifcfile) else: ifcfile.write(filename) - if preferences['STORE_UID']: + if preferences["STORE_UID"]: # some properties might have been changed FreeCAD.ActiveDocument.recompute() os.remove(templatefile) - if preferences['DEBUG'] and ifcbin.compress and (not filename.lower().endswith("json")): - f = pyopen(filename,"r") + if preferences["DEBUG"] and ifcbin.compress and (not filename.lower().endswith("json")): + f = pyopen(filename, "r") s = len(f.read().split("\n")) f.close() - print("Compression ratio:",int((float(ifcbin.spared)/(s+ifcbin.spared))*100),"%") + print( + "Compression ratio:", int((float(ifcbin.spared) / (s + ifcbin.spared)) * 100), "%" + ) del ifcbin if existing_file: @@ -1619,7 +1660,8 @@ def export(exportList, filename, colors=None, preferences=None): # ************************************************************************************************ # ********** helper for export IFC ************** -def getPropertyData(key,value,preferences): + +def getPropertyData(key, value, preferences): # in 0.18, properties in IfcProperties dict are stored as "key":"pset;;type;;value" or "key":"type;;value" # in 0.19, key = name;;pset, value = ptype;;value (because there can be several props with same name) @@ -1640,13 +1682,14 @@ def getPropertyData(key,value,preferences): ptype = value[0] pvalue = value[1] else: - if preferences['DEBUG']:print(" unable to export property:",pname,value) + if preferences["DEBUG"]: + print(" unable to export property:", pname, value) return pset, pname, ptype, None - #if preferences['DEBUG']: print(" property ",pname," : ",pvalue.encode("utf8"), " (", str(ptype), ") in ",pset) + # if preferences['DEBUG']: print(" property ",pname," : ",pvalue.encode("utf8"), " (", str(ptype), ") in ",pset) if pvalue == "": return pset, pname, ptype, None - if ptype in ["IfcLabel","IfcText","IfcIdentifier",'IfcDescriptiveMeasure']: + if ptype in ["IfcLabel", "IfcText", "IfcIdentifier", "IfcDescriptiveMeasure"]: pass elif ptype == "IfcBoolean": if pvalue in ["True", "False"]: @@ -1671,17 +1714,20 @@ def getPropertyData(key,value,preferences): try: pvalue = FreeCAD.Units.Quantity(pvalue).Value except Exception: - if preferences['DEBUG']:print(" warning: unable to export property as numeric value:",pname,pvalue) + if preferences["DEBUG"]: + print( + " warning: unable to export property as numeric value:", pname, pvalue + ) # print('pset: {}, pname: {}, ptype: {}, pvalue: {}'.format(pset, pname, ptype, pvalue)) return pset, pname, ptype, pvalue -def isStandardCase(obj,ifctype): +def isStandardCase(obj, ifctype): if ifctype.endswith("StandardCase"): - return False # type is already standard case, return False so "StandardCase" is not added twice - if hasattr(obj,"Proxy") and hasattr(obj.Proxy,"isStandardCase"): + return False # type is already standard case, return False so "StandardCase" is not added twice + if hasattr(obj, "Proxy") and hasattr(obj.Proxy, "isStandardCase"): return obj.Proxy.isStandardCase(obj) return False @@ -1689,15 +1735,15 @@ def isStandardCase(obj,ifctype): def getIfcTypeFromObj(obj): dtype = Draft.getType(obj) - if (dtype == "BuildingPart") and hasattr(obj,"IfcType") and (obj.IfcType == "Undefined"): + if (dtype == "BuildingPart") and hasattr(obj, "IfcType") and (obj.IfcType == "Undefined"): ifctype = "IfcBuildingElementPart" obj.IfcType = "Building Element Part" # export BuildingParts as Building Element Parts if their type wasn't explicitly set # set IfcType in the object as well # https://forum.freecad.org/viewtopic.php?p=662934#p662927 - elif hasattr(obj,"IfcType"): - ifctype = obj.IfcType.replace(" ","") - elif dtype in ["App::Part","Part::Compound"]: + elif hasattr(obj, "IfcType"): + ifctype = obj.IfcType.replace(" ", "") + elif dtype in ["App::Part", "Part::Compound"]: ifctype = "IfcElementAssembly" elif dtype in ["App::DocumentObjectGroup"]: ifctype = "IfcGroup" @@ -1730,23 +1776,22 @@ def exportIFC2X3Attributes(obj, kwargs, scale=0.001): kwargs.update({"CompositionType": "ELEMENT"}) elif ifctype == "IfcSpace": internal = "NOTDEFINED" - if hasattr(obj,"Internal"): + if hasattr(obj, "Internal"): if obj.Internal: internal = "INTERNAL" else: internal = "EXTERNAL" - kwargs.update({ - "CompositionType": "ELEMENT", - "InteriorOrExteriorSpace": internal, - "ElevationWithFlooring": obj.Shape.BoundBox.ZMin*scale - }) + kwargs.update( + { + "CompositionType": "ELEMENT", + "InteriorOrExteriorSpace": internal, + "ElevationWithFlooring": obj.Shape.BoundBox.ZMin * scale, + } + ) elif ifctype == "IfcReinforcingBar": - kwargs.update({ - "NominalDiameter": obj.Diameter.Value, - "BarLength": obj.Length.Value - }) + kwargs.update({"NominalDiameter": obj.Diameter.Value, "BarLength": obj.Length.Value}) elif ifctype == "IfcBuildingStorey": - kwargs.update({"Elevation": obj.Placement.Base.z*scale}) + kwargs.update({"Elevation": obj.Placement.Base.z * scale}) return kwargs @@ -1759,15 +1804,15 @@ def exportIfcAttributes(obj, kwargs, scale=0.001): if isinstance(value, FreeCAD.Units.Quantity): value = float(value) if "Elevation" in property: - value = value*scale # some properties must be changed to meters + value = value * scale # some properties must be changed to meters if (ifctype == "IfcFurnishingElement") and (property == "PredefinedType"): - pass # IFC2x3 Furniture objects get converted to IfcFurnishingElement and have no PredefinedType anymore + pass # IFC2x3 Furniture objects get converted to IfcFurnishingElement and have no PredefinedType anymore else: kwargs.update({property: value}) return kwargs -def buildAddress(obj,ifcfile): +def buildAddress(obj, ifcfile): a = obj.Address or None p = obj.PostalCode or None @@ -1775,7 +1820,9 @@ def buildAddress(obj,ifcfile): r = obj.Region or None c = obj.Country or None if a or p or t or r or c: - addr = ifcfile.createIfcPostalAddress("SITE",'Site Address','',None,[a],None,t,r,p,c) + addr = ifcfile.createIfcPostalAddress( + "SITE", "Site Address", "", None, [a], None, t, r, p, c + ) else: addr = None return addr @@ -1788,7 +1835,7 @@ def createCurve(ifcfile, wire, scaling=1.0): if wire.ShapeType != "Wire": return createCurveWithArcs(ifcfile, wire, scaling) for e in wire.Edges: - if isinstance(e.Curve,Part.Circle): + if isinstance(e.Curve, Part.Circle): return createCurveWithArcs(ifcfile, wire, scaling) verts = [v.Point for v in wire.Vertexes] if scaling != 1: @@ -1799,7 +1846,7 @@ def createCurve(ifcfile, wire, scaling=1.0): return idc -def createCurveWithArcs(ifcfile,wire,scaling=1.0): +def createCurveWithArcs(ifcfile, wire, scaling=1.0): "creates an IfcCompositeCurve from a shape" segments = [] @@ -1810,14 +1857,14 @@ def createCurveWithArcs(ifcfile,wire,scaling=1.0): else: edges = Part.__sortEdges__(wire.Edges) for e in edges: - if scaling not in (0,1): + if scaling not in (0, 1): e.scale(scaling) - if isinstance(e.Curve,Part.Circle): + if isinstance(e.Curve, Part.Circle): xaxis = e.Curve.XAxis zaxis = e.Curve.Axis follow = True if last: - if not DraftVecUtils.equals(last,e.Vertexes[0].Point): + if not DraftVecUtils.equals(last, e.Vertexes[0].Point): follow = False last = e.Vertexes[0].Point prev = e.Vertexes[-1].Point @@ -1827,52 +1874,54 @@ def createCurveWithArcs(ifcfile,wire,scaling=1.0): else: last = e.Vertexes[-1].Point prev = e.Vertexes[0].Point - p1 = math.degrees(-DraftVecUtils.angle(prev.sub(e.Curve.Center),xaxis,zaxis)) - p2 = math.degrees(-DraftVecUtils.angle(last.sub(e.Curve.Center),xaxis,zaxis)) - da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),prev.sub(e.Curve.Center)) - #print("curve params:",p1,",",p2,"da=",da) + p1 = math.degrees(-DraftVecUtils.angle(prev.sub(e.Curve.Center), xaxis, zaxis)) + p2 = math.degrees(-DraftVecUtils.angle(last.sub(e.Curve.Center), xaxis, zaxis)) + da = DraftVecUtils.angle( + e.valueAt(e.FirstParameter + 0.1).sub(e.Curve.Center), prev.sub(e.Curve.Center) + ) + # print("curve params:",p1,",",p2,"da=",da) if p1 < 0: p1 = 360 + p1 if p2 < 0: p2 = 360 + p2 if da > 0: - #follow = not(follow) # now we always draw segments in the correct order, so follow is always true + # follow = not(follow) # now we always draw segments in the correct order, so follow is always true pass - #print(" circle from",prev,"to",last,"a1=",p1,"a2=",p2) - ovc = ifcbin.createIfcCartesianPoint(tuple(e.Curve.Center)) - zvc = ifcbin.createIfcDirection(tuple(zaxis)) - xvc = ifcbin.createIfcDirection(tuple(xaxis)) - plc = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc) - cir = ifcfile.createIfcCircle(plc,e.Curve.Radius) - curve = ifcfile.createIfcTrimmedCurve( + # print(" circle from",prev,"to",last,"a1=",p1,"a2=",p2) + ovc = ifcbin.createIfcCartesianPoint(tuple(e.Curve.Center)) + zvc = ifcbin.createIfcDirection(tuple(zaxis)) + xvc = ifcbin.createIfcDirection(tuple(xaxis)) + plc = ifcbin.createIfcAxis2Placement3D(ovc, zvc, xvc) + cir = ifcfile.createIfcCircle(plc, e.Curve.Radius) + curve = ifcfile.createIfcTrimmedCurve( cir, [ifcfile.createIfcParameterValue(p1)], [ifcfile.createIfcParameterValue(p2)], follow, - "PARAMETER" + "PARAMETER", ) else: verts = [vertex.Point for vertex in e.Vertexes] if last: - if not DraftVecUtils.equals(last,verts[0]): + if not DraftVecUtils.equals(last, verts[0]): verts.reverse() last = e.Vertexes[0].Point else: last = e.Vertexes[-1].Point else: last = e.Vertexes[-1].Point - #print(" polyline:",verts) - pts = [ifcbin.createIfcCartesianPoint(tuple(v)) for v in verts] - curve = ifcbin.createIfcPolyline(pts) - segment = ifcfile.createIfcCompositeCurveSegment("CONTINUOUS",True,curve) + # print(" polyline:",verts) + pts = [ifcbin.createIfcCartesianPoint(tuple(v)) for v in verts] + curve = ifcbin.createIfcPolyline(pts) + segment = ifcfile.createIfcCompositeCurveSegment("CONTINUOUS", True, curve) segments.append(segment) if segments: - pol = ifcfile.createIfcCompositeCurve(segments,False) + pol = ifcfile.createIfcCompositeCurve(segments, False) return pol def getEdgesAngle(edge1, edge2): - """ getEdgesAngle(edge1, edge2): returns a angle between two edges.""" + """getEdgesAngle(edge1, edge2): returns a angle between two edges.""" vec1 = vec(edge1) vec2 = vec(edge2) @@ -1882,9 +1931,9 @@ def getEdgesAngle(edge1, edge2): def checkRectangle(edges): - """ checkRectangle(edges=[]): This function checks whether the given form is a rectangle - or not. It will return True when edges form a rectangular shape or return False - when edges do not form a rectangular shape.""" + """checkRectangle(edges=[]): This function checks whether the given form is a rectangle + or not. It will return True when edges form a rectangular shape or return False + when edges do not form a rectangular shape.""" if params.get_param_arch("DisableIfcRectangleProfileDef"): return False @@ -1893,29 +1942,32 @@ def checkRectangle(edges): angles = [ round(getEdgesAngle(edges[0], edges[1])), round(getEdgesAngle(edges[0], edges[2])), - round(getEdgesAngle(edges[0], edges[3])) + round(getEdgesAngle(edges[0], edges[3])), ] if angles.count(90) == 2 and (angles.count(180) == 1 or angles.count(0) == 1): return True return False -def getProfile(ifcfile,p): +def getProfile(ifcfile, p): """returns an IFC profile definition from a shape""" import Part import DraftGeomUtils + profile = None if len(p.Edges) == 1: - pxvc = ifcbin.createIfcDirection((1.0,0.0)) - povc = ifcbin.createIfcCartesianPoint((0.0,0.0)) - pt = ifcbin.createIfcAxis2Placement2D(povc,pxvc) - if isinstance(p.Edges[0].Curve,Part.Circle): + pxvc = ifcbin.createIfcDirection((1.0, 0.0)) + povc = ifcbin.createIfcCartesianPoint((0.0, 0.0)) + pt = ifcbin.createIfcAxis2Placement2D(povc, pxvc) + if isinstance(p.Edges[0].Curve, Part.Circle): # extruded circle - profile = ifcbin.createIfcCircleProfileDef("AREA",None,pt,p.Edges[0].Curve.Radius) - elif isinstance(p.Edges[0].Curve,Part.Ellipse): + profile = ifcbin.createIfcCircleProfileDef("AREA", None, pt, p.Edges[0].Curve.Radius) + elif isinstance(p.Edges[0].Curve, Part.Ellipse): # extruded ellipse - profile = ifcbin.createIfcEllipseProfileDef("AREA",None,pt,p.Edges[0].Curve.MajorRadius,p.Edges[0].Curve.MinorRadius) + profile = ifcbin.createIfcEllipseProfileDef( + "AREA", None, pt, p.Edges[0].Curve.MajorRadius, p.Edges[0].Curve.MinorRadius + ) elif checkRectangle(p.Edges): # arbitrarily use the first edge as the rectangle orientation d = vec(p.Edges[0]) @@ -1925,50 +1977,59 @@ def getProfile(ifcfile,p): # povc = ifcbin.createIfcCartesianPoint((0.0,0.0)) # the above statement appears wrong, so the line below has been uncommented for now # TODO we must sort this out at some point... For now the line below seems to work - if getattr(p,"CenterOfMass",None): + if getattr(p, "CenterOfMass", None): povc = ifcbin.createIfcCartesianPoint(tuple(p.CenterOfMass[:2])) else: - povc = ifcbin.createIfcCartesianPoint((0.0,0.0)) - pt = ifcbin.createIfcAxis2Placement2D(povc,pxvc) - #semiPerimeter = p.Length/2 - #diff = math.sqrt(semiPerimeter**2 - 4*p.Area) - #b = max(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) - #h = min(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) + povc = ifcbin.createIfcCartesianPoint((0.0, 0.0)) + pt = ifcbin.createIfcAxis2Placement2D(povc, pxvc) + # semiPerimeter = p.Length/2 + # diff = math.sqrt(semiPerimeter**2 - 4*p.Area) + # b = max(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) + # h = min(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) b = p.Edges[0].Length h = p.Edges[1].Length if h == b: # are these edges unordered? To be on the safe side, check the next one h = p.Edges[2].Length - profile = ifcbin.createIfcRectangleProfileDef("AREA",'rectangular',pt,b,h) + profile = ifcbin.createIfcRectangleProfileDef("AREA", "rectangular", pt, b, h) elif (len(p.Faces) == 1) and (len(p.Wires) > 1): # face with holes f = p.Faces[0] if DraftGeomUtils.hasCurves(f.OuterWire): - outerwire = createCurve(ifcfile,f.OuterWire) + outerwire = createCurve(ifcfile, f.OuterWire) else: w = Part.Wire(Part.__sortEdges__(f.OuterWire.Edges)) - pts = [ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) for v in w.Vertexes+[w.Vertexes[0]]] + pts = [ + ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) + for v in w.Vertexes + [w.Vertexes[0]] + ] outerwire = ifcbin.createIfcPolyline(pts) innerwires = [] for w in f.Wires: if w.hashCode() != f.OuterWire.hashCode(): if DraftGeomUtils.hasCurves(w): - innerwires.append(createCurve(ifcfile,w)) + innerwires.append(createCurve(ifcfile, w)) else: w = Part.Wire(Part.__sortEdges__(w.Edges)) - pts = [ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) for v in w.Vertexes+[w.Vertexes[0]]] + pts = [ + ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) + for v in w.Vertexes + [w.Vertexes[0]] + ] innerwires.append(ifcbin.createIfcPolyline(pts)) - profile = ifcfile.createIfcArbitraryProfileDefWithVoids("AREA",None,outerwire,innerwires) + profile = ifcfile.createIfcArbitraryProfileDefWithVoids("AREA", None, outerwire, innerwires) else: if DraftGeomUtils.hasCurves(p): # extruded composite curve - pol = createCurve(ifcfile,p) + pol = createCurve(ifcfile, p) else: # extruded polyline w = Part.Wire(Part.__sortEdges__(p.Wires[0].Edges)) - pts = [ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) for v in w.Vertexes+[w.Vertexes[0]]] + pts = [ + ifcbin.createIfcCartesianPoint(tuple(v.Point)[:2]) + for v in w.Vertexes + [w.Vertexes[0]] + ] pol = ifcbin.createIfcPolyline(pts) - profile = ifcfile.createIfcArbitraryClosedProfileDef("AREA",None,pol) + profile = ifcfile.createIfcArbitraryClosedProfileDef("AREA", None, pol) return profile @@ -1982,7 +2043,7 @@ def getRepresentation( colors=None, preferences=None, forceclone=False, - skipshape=False + skipshape=False, ): """returns an IfcShapeRepresentation object or None. forceclone can be False (does nothing), "store" or True (stores the object as clone base) or a Vector (creates a clone)""" @@ -1990,6 +2051,7 @@ def getRepresentation( import Part import DraftGeomUtils import DraftVecUtils + shapes = [] placement = None productdef = None @@ -1998,7 +2060,7 @@ def getRepresentation( subplacement = None # enable forcebrep for non-solids - if hasattr(obj,"Shape"): + if hasattr(obj, "Shape"): if obj.Shape: if not obj.Shape.Solids: forcebrep = True @@ -2009,21 +2071,31 @@ def getRepresentation( if forceclone: if obj.Name not in clones: clones[obj.Name] = [] - for k,v in clones.items(): + for k, v in clones.items(): if (obj.Name == k) or (obj.Name in v): if k in sharedobjects: # base shape already exists repmap = sharedobjects[k] pla = obj.getGlobalPlacement() pos = FreeCAD.Vector(pla.Base) - if isinstance(forceclone,FreeCAD.Vector): + if isinstance(forceclone, FreeCAD.Vector): pos += forceclone - axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0)))) - axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0)))) - axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - origin = ifcbin.createIfcCartesianPoint(tuple(pos.multiply(preferences['SCALE_FACTOR']))) - transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3) - mapitem = ifcfile.createIfcMappedItem(repmap,transf) + axis1 = ifcbin.createIfcDirection( + tuple(pla.Rotation.multVec(FreeCAD.Vector(1, 0, 0))) + ) + axis2 = ifcbin.createIfcDirection( + tuple(pla.Rotation.multVec(FreeCAD.Vector(0, 1, 0))) + ) + axis3 = ifcbin.createIfcDirection( + tuple(pla.Rotation.multVec(FreeCAD.Vector(0, 0, 1))) + ) + origin = ifcbin.createIfcCartesianPoint( + tuple(pos.multiply(preferences["SCALE_FACTOR"])) + ) + transf = ifcbin.createIfcCartesianTransformationOperator3D( + axis1, axis2, origin, 1.0, axis3 + ) + mapitem = ifcfile.createIfcMappedItem(repmap, transf) shapes = [mapitem] solidType = "MappedRepresentation" shapetype = "clone" @@ -2032,40 +2104,45 @@ def getRepresentation( tostore = k # unhandled case: object is duplicated because of Axis - if obj.isDerivedFrom("Part::Feature") and (len(obj.Shape.Solids) > 1) and hasattr(obj,"Axis") and obj.Axis: + if ( + obj.isDerivedFrom("Part::Feature") + and (len(obj.Shape.Solids) > 1) + and hasattr(obj, "Axis") + and obj.Axis + ): forcebrep = True if (not shapes) and (not forcebrep) and (not skipshape): profile = None ev = FreeCAD.Vector() - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getRebarData"): + if hasattr(obj, "Proxy"): + if hasattr(obj.Proxy, "getRebarData"): # export rebars as IfcSweptDiskSolid rdata = obj.Proxy.getRebarData(obj) if rdata: # convert to meters - r = rdata[1] * preferences['SCALE_FACTOR'] + r = rdata[1] * preferences["SCALE_FACTOR"] for w in rdata[0]: w.Placement = w.Placement.multiply(obj.getGlobalPlacement()) - w.scale(preferences['SCALE_FACTOR']) - cur = createCurve(ifcfile,w) - shape = ifcfile.createIfcSweptDiskSolid(cur,r) + w.scale(preferences["SCALE_FACTOR"]) + cur = createCurve(ifcfile, w) + shape = ifcfile.createIfcSweptDiskSolid(cur, r) shapes.append(shape) solidType = "SweptSolid" shapetype = "extrusion" - if (not shapes) and hasattr(obj.Proxy,"getExtrusionData"): + if (not shapes) and hasattr(obj.Proxy, "getExtrusionData"): extdata = obj.Proxy.getExtrusionData(obj) if extdata: - #print(extdata) + # print(extdata) # convert to meters p = extdata[0] - if not isinstance(p,list): + if not isinstance(p, list): p = [p] ev = extdata[1] - if not isinstance(ev,list): + if not isinstance(ev, list): ev = [ev] pl = extdata[2] - if not isinstance(pl,list): + if not isinstance(pl, list): pl = [pl] simpleExtrusion = True for evi in ev: @@ -2074,17 +2151,17 @@ def getRepresentation( if simpleExtrusion: for i in range(len(p)): pi = p[i] - pi.scale(preferences['SCALE_FACTOR']) + pi.scale(preferences["SCALE_FACTOR"]) if i < len(ev): evi = FreeCAD.Vector(ev[i]) else: evi = FreeCAD.Vector(ev[-1]) - evi.multiply(preferences['SCALE_FACTOR']) + evi.multiply(preferences["SCALE_FACTOR"]) if i < len(pl): pli = pl[i].copy() else: pli = pl[-1].copy() - pli.Base = pli.Base.multiply(preferences['SCALE_FACTOR']) + pli.Base = pli.Base.multiply(preferences["SCALE_FACTOR"]) pstr = str([v.Point for v in p[i].Vertexes]) if pstr in profiledefs: profile = profiledefs[pstr] @@ -2094,57 +2171,66 @@ def getRepresentation( # Fix bug in Forum Discussion # https://forum.freecad.org/viewtopic.php?p=771954#p771954 if not isinstance(pi, Part.Compound): - profile = getProfile(ifcfile,pi) + profile = getProfile(ifcfile, pi) if profile: profiledefs[pstr] = profile - profiles=[profile] + profiles = [profile] else: # i.e. Part.Compound - profiles=[] + profiles = [] for pif in pi.Faces: - profile = getProfile(ifcfile,pif) + profile = getProfile(ifcfile, pif) if profile: profiledefs[pstr] = profile profiles.append(profile) - if profiles and not(DraftVecUtils.isNull(evi)): - for profile in profiles: - #ev = pl.Rotation.inverted().multVec(evi) - #print("evi:",evi) - if not tostore: - # add the object placement to the profile placement. Otherwise it'll be done later at map insert - pl2 = obj.getGlobalPlacement() - pl2.Base = pl2.Base.multiply(preferences['SCALE_FACTOR']) - pli = pl2.multiply(pli) - xvc = ifcbin.createIfcDirection(tuple(pli.Rotation.multVec(FreeCAD.Vector(1,0,0)))) - zvc = ifcbin.createIfcDirection(tuple(pli.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - ovc = ifcbin.createIfcCartesianPoint(tuple(pli.Base)) - lpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc) - edir = ifcbin.createIfcDirection(tuple(FreeCAD.Vector(evi).normalize())) - shape = ifcfile.createIfcExtrudedAreaSolid(profile,lpl,edir,evi.Length) - shapes.append(shape) - solidType = "SweptSolid" - shapetype = "extrusion" + if profiles and not (DraftVecUtils.isNull(evi)): + for profile in profiles: + # ev = pl.Rotation.inverted().multVec(evi) + # print("evi:",evi) + if not tostore: + # add the object placement to the profile placement. Otherwise it'll be done later at map insert + pl2 = obj.getGlobalPlacement() + pl2.Base = pl2.Base.multiply(preferences["SCALE_FACTOR"]) + pli = pl2.multiply(pli) + xvc = ifcbin.createIfcDirection( + tuple(pli.Rotation.multVec(FreeCAD.Vector(1, 0, 0))) + ) + zvc = ifcbin.createIfcDirection( + tuple(pli.Rotation.multVec(FreeCAD.Vector(0, 0, 1))) + ) + ovc = ifcbin.createIfcCartesianPoint(tuple(pli.Base)) + lpl = ifcbin.createIfcAxis2Placement3D(ovc, zvc, xvc) + edir = ifcbin.createIfcDirection( + tuple(FreeCAD.Vector(evi).normalize()) + ) + shape = ifcfile.createIfcExtrudedAreaSolid( + profile, lpl, edir, evi.Length + ) + shapes.append(shape) + solidType = "SweptSolid" + shapetype = "extrusion" if (not shapes) and obj.isDerivedFrom("Part::Extrusion"): import ArchComponent + pstr = str([v.Point for v in obj.Base.Shape.Vertexes]) - profile,pl = ArchComponent.Component.rebase(obj,obj.Base.Shape) - profile.scale(preferences['SCALE_FACTOR']) - pl.Base = pl.Base.multiply(preferences['SCALE_FACTOR']) - profile = getProfile(ifcfile,profile) + profile, pl = ArchComponent.Component.rebase(obj, obj.Base.Shape) + profile.scale(preferences["SCALE_FACTOR"]) + pl.Base = pl.Base.multiply(preferences["SCALE_FACTOR"]) + profile = getProfile(ifcfile, profile) if profile: profiledefs[pstr] = profile ev = FreeCAD.Vector(obj.Dir) l = obj.LengthFwd.Value if l: - ev = ev.normalize() # new since 0.20 - obj.Dir length is ignored + ev = ev.normalize() # new since 0.20 - obj.Dir length is ignored ev.multiply(l) - ev.multiply(preferences['SCALE_FACTOR']) + ev.multiply(preferences["SCALE_FACTOR"]) ev = pl.Rotation.inverted().multVec(ev) - xvc = ifcbin.createIfcDirection(tuple(pl.Rotation.multVec(FreeCAD.Vector(1,0,0)))) - zvc = ifcbin.createIfcDirection(tuple(pl.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - ovc = ifcbin.createIfcCartesianPoint(tuple(pl.Base)) - lpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc) - edir = ifcbin.createIfcDirection(tuple(FreeCAD.Vector(ev).normalize())) - shape = ifcfile.createIfcExtrudedAreaSolid(profile,lpl,edir,ev.Length) + xvc = ifcbin.createIfcDirection(tuple(pl.Rotation.multVec(FreeCAD.Vector(1, 0, 0)))) + zvc = ifcbin.createIfcDirection(tuple(pl.Rotation.multVec(FreeCAD.Vector(0, 0, 1)))) + ovc = ifcbin.createIfcCartesianPoint(tuple(pl.Base)) + lpl = ifcbin.createIfcAxis2Placement3D(ovc, zvc, xvc) + edir = ifcbin.createIfcDirection(tuple(FreeCAD.Vector(ev).normalize())) + shape = ifcfile.createIfcExtrudedAreaSolid(profile, lpl, edir, ev.Length) shapes.append(shape) solidType = "SweptSolid" shapetype = "extrusion" @@ -2153,11 +2239,13 @@ def getRepresentation( # check if we keep a null shape (additions-only object) - if (hasattr(obj,"Base") and hasattr(obj,"Width") and hasattr(obj,"Height")) \ - and (not obj.Base) \ - and obj.Additions \ - and (not obj.Width.Value) \ - and (not obj.Height.Value): + if ( + (hasattr(obj, "Base") and hasattr(obj, "Width") and hasattr(obj, "Height")) + and (not obj.Base) + and obj.Additions + and (not obj.Width.Value) + and (not obj.Height.Value) + ): shapes = None else: @@ -2167,13 +2255,13 @@ def getRepresentation( fcshape = None solidType = "Brep" if subtraction: - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getSubVolume"): + if hasattr(obj, "Proxy"): + if hasattr(obj.Proxy, "getSubVolume"): fcshape = obj.Proxy.getSubVolume(obj) if not fcshape: if obj.isDerivedFrom("Part::Feature"): - #if hasattr(obj,"Base") and hasattr(obj,"Additions")and hasattr(obj,"Subtractions"): - if False: # above is buggy. No way to duplicate shapes that way? + # if hasattr(obj,"Base") and hasattr(obj,"Additions")and hasattr(obj,"Subtractions"): + if False: # above is buggy. No way to duplicate shapes that way? if obj.Base and not obj.Additions and not obj.Subtractions: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: @@ -2195,12 +2283,17 @@ def getRepresentation( # new ifcopenshell serializer from ifcopenshell import geom + serialized = False - if hasattr(geom,"serialise") and obj.isDerivedFrom("Part::Feature") and preferences['SERIALIZE']: + if ( + hasattr(geom, "serialise") + and obj.isDerivedFrom("Part::Feature") + and preferences["SERIALIZE"] + ): if obj.Shape.Faces: sh = obj.Shape.copy() sh.Placement = obj.getGlobalPlacement() - sh.scale(preferences['SCALE_FACTOR']) # to meters + sh.scale(preferences["SCALE_FACTOR"]) # to meters # clean shape and moves placement away from the outer element level # https://forum.freecad.org/viewtopic.php?p=675760#p675760 brep_data = sh.removeSplitter().exportBrepToString() @@ -2209,7 +2302,7 @@ def getRepresentation( except TypeError: # IfcOpenShell v0.6.0 # Serialization.cpp:IfcUtil::IfcBaseClass* IfcGeom::serialise(const std::string& schema_name, const TopoDS_Shape& shape, bool advanced) - p = geom.serialise(preferences['SCHEMA'], brep_data) + p = geom.serialise(preferences["SCHEMA"], brep_data) if p: productdef = ifcfile.add(p) for rep in productdef.Representations: @@ -2219,11 +2312,12 @@ def getRepresentation( shapes = None serialized = True else: - if preferences['DEBUG']: + if preferences["DEBUG"]: print( "Warning! IfcOS serializer did not return a ifc-geometry for object {}. " - "The shape will be exported with triangulation." - .format(obj.Label) + "The shape will be exported with triangulation.".format( + obj.Label + ) ) if not serialized: @@ -2240,12 +2334,15 @@ def getRepresentation( dataset = fcshape.Solids elif fcshape.Shells: dataset = fcshape.Shells - #if preferences['DEBUG']: print("Warning! object contains no solids") + # if preferences['DEBUG']: print("Warning! object contains no solids") else: - if preferences['DEBUG']: print("Warning! object "+obj.Label+" contains no solids or shells") + if preferences["DEBUG"]: + print( + "Warning! object " + obj.Label + " contains no solids or shells" + ) dataset = [fcshape] for fcsolid in dataset: - fcsolid.scale(preferences['SCALE_FACTOR']) # to meters + fcsolid.scale(preferences["SCALE_FACTOR"]) # to meters faces = [] curves = False shapetype = "brep" @@ -2253,8 +2350,15 @@ def getRepresentation( for e in fcface.Edges: if DraftGeomUtils.geomType(e) != "Line": from FreeCAD import Base + try: - if e.curvatureAt(e.FirstParameter+(e.LastParameter-e.FirstParameter)/2) > 0.0001: + if ( + e.curvatureAt( + e.FirstParameter + + (e.LastParameter - e.FirstParameter) / 2 + ) + > 0.0001 + ): curves = True break except Part.OCCError: @@ -2265,7 +2369,7 @@ def getRepresentation( joinfacets = params.get_param_arch("ifcJoinCoplanarFacets") usedae = params.get_param_arch("ifcUseDaeOptions") if joinfacets: - result = Arch.removeCurves(fcsolid,dae=usedae) + result = Arch.removeCurves(fcsolid, dae=usedae) if result: fcsolid = result else: @@ -2275,37 +2379,46 @@ def getRepresentation( shapetype = "triangulated" if usedae: from importers import importDAE + tris = importDAE.triangulate(fcsolid) else: tris = fcsolid.tessellate(tessellation) for tri in tris[1]: - pts = [ifcbin.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri] - loop = ifcbin.createIfcPolyLoop(pts) - bound = ifcfile.createIfcFaceOuterBound(loop,True) - face = ifcfile.createIfcFace([bound]) + pts = [ + ifcbin.createIfcCartesianPoint(tuple(tris[0][i])) + for i in tri + ] + loop = ifcbin.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceOuterBound(loop, True) + face = ifcfile.createIfcFace([bound]) faces.append(face) - fcsolid = Part.Shape() # empty shape so below code is not executed + fcsolid = ( + Part.Shape() + ) # empty shape so below code is not executed for fcface in fcsolid.Faces: loops = [] verts = [v.Point for v in fcface.OuterWire.OrderedVertexes] c = fcface.CenterOfMass if len(verts) < 1: - print("Warning: OuterWire returned no ordered Vertexes in ", obj.Label) + print( + "Warning: OuterWire returned no ordered Vertexes in ", + obj.Label, + ) # Part.show(fcface) # Part.show(fcsolid) continue v1 = verts[0].sub(c) v2 = verts[1].sub(c) try: - n = fcface.normalAt(0,0) + n = fcface.normalAt(0, 0) except Part.OCCError: - continue # this is a very wrong face, it probably shouldn't be here... - if DraftVecUtils.angle(v2,v1,n) >= 0: - verts.reverse() # inverting verts order if the direction is couterclockwise - pts = [ifcbin.createIfcCartesianPoint(tuple(v)) for v in verts] - loop = ifcbin.createIfcPolyLoop(pts) - bound = ifcfile.createIfcFaceOuterBound(loop,True) + continue # this is a very wrong face, it probably shouldn't be here... + if DraftVecUtils.angle(v2, v1, n) >= 0: + verts.reverse() # inverting verts order if the direction is couterclockwise + pts = [ifcbin.createIfcCartesianPoint(tuple(v)) for v in verts] + loop = ifcbin.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceOuterBound(loop, True) loops.append(bound) for wire in fcface.Wires: if wire.hashCode() != fcface.OuterWire.hashCode(): @@ -2313,15 +2426,21 @@ def getRepresentation( if len(verts) > 1: v1 = verts[0].sub(c) v2 = verts[1].sub(c) - if DraftVecUtils.angle(v2,v1,DraftVecUtils.neg(n)) >= 0: + if ( + DraftVecUtils.angle(v2, v1, DraftVecUtils.neg(n)) + >= 0 + ): verts.reverse() - pts = [ifcbin.createIfcCartesianPoint(tuple(v)) for v in verts] - loop = ifcbin.createIfcPolyLoop(pts) - bound = ifcfile.createIfcFaceBound(loop,True) + pts = [ + ifcbin.createIfcCartesianPoint(tuple(v)) + for v in verts + ] + loop = ifcbin.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceBound(loop, True) loops.append(bound) else: print("Warning: wire with one/no vertex in ", obj.Label) - face = ifcfile.createIfcFace(loops) + face = ifcfile.createIfcFace(loops) faces.append(face) if faces: @@ -2333,20 +2452,24 @@ def getRepresentation( if shapes: - colorshapes = shapes # to keep track of individual shapes for coloring below + colorshapes = shapes # to keep track of individual shapes for coloring below if tostore: - subrep = ifcfile.createIfcShapeRepresentation(context,'Body',solidType,shapes) + subrep = ifcfile.createIfcShapeRepresentation(context, "Body", solidType, shapes) gpl = ifcbin.createIfcAxis2Placement3D() - repmap = ifcfile.createIfcRepresentationMap(gpl,subrep) + repmap = ifcfile.createIfcRepresentationMap(gpl, subrep) pla = obj.getGlobalPlacement() - if isinstance(forceclone,FreeCAD.Vector): + if isinstance(forceclone, FreeCAD.Vector): pla.Base += forceclone - axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0)))) - axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0)))) - origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(preferences['SCALE_FACTOR']))) - axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3) - mapitem = ifcfile.createIfcMappedItem(repmap,transf) + axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1, 0, 0)))) + axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0, 1, 0)))) + origin = ifcbin.createIfcCartesianPoint( + tuple(FreeCAD.Vector(pla.Base).multiply(preferences["SCALE_FACTOR"])) + ) + axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0, 0, 1)))) + transf = ifcbin.createIfcCartesianTransformationOperator3D( + axis1, axis2, origin, 1.0, axis3 + ) + mapitem = ifcfile.createIfcMappedItem(repmap, transf) shapes = [mapitem] sharedobjects[tostore] = repmap solidType = "MappedRepresentation" @@ -2361,28 +2484,30 @@ def getRepresentation( if obj.Name in colors: color = colors[obj.Name] shapecolor = color - if isinstance(color[0],tuple): + if isinstance(color[0], tuple): # this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors diffusecolor = color shapecolor = color[0] - elif FreeCAD.GuiUp and (not subtraction) and hasattr(obj.ViewObject,"ShapeColor"): + elif FreeCAD.GuiUp and (not subtraction) and hasattr(obj.ViewObject, "ShapeColor"): # every object gets a surface style. If the obj has a material, the surfstyle # is named after it. Revit will treat surfacestyles as materials (and discard # actual ifcmaterial) shapecolor = obj.ViewObject.ShapeColor[:3] - transparency = obj.ViewObject.Transparency/100.0 + transparency = obj.ViewObject.Transparency / 100.0 if transparency == 1: # fix buggy fully transparent materials # TODO there is some problem somewhere in ShapeAppearance that needs solving. - transparency = 0. - if hasattr(obj.ViewObject,"DiffuseColor"): + transparency = 0.0 + if hasattr(obj.ViewObject, "DiffuseColor"): diffusecolor = obj.ViewObject.DiffuseColor - if shapecolor and (shapetype != "clone"): # cloned objects are already colored + if shapecolor and (shapetype != "clone"): # cloned objects are already colored key = None - rgbt = [shapecolor+(transparency,)] * len(shapes) - if diffusecolor \ - and (len(diffusecolor) == len(obj.Shape.Faces)) \ - and (len(obj.Shape.Solids) == len(colorshapes)): + rgbt = [shapecolor + (transparency,)] * len(shapes) + if ( + diffusecolor + and (len(diffusecolor) == len(obj.Shape.Faces)) + and (len(obj.Shape.Solids) == len(colorshapes)) + ): i = 0 rgbt = [] for sol in obj.Shape.Solids: @@ -2391,51 +2516,60 @@ def getRepresentation( else: rgbt.append(diffusecolor[0]) i += len(sol.Faces) - for i,shape in enumerate(colorshapes): + for i, shape in enumerate(colorshapes): if i < len(rgbt): key = rgbt[i] else: key = rgbt[0] - #if hasattr(obj,"Material"): + # if hasattr(obj,"Material"): # if obj.Material: # key = obj.Material.Name #TODO handle multimaterials if key in surfstyles: psa = surfstyles[key] else: m = None - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: m = obj.Material.Label - rgbt[i] = (rgbt[i][0], rgbt[i][1], rgbt[i][2], obj.Material.Transparency/100.0) - psa = ifcbin.createIfcPresentationStyleAssignment(m,rgbt[i][0],rgbt[i][1],rgbt[i][2],rgbt[i][3]) + rgbt[i] = ( + rgbt[i][0], + rgbt[i][1], + rgbt[i][2], + obj.Material.Transparency / 100.0, + ) + psa = ifcbin.createIfcPresentationStyleAssignment( + m, rgbt[i][0], rgbt[i][1], rgbt[i][2], rgbt[i][3] + ) surfstyles[key] = psa - isi = ifcfile.createIfcStyledItem(shape,[psa],None) + isi = ifcfile.createIfcStyledItem(shape, [psa], None) placement = ifcbin.createIfcLocalPlacement() - representation = [ifcfile.createIfcShapeRepresentation(context,'Body',solidType,shapes)] + representation = [ifcfile.createIfcShapeRepresentation(context, "Body", solidType, shapes)] # additional representations? - if Draft.getType(obj) in ["Wall","Structure"]: - addrepr = createAxis(ifcfile,obj,preferences, forceclone) + if Draft.getType(obj) in ["Wall", "Structure"]: + addrepr = createAxis(ifcfile, obj, preferences, forceclone) if addrepr: representation = representation + [addrepr] - productdef = ifcfile.createIfcProductDefinitionShape(None,None,representation) + productdef = ifcfile.createIfcProductDefinitionShape(None, None, representation) - return productdef,placement,shapetype + return productdef, placement, shapetype -def getBrepFlag(obj,preferences): +def getBrepFlag(obj, preferences): """returns True if the object must be exported as BREP""" brepflag = False - if preferences['FORCE_BREP']: + if preferences["FORCE_BREP"]: return True - if hasattr(obj,"IfcData"): + if hasattr(obj, "IfcData"): if "FlagForceBrep" in obj.IfcData: if obj.IfcData["FlagForceBrep"] == "True": brepflag = True return brepflag -def createProduct(ifcfile,obj,ifctype,uid,history,name,description,placement,representation,preferences): +def createProduct( + ifcfile, obj, ifctype, uid, history, name, description, placement, representation, preferences +): """creates a product in the given IFC file""" kwargs = { @@ -2444,20 +2578,22 @@ def createProduct(ifcfile,obj,ifctype,uid,history,name,description,placement,rep "Name": name, "Description": description, "ObjectPlacement": placement, - "Representation": representation + "Representation": representation, } if ifctype == "IfcSite": - kwargs.update({ - "RefLatitude":dd2dms(obj.Latitude), - "RefLongitude":dd2dms(obj.Longitude), - "RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'], - "SiteAddress":buildAddress(obj,ifcfile), - "CompositionType": "ELEMENT" - }) - if preferences['SCHEMA'] == "IFC2X3": - kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR']) + kwargs.update( + { + "RefLatitude": dd2dms(obj.Latitude), + "RefLongitude": dd2dms(obj.Longitude), + "RefElevation": obj.Elevation.Value * preferences["SCALE_FACTOR"], + "SiteAddress": buildAddress(obj, ifcfile), + "CompositionType": "ELEMENT", + } + ) + if preferences["SCHEMA"] == "IFC2X3": + kwargs = exportIFC2X3Attributes(obj, kwargs, preferences["SCALE_FACTOR"]) else: - kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR']) + kwargs = exportIfcAttributes(obj, kwargs, preferences["SCALE_FACTOR"]) # in some cases object have wrong ifctypes, thus set it # https://forum.freecad.org/viewtopic.php?f=39&t=50085 if ifctype not in ArchIFCSchema.IfcProducts: @@ -2465,16 +2601,16 @@ def createProduct(ifcfile,obj,ifctype,uid,history,name,description,placement,rep ifctype = "IfcBuildingElementProxy" # print("createProduct: {}".format(ifctype)) # print(kwargs) - product = getattr(ifcfile,"create"+ifctype)(**kwargs) + product = getattr(ifcfile, "create" + ifctype)(**kwargs) return product -def getUID(obj,preferences): +def getUID(obj, preferences): """gets or creates an UUID for an object""" global uids uid = None - if hasattr(obj,"IfcData"): + if hasattr(obj, "IfcData"): if "IfcUID" in obj.IfcData: uid = str(obj.IfcData["IfcUID"]) if uid in uids: @@ -2495,14 +2631,14 @@ def getUID(obj,preferences): return uid -def getText(field,obj): +def getText(field, obj): """Returns the value of a text property of an object""" result = "" if field == "Name": field = "Label" - if hasattr(obj,field): - result = getattr(obj,field) + if hasattr(obj, field): + result = getattr(obj, field) return result @@ -2516,36 +2652,39 @@ def getAxisContext(ifcfile): for ctx in subcontexts: if ctx.ContextIdentifier == "Axis": return ctx - ctx = contexts[0] # arbitrarily take the first one... - nctx = ifcfile.createIfcGeometricRepresentationSubContext('Axis','Model',None,None,None,None,ctx,None,"MODEL_VIEW",None) + ctx = contexts[0] # arbitrarily take the first one... + nctx = ifcfile.createIfcGeometricRepresentationSubContext( + "Axis", "Model", None, None, None, None, ctx, None, "MODEL_VIEW", None + ) return nctx -def createAxis(ifcfile,obj,preferences, delta=None): +def createAxis(ifcfile, obj, preferences, delta=None): """Creates an axis for a given wall, if applicable""" shape = None pla = FreeCAD.Placement(obj.Placement) - if isinstance(delta,FreeCAD.Vector): + if isinstance(delta, FreeCAD.Vector): pla.Base += delta - if getattr(obj,"Nodes",None): + if getattr(obj, "Nodes", None): shape = Part.makePolygon([pla.multVec(v) for v in obj.Nodes]) - elif hasattr(obj,"Base") and hasattr(obj.Base,"Shape") and obj.Base.Shape: + elif hasattr(obj, "Base") and hasattr(obj.Base, "Shape") and obj.Base.Shape: shape = obj.Base.Shape if shape: - if shape.ShapeType in ["Wire","Edge"]: - curve = createCurve(ifcfile,shape,preferences["SCALE_FACTOR"]) + if shape.ShapeType in ["Wire", "Edge"]: + curve = createCurve(ifcfile, shape, preferences["SCALE_FACTOR"]) if curve: ctx = getAxisContext(ifcfile) - axis = ifcfile.createIfcShapeRepresentation(ctx,'Axis','Curve2D',[curve]) + axis = ifcfile.createIfcShapeRepresentation(ctx, "Axis", "Curve2D", [curve]) return axis return None -def writeJson(filename,ifcfile): +def writeJson(filename, ifcfile): """writes an .ifcjson file""" import json + try: from ifcjson import ifc2json5a except Exception: @@ -2556,9 +2695,9 @@ def writeJson(filename,ifcfile): return print("Converting IFC to JSON...") jsonfile = ifc2json5a.IFC2JSON5a(ifcfile).spf2Json() - f = pyopen(filename,'w') - s = json.dumps(jsonfile,indent=4) - #print("json:",s) + f = pyopen(filename, "w") + s = json.dumps(jsonfile, indent=4) + # print("json:",s) f.write(s) f.close() @@ -2582,7 +2721,7 @@ def create_annotation(anno, ifcfile, context, history, preferences): if anno.isDerivedFrom("Part::Feature"): if Draft.getType(anno) == "Hatch": objectType = "HATCH" - elif getattr(anno.ViewObject,"EndArrow",False): + elif getattr(anno.ViewObject, "EndArrow", False): objectType = "LEADER" elif anno.Shape.Faces: objectType = "AREA" @@ -2590,26 +2729,33 @@ def create_annotation(anno, ifcfile, context, history, preferences): axdata = anno.Proxy.getAxisData(anno) axes = [] for ax in axdata: - p1 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[0]).multiply(preferences['SCALE_FACTOR'])[:2])) - p2 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[1]).multiply(preferences['SCALE_FACTOR'])[:2])) - pol = ifcbin.createIfcPolyline([p1,p2]) - axis = ifcfile.createIfcGridAxis(ax[2],pol,True) + p1 = ifcbin.createIfcCartesianPoint( + tuple(FreeCAD.Vector(ax[0]).multiply(preferences["SCALE_FACTOR"])[:2]) + ) + p2 = ifcbin.createIfcCartesianPoint( + tuple(FreeCAD.Vector(ax[1]).multiply(preferences["SCALE_FACTOR"])[:2]) + ) + pol = ifcbin.createIfcPolyline([p1, p2]) + axis = ifcfile.createIfcGridAxis(ax[2], pol, True) axes.append(axis) if axes: if len(axes) > 1: - print("DEBUG: exportIFC.create_annotation: Cannot create more than one axis",anno.Label) + print( + "DEBUG: exportIFC.create_annotation: Cannot create more than one axis", + anno.Label, + ) return axes[0] else: - print("Unable to handle object",anno.Label) + print("Unable to handle object", anno.Label) return None else: objectType = "LINEWORK" sh = anno.Shape.copy() - sh.scale(preferences['SCALE_FACTOR']) # to meters + sh.scale(preferences["SCALE_FACTOR"]) # to meters ehc = [] curves = [] for w in sh.Wires: - curves.append(createCurve(ifcfile,w)) + curves.append(createCurve(ifcfile, w)) for e in w.Edges: ehc.append(e.hashCode()) if curves: @@ -2617,61 +2763,69 @@ def create_annotation(anno, ifcfile, context, history, preferences): curves = [] for e in sh.Edges: if e.hashCode not in ehc: - curves.append(createCurve(ifcfile,e)) + curves.append(createCurve(ifcfile, e)) if curves: reps.append(ifcfile.createIfcGeometricCurveSet(curves)) elif anno.isDerivedFrom("App::Annotation"): objectType = "TEXT" - l = FreeCAD.Vector(anno.Position).multiply(preferences['SCALE_FACTOR']) - pos = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0)) - tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None) - ovc = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z)) + l = FreeCAD.Vector(anno.Position).multiply(preferences["SCALE_FACTOR"]) + pos = ifcbin.createIfcCartesianPoint((0.0, 0.0, 0.0)) + tpl = ifcbin.createIfcAxis2Placement3D(pos, None, None) + ovc = ifcbin.createIfcCartesianPoint((l.x, l.y, l.z)) s = ";".join(anno.LabelText) - txt = ifcfile.createIfcTextLiteral(s,tpl,"LEFT") + txt = ifcfile.createIfcTextLiteral(s, tpl, "LEFT") reps = [txt] - elif Draft.getType(anno) in ["DraftText","Text"]: + elif Draft.getType(anno) in ["DraftText", "Text"]: objectType = "TEXT" - l = FreeCAD.Vector(anno.Placement.Base).multiply(preferences['SCALE_FACTOR']) - pos = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0)) - tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None) - ovc = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z)) - zvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - xvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)))) + l = FreeCAD.Vector(anno.Placement.Base).multiply(preferences["SCALE_FACTOR"]) + pos = ifcbin.createIfcCartesianPoint((0.0, 0.0, 0.0)) + tpl = ifcbin.createIfcAxis2Placement3D(pos, None, None) + ovc = ifcbin.createIfcCartesianPoint((l.x, l.y, l.z)) + zvc = ifcbin.createIfcDirection( + tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1))) + ) + xvc = ifcbin.createIfcDirection( + tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0))) + ) alg = "LEFT" - if FreeCAD.GuiUp and hasattr(anno.ViewObject,"Justification"): + if FreeCAD.GuiUp and hasattr(anno.ViewObject, "Justification"): if anno.ViewObject.Justification == "Right": alg = "RIGHT" s = ";".join(anno.Text) - txt = ifcfile.createIfcTextLiteral(s,tpl,alg) + txt = ifcfile.createIfcTextLiteral(s, tpl, alg) reps = [txt] - elif Draft.getType(anno) in ["Dimension","LinearDimension","AngularDimension"]: + elif Draft.getType(anno) in ["Dimension", "LinearDimension", "AngularDimension"]: if FreeCAD.GuiUp: objectType = "DIMENSION" vp = anno.ViewObject.Proxy if "BBIMDIMS" in preferences and preferences["BBIMDIMS"]: - sh = Part.makePolygon([vp.p2,vp.p3]) + sh = Part.makePolygon([vp.p2, vp.p3]) else: - sh = Part.makePolygon([vp.p1,vp.p2,vp.p3,vp.p4]) - sh.scale(preferences['SCALE_FACTOR']) # to meters - curve = createCurve(ifcfile,sh) + sh = Part.makePolygon([vp.p1, vp.p2, vp.p3, vp.p4]) + sh.scale(preferences["SCALE_FACTOR"]) # to meters + curve = createCurve(ifcfile, sh) reps = [ifcfile.createIfcGeometricCurveSet([curve])] # Append text - l = FreeCAD.Vector(vp.tbase).multiply(preferences['SCALE_FACTOR']) + l = FreeCAD.Vector(vp.tbase).multiply(preferences["SCALE_FACTOR"]) zdir = None xdir = None - if hasattr(vp,"trot"): - r = FreeCAD.Rotation(vp.trot[0],vp.trot[1],vp.trot[2],vp.trot[3]) - zdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(0,0,1)))) - xdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(1,0,0)))) - pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z)) - tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir) - txt = ifcfile.createIfcTextLiteral(vp.string,tpl,"LEFT") + if hasattr(vp, "trot"): + r = FreeCAD.Rotation(vp.trot[0], vp.trot[1], vp.trot[2], vp.trot[3]) + zdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(0, 0, 1)))) + xdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(1, 0, 0)))) + pos = ifcbin.createIfcCartesianPoint((l.x, l.y, l.z)) + tpl = ifcbin.createIfcAxis2Placement3D(pos, zdir, xdir) + txt = ifcfile.createIfcTextLiteral(vp.string, tpl, "LEFT") reps.append(txt) elif Draft.getType(anno) == "SectionPlane": - p = FreeCAD.Vector(anno.Placement.Base).multiply(preferences['SCALE_FACTOR']) - ovc = ifcbin.createIfcCartesianPoint((p.x,p.y,p.z)) - zvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)))) - xvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)))) + p = FreeCAD.Vector(anno.Placement.Base).multiply(preferences["SCALE_FACTOR"]) + ovc = ifcbin.createIfcCartesianPoint((p.x, p.y, p.z)) + zvc = ifcbin.createIfcDirection( + tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1))) + ) + xvc = ifcbin.createIfcDirection( + tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0))) + ) objectType = "DRAWING" l = w = h = 1000 if anno.ViewObject: @@ -2681,53 +2835,47 @@ def create_annotation(anno, ifcfile, context, history, preferences): w = anno.ViewObject.DisplayHeight.Value if anno.Depth.Value: h = anno.Depth.Value - l = FreeCAD.Vector(l, w, h).multiply(preferences['SCALE_FACTOR']) - zdir = ifcbin.createIfcDirection((0.0,0.0,1.0)) - xdir = ifcbin.createIfcDirection((1.0,0.0,0.0)) - pos = ifcbin.createIfcCartesianPoint((-l.x/2,-l.y/2,-l.z)) - tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir) + l = FreeCAD.Vector(l, w, h).multiply(preferences["SCALE_FACTOR"]) + zdir = ifcbin.createIfcDirection((0.0, 0.0, 1.0)) + xdir = ifcbin.createIfcDirection((1.0, 0.0, 0.0)) + pos = ifcbin.createIfcCartesianPoint((-l.x / 2, -l.y / 2, -l.z)) + tpl = ifcbin.createIfcAxis2Placement3D(pos, zdir, xdir) blk = ifcfile.createIfcBlock(tpl, l.x, l.y, l.z) csg = ifcfile.createIfcCsgSolid(blk) reps = [csg] repid = "Body" reptype = "CSG" else: - print("Unable to handle object",anno.Label) + print("Unable to handle object", anno.Label) return None - for coldef in ["LineColor","TextColor","ShapeColor"]: - if hasattr(anno.ViewObject,coldef): - rgb = getattr(anno.ViewObject,coldef)[:3] + for coldef in ["LineColor", "TextColor", "ShapeColor"]: + if hasattr(anno.ViewObject, coldef): + rgb = getattr(anno.ViewObject, coldef)[:3] if rgb in curvestyles: psa = curvestyles[rgb] else: - col = ifcbin.createIfcColourRgb(rgb[0],rgb[1],rgb[2]) + col = ifcbin.createIfcColourRgb(rgb[0], rgb[1], rgb[2]) cvf = ifcfile.createIfcDraughtingPredefinedCurveFont("continuous") - ics = ifcfile.createIfcCurveStyle('Line',cvf,None,col) + ics = ifcfile.createIfcCurveStyle("Line", cvf, None, col) psa = ifcfile.createIfcPresentationStyleAssignment([ics]) curvestyles[rgb] = psa for rep in reps: - isi = ifcfile.createIfcStyledItem(rep,[psa],None) + isi = ifcfile.createIfcStyledItem(rep, [psa], None) break if not xvc: - xvc = ifcbin.createIfcDirection((1.0,0.0,0.0)) + xvc = ifcbin.createIfcDirection((1.0, 0.0, 0.0)) if not zvc: - zvc = ifcbin.createIfcDirection((0.0,0.0,1.0)) + zvc = ifcbin.createIfcDirection((0.0, 0.0, 1.0)) if not ovc: - ovc = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0)) - gpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc) + ovc = ifcbin.createIfcCartesianPoint((0.0, 0.0, 0.0)) + gpl = ifcbin.createIfcAxis2Placement3D(ovc, zvc, xvc) placement = ifcbin.createIfcLocalPlacement(gpl) - shp = ifcfile.createIfcShapeRepresentation(context,'Annotation','Annotation2D',reps) - rep = ifcfile.createIfcProductDefinitionShape(None,None,[shp]) + shp = ifcfile.createIfcShapeRepresentation(context, "Annotation", "Annotation2D", reps) + rep = ifcfile.createIfcProductDefinitionShape(None, None, [shp]) label = anno.Label description = getattr(anno, "Description", "") ann = ifcfile.createIfcAnnotation( - ifcopenshell.guid.new(), - history, - label, - description, - objectType, - placement, - rep + ifcopenshell.guid.new(), history, label, description, objectType, placement, rep ) return ann diff --git a/src/Mod/BIM/importers/exportIFCHelper.py b/src/Mod/BIM/importers/exportIFCHelper.py index 730b2c6960..df1e7c8773 100644 --- a/src/Mod/BIM/importers/exportIFCHelper.py +++ b/src/Mod/BIM/importers/exportIFCHelper.py @@ -29,6 +29,7 @@ import ifcopenshell from ifcopenshell import guid import FreeCAD + # import Draft from draftutils import params @@ -37,79 +38,149 @@ from draftutils import params def getObjectsOfIfcType(objects, ifcType): results = [] for object in objects: - if hasattr(object,"IfcType"): + if hasattr(object, "IfcType"): if object.IfcType == ifcType: results.append(object) return results -def writeUnits(ifcfile,unit="metre"): +def writeUnits(ifcfile, unit="metre"): """adds additional units settings to the given ifc file if needed""" # so far, only metre or foot possible (which is all revit knows anyway) if unit == "foot": - d1 = ifcfile.createIfcDimensionalExponents(1,0,0,0,0,0,0) - d2 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.3048),ifcfile[13]) - d3 = ifcfile.createIfcConversionBasedUnit(d1,'LENGTHUNIT','FOOT',d2) - d4 = ifcfile.createIfcDimensionalExponents(2,0,0,0,0,0,0) - d5 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.09290304000000001),ifcfile[14]) - d6 = ifcfile.createIfcConversionBasedUnit(d4,'AREAUNIT','SQUARE FOOT',d5) - d7 = ifcfile.createIfcDimensionalExponents(3,0,0,0,0,0,0) - d8 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.028316846592),ifcfile[15]) - d9 = ifcfile.createIfcConversionBasedUnit(d7,'VOLUMEUNIT','CUBIC FOOT',d8) - ifcfile.createIfcUnitAssignment((d3,d6,d9,ifcfile[18])) - else: # default = metre, no need to add anything - ifcfile.createIfcUnitAssignment((ifcfile[13],ifcfile[14],ifcfile[15],ifcfile[18])) + d1 = ifcfile.createIfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0) + d2 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.3048), ifcfile[13]) + d3 = ifcfile.createIfcConversionBasedUnit(d1, "LENGTHUNIT", "FOOT", d2) + d4 = ifcfile.createIfcDimensionalExponents(2, 0, 0, 0, 0, 0, 0) + d5 = ifcfile.createIfcMeasureWithUnit( + ifcfile.createIfcRatioMeasure(0.09290304000000001), ifcfile[14] + ) + d6 = ifcfile.createIfcConversionBasedUnit(d4, "AREAUNIT", "SQUARE FOOT", d5) + d7 = ifcfile.createIfcDimensionalExponents(3, 0, 0, 0, 0, 0, 0) + d8 = ifcfile.createIfcMeasureWithUnit( + ifcfile.createIfcRatioMeasure(0.028316846592), ifcfile[15] + ) + d9 = ifcfile.createIfcConversionBasedUnit(d7, "VOLUMEUNIT", "CUBIC FOOT", d8) + ifcfile.createIfcUnitAssignment((d3, d6, d9, ifcfile[18])) + else: # default = metre, no need to add anything + ifcfile.createIfcUnitAssignment((ifcfile[13], ifcfile[14], ifcfile[15], ifcfile[18])) return ifcfile def writeQuantities(ifcfile, obj, product, history, scale): "append quantities to the given object" - if hasattr(obj,"IfcData"): + if hasattr(obj, "IfcData"): quantities = [] - if ("ExportHeight" in obj.IfcData) and obj.IfcData["ExportHeight"] and hasattr(obj,"Height"): - quantities.append(ifcfile.createIfcQuantityLength('Height',None,None,obj.Height.Value*scale)) - if ("ExportWidth" in obj.IfcData) and obj.IfcData["ExportWidth"] and hasattr(obj,"Width"): - quantities.append(ifcfile.createIfcQuantityLength('Width',None,None,obj.Width.Value*scale)) - if ("ExportLength" in obj.IfcData) and obj.IfcData["ExportLength"] and hasattr(obj,"Length"): - quantities.append(ifcfile.createIfcQuantityLength('Length',None,None,obj.Length.Value*scale)) - if ("ExportHorizontalArea" in obj.IfcData) and obj.IfcData["ExportHorizontalArea"] and hasattr(obj,"HorizontalArea"): - quantities.append(ifcfile.createIfcQuantityArea('HorizontalArea',None,None,obj.HorizontalArea.Value*(scale**2))) - if ("ExportVerticalArea" in obj.IfcData) and obj.IfcData["ExportVerticalArea"] and hasattr(obj,"VerticalArea"): - quantities.append(ifcfile.createIfcQuantityArea('VerticalArea',None,None,obj.VerticalArea.Value*(scale**2))) - if ("ExportVolume" in obj.IfcData) and obj.IfcData["ExportVolume"] and obj.isDerivedFrom("Part::Feature"): - quantities.append(ifcfile.createIfcQuantityVolume('Volume',None,None,obj.Shape.Volume*(scale**3))) + if ( + ("ExportHeight" in obj.IfcData) + and obj.IfcData["ExportHeight"] + and hasattr(obj, "Height") + ): + quantities.append( + ifcfile.createIfcQuantityLength("Height", None, None, obj.Height.Value * scale) + ) + if ("ExportWidth" in obj.IfcData) and obj.IfcData["ExportWidth"] and hasattr(obj, "Width"): + quantities.append( + ifcfile.createIfcQuantityLength("Width", None, None, obj.Width.Value * scale) + ) + if ( + ("ExportLength" in obj.IfcData) + and obj.IfcData["ExportLength"] + and hasattr(obj, "Length") + ): + quantities.append( + ifcfile.createIfcQuantityLength("Length", None, None, obj.Length.Value * scale) + ) + if ( + ("ExportHorizontalArea" in obj.IfcData) + and obj.IfcData["ExportHorizontalArea"] + and hasattr(obj, "HorizontalArea") + ): + quantities.append( + ifcfile.createIfcQuantityArea( + "HorizontalArea", None, None, obj.HorizontalArea.Value * (scale**2) + ) + ) + if ( + ("ExportVerticalArea" in obj.IfcData) + and obj.IfcData["ExportVerticalArea"] + and hasattr(obj, "VerticalArea") + ): + quantities.append( + ifcfile.createIfcQuantityArea( + "VerticalArea", None, None, obj.VerticalArea.Value * (scale**2) + ) + ) + if ( + ("ExportVolume" in obj.IfcData) + and obj.IfcData["ExportVolume"] + and obj.isDerivedFrom("Part::Feature") + ): + quantities.append( + ifcfile.createIfcQuantityVolume("Volume", None, None, obj.Shape.Volume * (scale**3)) + ) if quantities: eltq = ifcfile.createIfcElementQuantity( - ifcopenshell.guid.new(), - history, - "ElementQuantities", - None, - "FreeCAD",quantities + ifcopenshell.guid.new(), history, "ElementQuantities", None, "FreeCAD", quantities ) ifcfile.createIfcRelDefinesByProperties( - ifcopenshell.guid.new(), - history, - None, - None, - [product],eltq + ifcopenshell.guid.new(), history, None, None, [product], eltq ) class SIUnitCreator: def __init__(self, file, text, type): self.prefixes = [ - "EXA", "PETA", "TERA", "GIGA", "MEGA", "KILO", "HECTO", - "DECA", "DECI", "CENTI", "MILLI", "MICRO", "NANO", "PICO", "FEMTO", - "ATTO" + "EXA", + "PETA", + "TERA", + "GIGA", + "MEGA", + "KILO", + "HECTO", + "DECA", + "DECI", + "CENTI", + "MILLI", + "MICRO", + "NANO", + "PICO", + "FEMTO", + "ATTO", ] self.unitNames = [ - "AMPERE", "BECQUEREL", "CANDELA", "COULOMB", - "CUBIC_METRE", "DEGREE CELSIUS", "FARAD", "GRAM", "GRAY", "HENRY", - "HERTZ", "JOULE", "KELVIN", "LUMEN", "LUX", "MOLE", "NEWTON", "OHM", - "PASCAL", "RADIAN", "SECOND", "SIEMENS", "SIEVERT", "SQUARE METRE", - "METRE", "STERADIAN", "TESLA", "VOLT", "WATT", "WEBER" + "AMPERE", + "BECQUEREL", + "CANDELA", + "COULOMB", + "CUBIC_METRE", + "DEGREE CELSIUS", + "FARAD", + "GRAM", + "GRAY", + "HENRY", + "HERTZ", + "JOULE", + "KELVIN", + "LUMEN", + "LUX", + "MOLE", + "NEWTON", + "OHM", + "PASCAL", + "RADIAN", + "SECOND", + "SIEMENS", + "SIEVERT", + "SQUARE METRE", + "METRE", + "STERADIAN", + "TESLA", + "VOLT", + "WATT", + "WEBER", ] self.text = text self.SIUnit = file.createIfcSIUnit(None, type, self.getSIPrefix(), self.getSIUnitName()) @@ -141,16 +212,18 @@ class ContextCreator: def createGeometricRepresentationContext(self): return self.file.createIfcGeometricRepresentationContext( - None, "Model", - 3, 1.0E-05, + None, + "Model", + 3, + 1.0e-05, self.file.by_type("IfcAxis2Placement3D")[0], - self.createTrueNorth()) + self.createTrueNorth(), + ) def createGeometricRepresentationSubContext(self): return self.file.createIfcGeometricRepresentationSubContext( - "Body", "Model", - None, None, None, None, - self.model_context, None, "MODEL_VIEW", None) + "Body", "Model", None, None, None, None, self.model_context, None, "MODEL_VIEW", None + ) def createTargetCRS(self): try: @@ -162,7 +235,7 @@ class ContextCreator: self.project_data["vertical_datum"], self.project_data["map_projection"], self.project_data["map_zone"], - SIUnit.SIUnit + SIUnit.SIUnit, ) except Exception: return None @@ -170,30 +243,32 @@ class ContextCreator: def createMapConversion(self): try: return self.file.createIfcMapConversion( - self.model_context, self.target_crs, + self.model_context, + self.target_crs, float(self.project_data["eastings"]), float(self.project_data["northings"]), float(self.project_data["orthogonal_height"]), self.calculateXAxisAbscissa(), self.calculateXAxisOrdinate(), - float(self.project_data["scale"]) + float(self.project_data["scale"]), ) except Exception: return None def createTrueNorth(self): return self.file.createIfcDirection( - (self.calculateXAxisAbscissa(), self.calculateXAxisOrdinate())) + (self.calculateXAxisAbscissa(), self.calculateXAxisOrdinate()) + ) def calculateXAxisAbscissa(self): if "true_north" in self.project_data: return math.cos(math.radians(float(self.project_data["true_north"]) + 90)) - return 0. + return 0.0 def calculateXAxisOrdinate(self): if "true_north" in self.project_data: return math.sin(math.radians(float(self.project_data["true_north"]) + 90)) - return 1. + return 1.0 def createProject(self): if not self.project_object: @@ -204,19 +279,27 @@ class ContextCreator: return self.file.createIfcProject( self.getProjectGUID(), self.file.by_type("IfcOwnerHistory")[0], - FreeCAD.ActiveDocument.Name, None, - None, None, None, [self.model_context], - self.file.by_type("IfcUnitAssignment")[0]) + FreeCAD.ActiveDocument.Name, + None, + None, + None, + None, + [self.model_context], + self.file.by_type("IfcUnitAssignment")[0], + ) def createCustomProject(self): return self.file.createIfcProject( self.getProjectGUID(), self.file.by_type("IfcOwnerHistory")[0], - self.project_object.Label, self.project_object.Description, - self.project_object.ObjectType, self.project_object.LongName, + self.project_object.Label, + self.project_object.Description, + self.project_object.ObjectType, + self.project_object.LongName, self.project_object.Phase, [self.model_context], - self.file.by_type("IfcUnitAssignment")[0]) + self.file.by_type("IfcUnitAssignment")[0], + ) def getProjectGUID(self): # TODO: Do not generate a new one each time, but at least this one @@ -232,18 +315,19 @@ class ContextCreator: def getProjectObjectData(self): if not self.project_object: return {} - return json.loads(self.project_object.IfcData['complex_attributes'])["RepresentationContexts"] + return json.loads(self.project_object.IfcData["complex_attributes"])[ + "RepresentationContexts" + ] class recycler: - "the compression engine - a mechanism to reuse ifc entities if needed" # this object has some methods identical to corresponding ifcopenshell methods, # but it checks if a similar entity already exists before creating a new one # to compress a new type, just add the necessary method here - def __init__(self,ifcfile,template=True): + def __init__(self, ifcfile, template=True): self.ifcfile = ifcfile self.compress = params.get_param_arch("ifcCompress") @@ -251,10 +335,16 @@ class recycler: self.cartesianpoints = {} self.directions = {} self.axis2placement3ds = {} - if template: # we are using the default template from exportIFC.py - self.cartesianpoints = {(0,0,0):self.ifcfile[8]} # from template - self.directions = {(1,0,0):self.ifcfile[6],(0,0,1):self.ifcfile[7],(0,1,0):self.ifcfile[10]} # from template - self.axis2placement3ds = {'(0.0, 0.0, 0.0)(0.0, 0.0, 1.0)(1.0, 0.0, 0.0)':self.ifcfile[9]} # from template + if template: # we are using the default template from exportIFC.py + self.cartesianpoints = {(0, 0, 0): self.ifcfile[8]} # from template + self.directions = { + (1, 0, 0): self.ifcfile[6], + (0, 0, 1): self.ifcfile[7], + (0, 1, 0): self.ifcfile[10], + } # from template + self.axis2placement3ds = { + "(0.0, 0.0, 0.0)(0.0, 0.0, 1.0)(1.0, 0.0, 0.0)": self.ifcfile[9] + } # from template self.polylines = {} self.polyloops = {} self.propertysinglevalues = {} @@ -268,7 +358,7 @@ class recycler: self.spared = 0 self.profiledefs = {} - def createIfcCartesianPoint(self,points): + def createIfcCartesianPoint(self, points): if self.compress and points in self.cartesianpoints: self.spared += 1 return self.cartesianpoints[points] @@ -278,7 +368,7 @@ class recycler: self.cartesianpoints[points] = c return c - def createIfcDirection(self,points): + def createIfcDirection(self, points): if self.compress and points in self.directions: self.spared += 1 return self.directions[points] @@ -288,7 +378,7 @@ class recycler: self.directions[points] = c return c - def createIfcPolyline(self,points): + def createIfcPolyline(self, points): key = "".join([str(p.Coordinates) for p in points]) if self.compress and key in self.polylines: self.spared += 1 @@ -299,7 +389,7 @@ class recycler: self.polylines[key] = c return c - def createIfcPolyLoop(self,points): + def createIfcPolyLoop(self, points): key = "".join([str(p.Coordinates) for p in points]) if self.compress and key in self.polyloops: self.spared += 1 @@ -310,24 +400,28 @@ class recycler: self.polyloops[key] = c return c - def createIfcPropertySingleValue(self,name,ptype,pvalue): + def createIfcPropertySingleValue(self, name, ptype, pvalue): key = str(name) + str(ptype) + str(pvalue) if self.compress and key in self.propertysinglevalues: self.spared += 1 return self.propertysinglevalues[key] else: - if isinstance(pvalue,float) and pvalue < 0.000000001: # remove the exp notation that some bim apps hate + if ( + isinstance(pvalue, float) and pvalue < 0.000000001 + ): # remove the exp notation that some bim apps hate pvalue = 0 - c = self.ifcfile.createIfcPropertySingleValue(name,None,self.ifcfile.create_entity(ptype,pvalue),None) + c = self.ifcfile.createIfcPropertySingleValue( + name, None, self.ifcfile.create_entity(ptype, pvalue), None + ) if self.compress: self.propertysinglevalues[key] = c return c - def createIfcAxis2Placement3D(self,p1=None,p2=None,p3=None): + def createIfcAxis2Placement3D(self, p1=None, p2=None, p3=None): if not p1: - p1 = self.createIfcCartesianPoint((0.0,0.0,0.0)) - p2 = self.createIfcDirection((0.0,0.0,1.0)) - p3 = self.createIfcDirection((1.0,0.0,0.0)) + p1 = self.createIfcCartesianPoint((0.0, 0.0, 0.0)) + p2 = self.createIfcDirection((0.0, 0.0, 1.0)) + p3 = self.createIfcDirection((1.0, 0.0, 0.0)) if p2: tp2 = str(p2.DirectionRatios) else: @@ -341,96 +435,110 @@ class recycler: self.spared += 1 return self.axis2placement3ds[key] else: - c = self.ifcfile.createIfcAxis2Placement3D(p1,p2,p3) + c = self.ifcfile.createIfcAxis2Placement3D(p1, p2, p3) if self.compress: self.axis2placement3ds[key] = c return c - def createIfcAxis2Placement2D(self,p1,p2): + def createIfcAxis2Placement2D(self, p1, p2): key = str(p1.Coordinates) + str(p2.DirectionRatios) if self.compress and key in self.axis2placement2ds: self.spared += 1 return self.axis2placement2ds[key] else: - c = self.ifcfile.createIfcAxis2Placement2D(p1,p2) + c = self.ifcfile.createIfcAxis2Placement2D(p1, p2) if self.compress: self.axis2placement2ds[key] = c return c - def createIfcLocalPlacement(self,gpl=None): + def createIfcLocalPlacement(self, gpl=None): if not gpl: gpl = self.createIfcAxis2Placement3D() - key = str(gpl.Location.Coordinates) + str(gpl.Axis.DirectionRatios) + str(gpl.RefDirection.DirectionRatios) + key = ( + str(gpl.Location.Coordinates) + + str(gpl.Axis.DirectionRatios) + + str(gpl.RefDirection.DirectionRatios) + ) if self.compress and key in self.localplacements: self.spared += 1 return self.localplacements[key] else: - c = self.ifcfile.createIfcLocalPlacement(None,gpl) + c = self.ifcfile.createIfcLocalPlacement(None, gpl) if self.compress: self.localplacements[key] = c return c - def createIfcColourRgb(self,r,g,b): - key = (r,g,b) + def createIfcColourRgb(self, r, g, b): + key = (r, g, b) if self.compress and key in self.rgbs: self.spared += 1 return self.rgbs[key] else: - c = self.ifcfile.createIfcColourRgb(None,r,g,b) + c = self.ifcfile.createIfcColourRgb(None, r, g, b) if self.compress: self.rgbs[key] = c return c - def createIfcSurfaceStyleRendering(self,col,alpha=1): - key = (col.Red,col.Green,col.Blue,alpha) + def createIfcSurfaceStyleRendering(self, col, alpha=1): + key = (col.Red, col.Green, col.Blue, alpha) if self.compress and key in self.ssrenderings: self.spared += 1 return self.ssrenderings[key] else: if alpha == 1: alpha = None - c = self.ifcfile.createIfcSurfaceStyleRendering(col,alpha,None,None,None,None,None,None,"FLAT") + c = self.ifcfile.createIfcSurfaceStyleRendering( + col, alpha, None, None, None, None, None, None, "FLAT" + ) if self.compress: self.ssrenderings[key] = c return c - def createIfcCartesianTransformationOperator3D(self,axis1,axis2,origin,scale,axis3): - key = str(axis1.DirectionRatios) + str(axis2.DirectionRatios) + str(origin.Coordinates) + str(scale) + str(axis3.DirectionRatios) + def createIfcCartesianTransformationOperator3D(self, axis1, axis2, origin, scale, axis3): + key = ( + str(axis1.DirectionRatios) + + str(axis2.DirectionRatios) + + str(origin.Coordinates) + + str(scale) + + str(axis3.DirectionRatios) + ) if self.compress and key in self.transformationoperators: self.spared += 1 return self.transformationoperators[key] else: - c = self.ifcfile.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,scale,axis3) + c = self.ifcfile.createIfcCartesianTransformationOperator3D( + axis1, axis2, origin, scale, axis3 + ) if self.compress: self.transformationoperators[key] = c return c - def createIfcSurfaceStyle(self,name,r,g,b,a=1): + def createIfcSurfaceStyle(self, name, r, g, b, a=1): if name: - key = name + str((r,g,b)) + key = name + str((r, g, b)) else: - key = str((r,g,b)) + key = str((r, g, b)) if self.compress and key in self.sstyles: self.spared += 1 return self.sstyles[key] else: - col = self.createIfcColourRgb(r,g,b) - ssr = self.createIfcSurfaceStyleRendering(col,a) - c = self.ifcfile.createIfcSurfaceStyle(name,"BOTH",[ssr]) + col = self.createIfcColourRgb(r, g, b) + ssr = self.createIfcSurfaceStyleRendering(col, a) + c = self.ifcfile.createIfcSurfaceStyle(name, "BOTH", [ssr]) if self.compress: self.sstyles[key] = c return c - def createIfcPresentationStyleAssignment(self,name,r,g,b,a=1,ifc4=False): + def createIfcPresentationStyleAssignment(self, name, r, g, b, a=1, ifc4=False): if name: - key = name+str((r,g,b,a)) + key = name + str((r, g, b, a)) else: - key = str((r,g,b,a)) + key = str((r, g, b, a)) if self.compress and key in self.psas: self.spared += 1 return self.psas[key] else: - iss = self.createIfcSurfaceStyle(name,r,g,b,a) + iss = self.createIfcSurfaceStyle(name, r, g, b, a) if ifc4: c = iss else: @@ -439,32 +547,32 @@ class recycler: self.psas[key] = c return c - def createIfcRectangleProfileDef(self,name,mode,pt,b,h): - key = "RECT"+str(name)+str(mode)+str(pt)+str(b)+str(h) + def createIfcRectangleProfileDef(self, name, mode, pt, b, h): + key = "RECT" + str(name) + str(mode) + str(pt) + str(b) + str(h) if self.compress and self.mergeProfiles and key in self.profiledefs: return self.profiledefs[key] else: - c = self.ifcfile.createIfcRectangleProfileDef(name,mode,pt,b,h) + c = self.ifcfile.createIfcRectangleProfileDef(name, mode, pt, b, h) if self.compress and self.mergeProfiles: self.profiledefs[key] = c return c - def createIfcCircleProfileDef(self,name,mode,pt,r): - key = "CIRC"+str(name)+str(mode)+str(pt)+str(r) + def createIfcCircleProfileDef(self, name, mode, pt, r): + key = "CIRC" + str(name) + str(mode) + str(pt) + str(r) if self.compress and self.mergeProfiles and key in self.profiledefs: return self.profiledefs[key] else: - c = self.ifcfile.createIfcCircleProfileDef(name,mode,pt,r) + c = self.ifcfile.createIfcCircleProfileDef(name, mode, pt, r) if self.compress and self.mergeProfiles: self.profiledefs[key] = c return c - def createIfcEllipseProfileDef(self,name,mode,pt,majr,minr): - key = "ELLI"+str(name)+str(mode)+str(pt)+str(majr)+str(minr) + def createIfcEllipseProfileDef(self, name, mode, pt, majr, minr): + key = "ELLI" + str(name) + str(mode) + str(pt) + str(majr) + str(minr) if self.compress and self.mergeProfiles and key in self.profiledefs: return self.profiledefs[key] else: - c = self.ifcfile.createIfcEllipseProfileDef(name,mode,pt,majr,minr) + c = self.ifcfile.createIfcEllipseProfileDef(name, mode, pt, majr, minr) if self.compress and self.mergeProfiles: self.profiledefs[key] = c return c diff --git a/src/Mod/BIM/importers/exportIFCStructuralTools.py b/src/Mod/BIM/importers/exportIFCStructuralTools.py index 35471f6184..6a6156a9e5 100644 --- a/src/Mod/BIM/importers/exportIFCStructuralTools.py +++ b/src/Mod/BIM/importers/exportIFCStructuralTools.py @@ -23,54 +23,74 @@ # *************************************************************************** -__title__ = "FreeCAD structural IFC export tools" +__title__ = "FreeCAD structural IFC export tools" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" -ALLOW_LINEAR_OBJECTS = True # allow non-solid objects (wires, etc) to become analytic objects? +ALLOW_LINEAR_OBJECTS = True # allow non-solid objects (wires, etc) to become analytic objects? -structural_nodes = {} # this keeps track of nodes during this session -structural_curves = {} # this keeps track of structural curves during this session -scaling = 1.0 # this keeps track of scaling during this session +structural_nodes = {} # this keeps track of nodes during this session +structural_curves = {} # this keeps track of structural curves during this session +scaling = 1.0 # this keeps track of scaling during this session def setup(ifcfile, ifcbin, scale): - """Creates all the needed setup for structural model.""" global structural_nodes, scaling structural_nodes = {} scaling = scale import ifcopenshell + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] project = ifcfile.by_type("IfcProject")[0] structContext = createStructuralContext(ifcfile) if ifcfile.wrapped_data.schema_name() == "IFC2X3": mod = ifcfile.createIfcStructuralAnalysisModel( - uid(), ownerHistory, "Structural Analysis Model", None, None, "NOTDEFINED", None, None, None) + uid(), + ownerHistory, + "Structural Analysis Model", + None, + None, + "NOTDEFINED", + None, + None, + None, + ) else: localPlacement = ifcbin.createIfcLocalPlacement() structModel = ifcfile.createIfcStructuralAnalysisModel( - uid(), ownerHistory, "Structural Analysis Model", None, None, "NOTDEFINED", None, None, None, localPlacement) - relation = ifcfile.createIfcRelDeclares(uid(), ownerHistory, None, None, project, [structModel]) + uid(), + ownerHistory, + "Structural Analysis Model", + None, + None, + "NOTDEFINED", + None, + None, + None, + localPlacement, + ) + relation = ifcfile.createIfcRelDeclares( + uid(), ownerHistory, None, None, project, [structModel] + ) def createStructuralContext(ifcfile): - """Creates an additional geometry context for structural objects. Returns the new context""" contexts = ifcfile.by_type("IfcGeometricRepresentationContext") # filter out subcontexts contexts = [c for c in contexts if c.is_a() == "IfcGeometricRepresentationContext"] - geomContext = contexts[0] # arbitrarily take the first one... + geomContext = contexts[0] # arbitrarily take the first one... structContext = ifcfile.createIfcGeometricRepresentationSubContext( - 'Analysis', 'Axis', None, None, None, None, geomContext, None, "GRAPH_VIEW", None) + "Analysis", "Axis", None, None, None, None, geomContext, None, "GRAPH_VIEW", None + ) return structContext def getStructuralContext(ifcfile): - """Returns the structural context from the file""" for c in ifcfile.by_type("IfcGeometricRepresentationSubContext"): if c.ContextIdentifier == "Analysis": @@ -78,16 +98,18 @@ def getStructuralContext(ifcfile): def createStructuralNode(ifcfile, ifcbin, point): - """Creates a connection node at the given point""" import ifcopenshell + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] structContext = getStructuralContext(ifcfile) cartPoint = ifcbin.createIfcCartesianPoint(tuple(point)) vertPoint = ifcfile.createIfcVertexPoint(cartPoint) - topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, 'Analysis', 'Vertex', [vertPoint]) + topologyRep = ifcfile.createIfcTopologyRepresentation( + structContext, "Analysis", "Vertex", [vertPoint] + ) prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, [topologyRep]) # boundary conditions serve for ex. to create fixed nodes # appliedCondition = ifcfile.createIfcBoundaryNodeCondition( @@ -98,29 +120,46 @@ def createStructuralNode(ifcfile, ifcbin, point): localPlacement = ifcbin.createIfcLocalPlacement() if ifcfile.wrapped_data.schema_name() == "IFC2X3": structPntConn = ifcfile.createIfcStructuralPointConnection( - uid(), ownerHistory, 'Vertex', None, None, localPlacement, prodDefShape, appliedCondition) + uid(), + ownerHistory, + "Vertex", + None, + None, + localPlacement, + prodDefShape, + appliedCondition, + ) else: structPntConn = ifcfile.createIfcStructuralPointConnection( - uid(), ownerHistory, 'Vertex', None, None, localPlacement, prodDefShape, appliedCondition, None) + uid(), + ownerHistory, + "Vertex", + None, + None, + localPlacement, + prodDefShape, + appliedCondition, + None, + ) return structPntConn def createStructuralCurve(ifcfile, ifcbin, curve): - """Creates a structural connection for a curve""" import ifcopenshell + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] structContext = getStructuralContext(ifcfile) - cartPnt1 = ifcbin.createIfcCartesianPoint(tuple(curve.Vertexes[ 0].Point.multiply(scaling))) + cartPnt1 = ifcbin.createIfcCartesianPoint(tuple(curve.Vertexes[0].Point.multiply(scaling))) cartPnt2 = ifcbin.createIfcCartesianPoint(tuple(curve.Vertexes[-1].Point.multiply(scaling))) vertPnt1 = ifcfile.createIfcVertexPoint(cartPnt1) vertPnt2 = ifcfile.createIfcVertexPoint(cartPnt2) edge = ifcfile.createIfcEdge(vertPnt1, vertPnt2) topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Edge", [edge]) - prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None , [topologyRep]) + prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, [topologyRep]) # boundary conditions serve for ex. to create fixed edges # for now we don't create any boundary condition @@ -132,12 +171,20 @@ def createStructuralCurve(ifcfile, ifcbin, curve): zAxis = ifcfile.createIfcDirection(tuple(orientation[2])) localAxes = ifcfile.createIfcAxis2Placement3D(origin, zAxis, xAxis) structCrvConn = ifcfile.createIfcStructuralCurveConnection( - uid(), ownerHistory, "Line", None, None, localPlacement, prodDefShape, appliedCondition, localAxes) + uid(), + ownerHistory, + "Line", + None, + None, + localPlacement, + prodDefShape, + appliedCondition, + localAxes, + ) return structCrvConn def createStructuralMember(ifcfile, ifcbin, obj): - """Creates a structural member if possible. Returns the member""" global structural_nodes, structural_curves @@ -146,6 +193,7 @@ def createStructuralMember(ifcfile, ifcbin, obj): import Part import ifcopenshell import FreeCAD + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] structContext = getStructuralContext(ifcfile) @@ -186,15 +234,17 @@ def createStructuralMember(ifcfile, ifcbin, obj): # - no materials properties are takein into account # - # create geometry - verts = [None for _ in range(len(edges)+1)] - verts[0] = tuple(edges[0].Vertexes[ 0].Point.multiply(scaling)) + verts = [None for _ in range(len(edges) + 1)] + verts[0] = tuple(edges[0].Vertexes[0].Point.multiply(scaling)) verts[1] = tuple(edges[0].Vertexes[-1].Point.multiply(scaling)) cartPnt1 = ifcfile.createIfcCartesianPoint(verts[0]) cartPnt2 = ifcfile.createIfcCartesianPoint(verts[1]) vertPnt1 = ifcfile.createIfcVertexPoint(cartPnt1) vertPnt2 = ifcfile.createIfcVertexPoint(cartPnt2) newEdge = ifcfile.createIfcEdge(vertPnt1, vertPnt2) - topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Edge", (newEdge,)) + topologyRep = ifcfile.createIfcTopologyRepresentation( + structContext, "Analysis", "Edge", (newEdge,) + ) prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, (topologyRep,)) # set local coordinate system localPlacement = ifcbin.createIfcLocalPlacement() @@ -202,11 +252,28 @@ def createStructuralMember(ifcfile, ifcbin, obj): # create structural member if ifcfile.wrapped_data.schema_name() == "IFC2X3": structuralMember = ifcfile.createIfcStructuralCurveMember( - uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER") + uid(), + ownerHistory, + obj.Label, + None, + None, + localPlacement, + prodDefShape, + "RIGID_JOINED_MEMBER", + ) else: localZAxis = ifcbin.createIfcDirection((0, 0, 1)) structuralMember = ifcfile.createIfcStructuralCurveMember( - uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER", localZAxis) + uid(), + ownerHistory, + obj.Label, + None, + None, + localPlacement, + prodDefShape, + "RIGID_JOINED_MEMBER", + localZAxis, + ) elif len(edges) > 1: # SURFACE OBJECTS: slabs (horizontal, vertical, inclined) @@ -249,15 +316,26 @@ def createStructuralMember(ifcfile, ifcbin, obj): plane = ifcfile.createIfcPlane(localAxes) faceBound = ifcfile.createIfcFaceBound(edgeLoop, True) face = ifcfile.createIfcFaceSurface((faceBound,), plane, True) - topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Face", (face,)) + topologyRep = ifcfile.createIfcTopologyRepresentation( + structContext, "Analysis", "Face", (face,) + ) prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, (topologyRep,)) # sets surface thickness # TODO: ATM limitations # - for vertical slabs (walls) or inclined slabs (ramps) the thickness is taken from the Height property - thickness = float(obj.Height)*scaling + thickness = float(obj.Height) * scaling # creates structural member structuralMember = ifcfile.createIfcStructuralSurfaceMember( - uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "SHELL", thickness) + uid(), + ownerHistory, + obj.Label, + None, + None, + localPlacement, + prodDefShape, + "SHELL", + thickness, + ) # check for existing connection nodes for vert in verts: @@ -271,24 +349,50 @@ def createStructuralMember(ifcfile, ifcbin, obj): structPntConn = createStructuralNode(ifcfile, ifcbin, vert) structural_nodes[vertCoord] = structPntConn ifcfile.createIfcRelConnectsStructuralMember( - uid(), ownerHistory, None, None, structuralMember, structPntConn, None, None, None, None) + uid(), + ownerHistory, + None, + None, + structuralMember, + structPntConn, + None, + None, + None, + None, + ) else: # just add the point, no other member using it yet structural_nodes[vertCoord] = None # check for existing connection curves for edge in edges: - verts12 = tuple([edge.Vertexes[ 0].Point.x, edge.Vertexes[ 0].Point.y, edge.Vertexes[ 0].Point.z, - edge.Vertexes[-1].Point.x, edge.Vertexes[-1].Point.y, edge.Vertexes[-1].Point.z]) - verts21 = tuple([edge.Vertexes[-1].Point.x, edge.Vertexes[-1].Point.y, edge.Vertexes[-1].Point.z, - edge.Vertexes[ 0].Point.x, edge.Vertexes[ 0].Point.y, edge.Vertexes[ 0].Point.z]) + verts12 = tuple( + [ + edge.Vertexes[0].Point.x, + edge.Vertexes[0].Point.y, + edge.Vertexes[0].Point.z, + edge.Vertexes[-1].Point.x, + edge.Vertexes[-1].Point.y, + edge.Vertexes[-1].Point.z, + ] + ) + verts21 = tuple( + [ + edge.Vertexes[-1].Point.x, + edge.Vertexes[-1].Point.y, + edge.Vertexes[-1].Point.z, + edge.Vertexes[0].Point.x, + edge.Vertexes[0].Point.y, + edge.Vertexes[0].Point.z, + ] + ) verts12_in_curves = verts12 in structural_curves verts21_in_curves = verts21 in structural_curves if verts21_in_curves: verts = verts21 else: verts = verts12 - if (verts12_in_curves or verts21_in_curves): + if verts12_in_curves or verts21_in_curves: if structural_curves[verts]: # there is already another member using this curve strucCrvConn = structural_curves[verts] @@ -297,7 +401,8 @@ def createStructuralMember(ifcfile, ifcbin, obj): strucCrvConn = createStructuralCurve(ifcfile, ifcbin, edge) structural_curves[verts] = strucCrvConn ifcfile.createIfcRelConnectsStructuralMember( - uid(), None, None, None, structuralMember, strucCrvConn, None, None, None, None) + uid(), None, None, None, structuralMember, strucCrvConn, None, None, None, None + ) else: # just add the curve, no other member using it yet structural_curves[verts] = None @@ -305,10 +410,10 @@ def createStructuralMember(ifcfile, ifcbin, obj): def createStructuralGroup(ifcfile): - """Assigns all structural objects found in the file to the structural model""" import ifcopenshell + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] structSrfMember = ifcfile.by_type("IfcStructuralSurfaceMember") @@ -319,17 +424,19 @@ def createStructuralGroup(ifcfile): if structModel: members = structSrfMember + structCrvMember + structPntConn + structCrvConn if members: - ifcfile.createIfcRelAssignsToGroup(uid(), ownerHistory, None, None, members, "PRODUCT", structModel) + ifcfile.createIfcRelAssignsToGroup( + uid(), ownerHistory, None, None, members, "PRODUCT", structModel + ) def associates(ifcfile, aobj, sobj): - """Associates an arch object with a struct object""" # This is probably not the right way to do this, ie. relate a structural # object with an IfcProduct. Needs to investigate more.... import ifcopenshell + uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] ifcfile.createIfcRelAssignsToProduct(uid(), ownerHistory, None, None, [sobj], None, aobj) diff --git a/src/Mod/BIM/importers/import3DS.py b/src/Mod/BIM/importers/import3DS.py index 16e88a8584..26193caa16 100644 --- a/src/Mod/BIM/importers/import3DS.py +++ b/src/Mod/BIM/importers/import3DS.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD 3DS importer" +__title__ = "FreeCAD 3DS importer" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package import3DS # \ingroup ARCH @@ -65,7 +65,7 @@ def open(filename): return doc -def insert(filename,docname): +def insert(filename, docname): "called when freecad wants to import a file" if not check3DS(): return @@ -79,15 +79,15 @@ def insert(filename,docname): def read(filename): - dom = dom3ds.read_3ds_file(filename,tight=False) + dom = dom3ds.read_3ds_file(filename, tight=False) - for j,d_nobj in enumerate(dom.mdata.objects): + for j, d_nobj in enumerate(dom.mdata.objects): if type(d_nobj.obj) != dom3ds.N_TRI_OBJECT: continue verts = [] if d_nobj.obj.points: for d_point in d_nobj.obj.points.array: - verts.append([d_point[0],d_point[1],d_point[2]]) + verts.append([d_point[0], d_point[1], d_point[2]]) meshdata = [] for d_face in d_nobj.obj.faces.array: meshdata.append([verts[int(d_face[i])] for i in range(3)]) @@ -95,8 +95,8 @@ def read(filename): m = m[0] + m[1] + m[2] + m[3] placement = FreeCAD.Placement(FreeCAD.Matrix(*m)) mesh = Mesh.Mesh(meshdata) - obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature","Mesh") + obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature", "Mesh") obj.Mesh = mesh obj.Placement = placement else: - print("Skipping object without vertices array: ",d_nobj.obj) + print("Skipping object without vertices array: ", d_nobj.obj) diff --git a/src/Mod/BIM/importers/importDAE.py b/src/Mod/BIM/importers/importDAE.py index 92248014f4..ef04a756e7 100644 --- a/src/Mod/BIM/importers/importDAE.py +++ b/src/Mod/BIM/importers/importDAE.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD Collada importer" +__title__ = "FreeCAD Collada importer" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package importDAE # \ingroup ARCH @@ -54,6 +54,7 @@ else: # \cond def translate(context, text): return text + # \endcond @@ -99,13 +100,13 @@ def triangulate(shape): return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology else: return MeshPart.meshFromShape( - Shape=shape, - GrowthRate=grading, - SegPerEdge=segs_per_edge, - SegPerRadius=segs_per_radius, - SecondOrder=second_order, - Optimize=optimize, - AllowQuad=allow_quads, + Shape=shape, + GrowthRate=grading, + SegPerEdge=segs_per_edge, + SegPerRadius=segs_per_radius, + SecondOrder=second_order, + Optimize=optimize, + AllowQuad=allow_quads, ).Topology @@ -147,7 +148,7 @@ def read(filename): # Implementation note: there's also `col.geometries` but when using them, # the materials are string and Gaël didn't find a way to get the material # node from this string. - for bound_geom in col.scene.objects('geometry'): + for bound_geom in col.scene.objects("geometry"): prim: collada.primitive.BoundPrimitive for prim in bound_geom.primitives(): if isinstance(prim, collada.triangleset.BoundTriangleSet): @@ -169,10 +170,10 @@ def read(filename): def export( - exports: list[FreeCAD.DocumentObject], - filename: str, - tessellation: int = 1, - colors: Optional[dict[str, tuple]] = None, + exports: list[FreeCAD.DocumentObject], + filename: str, + tessellation: int = 1, + colors: Optional[dict[str, tuple]] = None, ): """Export FreeCAD contents to a DAE file. @@ -213,21 +214,25 @@ def export( obj_ind = 0 scene_nodes = [] objects = Draft.get_group_contents( - exports, - walls=True, - addgroups=True, + exports, + walls=True, + addgroups=True, ) objects = Arch.pruneIncluded(objects, strict=True) for obj in objects: findex = np.array([]) m: Optional[Mesh.Mesh] = None if obj.isDerivedFrom("Part::Feature"): - FreeCAD.Console.PrintMessage(f"Exporting shape of object {obj.Name} (\"{obj.Label}\")" + "\n") + FreeCAD.Console.PrintMessage( + f'Exporting shape of object {obj.Name} ("{obj.Label}")' + "\n" + ) new_shape = obj.Shape.copy() new_shape.Placement = obj.getGlobalPlacement() m = Mesh.Mesh(triangulate(new_shape)) elif obj.isDerivedFrom("Mesh::Feature"): - FreeCAD.Console.PrintMessage(f"Exporting mesh of object {obj.Name} (\"{obj.Label}\")" + "\n") + FreeCAD.Console.PrintMessage( + f'Exporting mesh of object {obj.Name} ("{obj.Label}")' + "\n" + ) m = obj.Mesh elif obj.isDerivedFrom("App::Part"): for child in obj.OutList: @@ -243,27 +248,29 @@ def export( vindex = np.empty(len(topology[0]) * 3) for i in range(len(topology[0])): v = topology[0][i] - vindex[list(range(i*3, i*3+3))] = (v.x*scale, v.y*scale, v.z*scale) + vindex[list(range(i * 3, i * 3 + 3))] = (v.x * scale, v.y * scale, v.z * scale) # Normals. nindex = np.empty(len(facets) * 3) for i in range(len(facets)): n = facets[i].Normal - nindex[list(range(i*3, i*3+3))] = (n.x,n.y,n.z) + nindex[list(range(i * 3, i * 3 + 3))] = (n.x, n.y, n.z) # Face indices. findex = np.empty(len(topology[1]) * 6, np.int64) for i in range(len(topology[1])): f = topology[1][i] - findex[list(range(i*6, i*6+6))] = (f[0], i, f[1], i, f[2], i) + findex[list(range(i * 6, i * 6 + 6))] = (f[0], i, f[1], i, f[2], i) vert_src = collada.source.FloatSource(f"cubeverts-array{obj_ind}", vindex, ("X", "Y", "Z")) - normal_src = collada.source.FloatSource(f"cubenormals-array{obj_ind}", nindex, ("X", "Y", "Z")) + normal_src = collada.source.FloatSource( + f"cubenormals-array{obj_ind}", nindex, ("X", "Y", "Z") + ) geom = collada.geometry.Geometry( - collada=col_mesh, - id=f"geometry{obj_ind}", - name=xml_escape(obj.Label), - sourcebyid=[vert_src, normal_src], + collada=col_mesh, + id=f"geometry{obj_ind}", + name=xml_escape(obj.Label), + sourcebyid=[vert_src, normal_src], ) input_list = collada.source.InputList() input_list.addInput(0, "VERTEX", f"#cubeverts-array{obj_ind}") @@ -271,31 +278,33 @@ def export( mat_node: Optional[collada.scene.MaterialNode] = None mat_ref = "materialref" if ( - hasattr(obj, "Material") + hasattr(obj, "Material") and obj.Material and hasattr(obj.Material, "Material") and ("DiffuseColor" in obj.Material.Material) ): - kd = tuple([float(k) for k in obj.Material.Material["DiffuseColor"].strip("()").split(",")]) + kd = tuple( + [float(k) for k in obj.Material.Material["DiffuseColor"].strip("()").split(",")] + ) effect = collada.material.Effect( - id=f"effect_{obj.Material.Name}", - params=[], - shadingtype="phong", - diffuse=kd, - specular=(1, 1, 1), + id=f"effect_{obj.Material.Name}", + params=[], + shadingtype="phong", + diffuse=kd, + specular=(1, 1, 1), ) mat = collada.material.Material( - id=f"mat_{obj.Material.Name}", - name=obj.Material.Name, - effect=effect, + id=f"mat_{obj.Material.Name}", + name=obj.Material.Name, + effect=effect, ) col_mesh.effects.append(effect) col_mesh.materials.append(mat) mat_ref = f"ref_{obj.Material.Name}" mat_node = collada.scene.MaterialNode( - symbol=mat_ref, - target=mat, - inputs=[], + symbol=mat_ref, + target=mat, + inputs=[], ) if not mat_node: if obj.Name in colors: @@ -307,68 +316,68 @@ def export( color = color[0] kd = color[:3] effect = collada.material.Effect( - id=f"effect_{obj.Name}", - params=[], - shadingtype="phong", - diffuse=kd, - specular=(1, 1, 1), + id=f"effect_{obj.Name}", + params=[], + shadingtype="phong", + diffuse=kd, + specular=(1, 1, 1), ) mat = collada.material.Material( - id=f"mat_{obj.Name}", - name=obj.Name, - effect=effect, + id=f"mat_{obj.Name}", + name=obj.Name, + effect=effect, ) col_mesh.effects.append(effect) col_mesh.materials.append(mat) mat_ref = f"ref_{obj.Name}" mat_node = collada.scene.MaterialNode( - symbol=mat_ref, - target=mat, - inputs=[], + symbol=mat_ref, + target=mat, + inputs=[], ) elif FreeCAD.GuiUp: if hasattr(obj.ViewObject, "ShapeColor"): kd = obj.ViewObject.ShapeColor[:3] effect = collada.material.Effect( - id=f"effect_{obj.Name}", - params=[], - shadingtype="phong", - diffuse=kd, - specular=(1, 1, 1), + id=f"effect_{obj.Name}", + params=[], + shadingtype="phong", + diffuse=kd, + specular=(1, 1, 1), ) mat = collada.material.Material( - id=f"mat_{obj.Name}", - name=obj.Name, - effect=effect, + id=f"mat_{obj.Name}", + name=obj.Name, + effect=effect, ) col_mesh.effects.append(effect) col_mesh.materials.append(mat) mat_ref = f"ref_{obj.Name}" mat_node = collada.scene.MaterialNode( - symbol=mat_ref, - target=mat, - inputs=[], + symbol=mat_ref, + target=mat, + inputs=[], ) if not mat_node: if not default_mat: effect = collada.material.Effect( - id="effect_default", - params=[], - shadingtype="phong", - diffuse=default_color, - specular=(1, 1, 1), + id="effect_default", + params=[], + shadingtype="phong", + diffuse=default_color, + specular=(1, 1, 1), ) default_mat = collada.material.Material( - id="mat_default", - name="default_material", - effect=effect, + id="mat_default", + name="default_material", + effect=effect, ) col_mesh.effects.append(effect) col_mesh.materials.append(default_mat) mat_node = collada.scene.MaterialNode( - symbol=mat_ref, - target=default_mat, - inputs=[], + symbol=mat_ref, + target=default_mat, + inputs=[], ) triset = geom.createTriangleSet(indices=findex, inputlist=input_list, materialid=mat_ref) geom.primitives.append(triset) @@ -397,10 +406,10 @@ class _TriangleSet: def _read_bound_triangle_set( - doc: FreeCAD.Document, - bound_geom: "collada.geometry.BoundGeometry", - prim: "collada.primitive.BoundPrimitive", - unit: float, + doc: FreeCAD.Document, + bound_geom: "collada.geometry.BoundGeometry", + prim: "collada.primitive.BoundPrimitive", + unit: float, ) -> list[FreeCAD.DocumentObject]: """ Read a BoundTriangleSet primitive. @@ -420,10 +429,10 @@ def _read_bound_triangle_set( def _read_bound_polylist( - doc: FreeCAD.Document, - bound_geom: "collada.geometry.BoundGeometry", - prim: "collada.primitive.BoundPrimitive", - unit: float, + doc: FreeCAD.Document, + bound_geom: "collada.geometry.BoundGeometry", + prim: "collada.primitive.BoundPrimitive", + unit: float, ) -> list[FreeCAD.DocumentObject]: """ Read a BoundPolylist primitive. @@ -439,12 +448,14 @@ def _read_bound_polylist( if not isinstance(prim, collada.polylist.BoundPolylist): raise TypeError("Expected a BoundPolylist") triangle_sets = _get_triangle_sets_from_polylist(prim, unit) - return _add_meshes_to_doc(doc, triangle_sets, _name_from_bound_geom(bound_geom) + " (triangulated)") + return _add_meshes_to_doc( + doc, triangle_sets, _name_from_bound_geom(bound_geom) + " (triangulated)" + ) def _get_triangle_sets_from_triangle( - prim: "collada.triangleset.BoundTriangleSet", - unit: float, + prim: "collada.triangleset.BoundTriangleSet", + unit: float, ) -> list[_TriangleSet]: """Return triangles from the given BoundTriangleSet.""" # Get the materials and associated triangles. @@ -461,8 +472,8 @@ def _get_triangle_sets_from_triangle( def _get_triangle_sets_from_polylist( - prim: "collada.triangleset.BoundPolylist", - unit: float, + prim: "collada.triangleset.BoundPolylist", + unit: float, ) -> list[_TriangleSet]: """Return triangles from the given BoundPolylist.""" # Get the materials and associated triangles. @@ -480,7 +491,7 @@ def _get_triangle_sets_from_polylist( def _name_from_bound_geom( - bound_geom: "collada.geometry.BoundGeometry", + bound_geom: "collada.geometry.BoundGeometry", ) -> str: """Return `name` or `id` of the original geometry.""" name = bound_geom.original.name @@ -490,9 +501,9 @@ def _name_from_bound_geom( def _add_meshes_to_doc( - doc: FreeCAD.Document, - triangle_sets: list[_TriangleSet], - name: str, + doc: FreeCAD.Document, + triangle_sets: list[_TriangleSet], + name: str, ) -> list[FreeCAD.DocumentObject]: """Create a mesh object for each _TriangleSet.""" objects: list[FreeCAD.DocumentObject] = [] @@ -519,12 +530,12 @@ def _add_meshes_to_doc( # wrongly (transparency mistaken for opacity). # TODO: Ask whether to import transparency. field_map = { - "ambient": "AmbientColor", - "diffuse": "DiffuseColor", - "emission": "EmissiveColor", - "specular": "SpecularColor", - "shininess": "Shininess", - # "transparency": "Transparency", + "ambient": "AmbientColor", + "diffuse": "DiffuseColor", + "emission": "EmissiveColor", + "specular": "SpecularColor", + "shininess": "Shininess", + # "transparency": "Transparency", } for col_field, fc_field in field_map.items(): try: diff --git a/src/Mod/BIM/importers/importGBXML.py b/src/Mod/BIM/importers/importGBXML.py index c87e0278c0..c66115d8c9 100644 --- a/src/Mod/BIM/importers/importGBXML.py +++ b/src/Mod/BIM/importers/importGBXML.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD GbXml exporter" +__title__ = "FreeCAD GbXml exporter" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package importGBXML # \ingroup ARCH @@ -41,113 +41,168 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(ctx,txt): + def translate(ctx, txt): return txt + # \endcond -def export(objectslist,filename): +def export(objectslist, filename): if len(objectslist) != 1: - FreeCAD.Console.PrintError(translate("Arch","This exporter can currently only export one site object")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "This exporter can currently only export one site object") + "\n" + ) return site = objectslist[0] if Draft.getType(site) != "Site": - FreeCAD.Console.PrintError(translate("Arch","This exporter can currently only export one site object")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "This exporter can currently only export one site object") + "\n" + ) return - filestream = pyopen(filename,"wb") + filestream = pyopen(filename, "wb") # header - filestream.write( '\n' ) - filestream.write( '\n' % FreeCAD.Version()[0]+FreeCAD.Version()[1]+FreeCAD.Version()[2] ) - filestream.write( '\n' ) - filestream.write( '\n' ) + filestream.write('\n') + filestream.write( + "\n" % FreeCAD.Version()[0] + + FreeCAD.Version()[1] + + FreeCAD.Version()[2] + ) + filestream.write("\n') + filestream.write("\n") # campus - filestream.write( '\n' % site.Name ) - filestream.write( '\n' ) - filestream.write( ' %s\n' % site.PostalCode ) - filestream.write( ' %f\n' % site.Longitude ) - filestream.write( ' %f\n' % site.Latitude ) - filestream.write( ' %f/Elevation>\n' % site.Elevation.Value ) - filestream.write( ' %s\n' % site.Label ) - #filestream.write( ' 0\n' ) - #filestream.write( ' 53158_2004\n' ) - filestream.write( '\n' ) + filestream.write('\n' % site.Name) + filestream.write("\n") + filestream.write(" %s\n" % site.PostalCode) + filestream.write(" %f\n" % site.Longitude) + filestream.write(" %f\n" % site.Latitude) + filestream.write(" %f/Elevation>\n" % site.Elevation.Value) + filestream.write(" %s\n" % site.Label) + # filestream.write( ' 0\n' ) + # filestream.write( ' 53158_2004\n' ) + filestream.write("\n") # buildings for building in site.Group: if Draft.getType(building) == "Building": - filestream.write( ' \n' % (building.Name,building.BuildingType) ) - filestream.write( ' $f\n' % str(building.Area.getValueAs("m^2")) ) + filestream.write( + ' \n' + % (building.Name, building.BuildingType) + ) + filestream.write(" $f\n" % str(building.Area.getValueAs("m^2"))) # spaces - for space in Draft.getObjectsOfType(Draft.get_group_contents(building.Group, addgroups=True), - "Space"): + for space in Draft.getObjectsOfType( + Draft.get_group_contents(building.Group, addgroups=True), "Space" + ): if not space.Zone: - FreeCAD.Console.PrintError(translate("Arch","Error: Space '%s' has no Zone. Aborting.") % space.Label + "\n") + FreeCAD.Console.PrintError( + translate("Arch", "Error: Space '%s' has no Zone. Aborting.") % space.Label + + "\n" + ) return - filestream.write( ' \n' % (space.Name, space.SpaceType, space.Zone.Name, space.Conditioning) ) - #filestream.write( ' %s\n' % space.Name ) # not sure what this is used for? - filestream.write( ' %s\n' % space.Label ) - filestream.write( ' %s\n' % space.Description ) - filestream.write( ' %i\n' % space.NumberOfPeople) - filestream.write( ' %f\n' % space.LightingPower/space.Area.getValueAs("m^2") ) - filestream.write( ' %f\n' % space.EquipmentPower/space.Area.getValueAs("m^2") ) - filestream.write( ' $f\n' % space.Area.getValueAs("m^2") ) - filestream.write( ' $f\n' % FreeCAD.Units.Quantity(space.Shape.Volume,FreeCAD.Units.Volume).getValueAs("m^3") ) - filestream.write( ' \n' % space.Name ) + filestream.write( + ' \n' + % (space.Name, space.SpaceType, space.Zone.Name, space.Conditioning) + ) + # filestream.write( ' %s\n' % space.Name ) # not sure what this is used for? + filestream.write(" %s\n" % space.Label) + filestream.write(" %s\n" % space.Description) + filestream.write( + ' %i\n' + % space.NumberOfPeople + ) + filestream.write( + ' %f\n' + % space.LightingPower + / space.Area.getValueAs("m^2") + ) + filestream.write( + ' %f\n' + % space.EquipmentPower + / space.Area.getValueAs("m^2") + ) + filestream.write(" $f\n" % space.Area.getValueAs("m^2")) + filestream.write( + " $f\n" + % FreeCAD.Units.Quantity(space.Shape.Volume, FreeCAD.Units.Volume).getValueAs( + "m^3" + ) + ) + filestream.write(' \n' % space.Name) # shells for solid in space.Shape.Solids: - filestream.write( ' \n' ) + filestream.write(" \n") for face in solid.Faces: - filestream.write( ' \n' ) + filestream.write(" \n") for v in face.OuterWire.Vertexes: - filestream.write( ' \n' ) - filestream.write( ' %f\n' % v.Point.x ) - filestream.write( ' %f\n' % v.Point.y ) - filestream.write( ' %f\n' % v.Point.z ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) + filestream.write(" \n") + filestream.write( + " %f\n" + % v.Point.x + ) + filestream.write( + " %f\n" + % v.Point.y + ) + filestream.write( + " %f\n" + % v.Point.z + ) + filestream.write(" \n") + filestream.write(" \n") + filestream.write(" \n") + filestream.write(" \n") + filestream.write(" \n") # surfaces - for i,face in enumerate(space.Shape.Faces): - filestream.write( ' \n' ) - filestream.write( ' \n' ) + for i, face in enumerate(space.Shape.Faces): + filestream.write( + ' \n") + filestream.write(" \n") for v in face.OuterWire.Vertexes: - filestream.write( ' \n' ) - filestream.write( ' %f\n' % v.Point.x ) - filestream.write( ' %f\n' % v.Point.y ) - filestream.write( ' %f\n' % v.Point.z ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) - filestream.write( ' \n' ) + filestream.write(" \n") + filestream.write( + " %f\n" % v.Point.x + ) + filestream.write( + " %f\n" % v.Point.y + ) + filestream.write( + " %f\n" % v.Point.z + ) + filestream.write(" \n") + filestream.write(" \n") + filestream.write(" \n") + filestream.write(" \n") - filestream.write( ' \n' ) + filestream.write(" \n") - filestream.write( ' \n' ) + filestream.write(" \n") - filestream.write( '\n' ) + filestream.write("\n") - filestream.write( '' ) + filestream.write("") -''' + +""" 18000.00000 Lab corridor @@ -501,4 +556,4 @@ def export(objectslist,filename): ... repeat -''' +""" diff --git a/src/Mod/BIM/importers/importIFC.py b/src/Mod/BIM/importers/importIFC.py index a889df7fd0..16ca171879 100644 --- a/src/Mod/BIM/importers/importIFC.py +++ b/src/Mod/BIM/importers/importIFC.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD IFC importer - Enhanced IfcOpenShell-only version" +__title__ = "FreeCAD IFC importer - Enhanced IfcOpenShell-only version" __author__ = ("Yorik van Havre", "Jonathan Wiedemann", "Bernd Hahnebach") -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package importIFC # \ingroup ARCH @@ -64,15 +64,9 @@ ZOOMOUT = True # Set to False to not zoom extents after import # which IFC type must create which FreeCAD type typesmap = { - "Site": [ - "IfcSite" - ], - "Building": [ - "IfcBuilding" - ], - "Floor": [ - "IfcBuildingStorey" - ], + "Site": ["IfcSite"], + "Building": ["IfcBuilding"], + "Floor": ["IfcBuildingStorey"], "Structure": [ "IfcBeam", "IfcBeamStandardCase", @@ -81,52 +75,24 @@ typesmap = { "IfcSlab", "IfcFooting", "IfcPile", - "IfcTendon" - ], - "Wall": [ - "IfcWall", - "IfcWallStandardCase", - "IfcCurtainWall" - ], - "Window": [ - "IfcWindow", - "IfcWindowStandardCase", - "IfcDoor", - "IfcDoorStandardCase" - ], - "Roof": [ - "IfcRoof" - ], - "Stairs": [ - "IfcStair", - "IfcStairFlight", - "IfcRamp", - "IfcRampFlight" - ], - "Space": [ - "IfcSpace" - ], - "Rebar": [ - "IfcReinforcingBar" - ], - "Panel": [ - "IfcPlate" + "IfcTendon", ], + "Wall": ["IfcWall", "IfcWallStandardCase", "IfcCurtainWall"], + "Window": ["IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"], + "Roof": ["IfcRoof"], + "Stairs": ["IfcStair", "IfcStairFlight", "IfcRamp", "IfcRampFlight"], + "Space": ["IfcSpace"], + "Rebar": ["IfcReinforcingBar"], + "Panel": ["IfcPlate"], "Equipment": [ "IfcFurnishingElement", "IfcSanitaryTerminal", "IfcFlowTerminal", - "IfcElectricAppliance" + "IfcElectricAppliance", ], - "Pipe": [ - "IfcPipeSegment" - ], - "PipeConnector": [ - "IfcPipeFitting" - ], - "BuildingPart": [ - "IfcElementAssembly" - ] + "Pipe": ["IfcPipeSegment"], + "PipeConnector": ["IfcPipeFitting"], + "BuildingPart": ["IfcElementAssembly"], } # which IFC entity (product) is a structural object @@ -140,7 +106,7 @@ structuralifcobjects = ( "IfcStructuralPointAction", "IfcStructuralLinearAction", "IfcStructuralLinearActionVarying", - "IfcStructuralPlanarAction" + "IfcStructuralPlanarAction", ) # backwards compatibility @@ -154,6 +120,7 @@ def export(exportList, filename, colors=None, preferences=None): in this module for compatibility purposes with older scripts. """ from importers import exportIFC + exportIFC.export(exportList, filename, colors, preferences) @@ -205,10 +172,12 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # For some reason this works; see the bug report # https://github.com/IfcOpenShell/IfcOpenShell/issues/689 except ModuleNotFoundError: - _err("IfcOpenShell was not found on this system. " - "IFC support is disabled.\n" - "Visit https://wiki.freecad.org/IfcOpenShell " - "to learn about installing it.") + _err( + "IfcOpenShell was not found on this system. " + "IFC support is disabled.\n" + "Visit https://wiki.freecad.org/IfcOpenShell " + "to learn about installing it." + ) return starttime = time.time() # in seconds @@ -220,6 +189,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # override with BIM IFC importer if present try: import BimIfcImport + return BimIfcImport.insert(srcfile, docname, preferences) except: pass @@ -236,7 +206,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # allow one to override the root element if root: - preferences['ROOT_ELEMENT'] = root + preferences["ROOT_ELEMENT"] = root # keeping global variable for debugging purposes # global ifcfile @@ -248,13 +218,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): filesize = None filename = None else: - if preferences['DEBUG']: + if preferences["DEBUG"]: _msg("Opening '{}'... ".format(srcfile), end="") filename = srcfile - filesize = os.path.getsize(filename) * 1E-6 # in megabytes + filesize = os.path.getsize(filename) * 1e-6 # in megabytes ifcfile = ifcopenshell.open(filename) - if preferences['DEBUG']: + if preferences["DEBUG"]: _msg("done.") # Get file scale @@ -272,13 +242,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): settings.set(settings.USE_BREP_DATA, True) settings.set(settings.SEW_SHELLS, True) settings.set(settings.USE_WORLD_COORDS, True) - if preferences['SEPARATE_OPENINGS']: + if preferences["SEPARATE_OPENINGS"]: settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True) - if preferences['SPLIT_LAYERS'] and hasattr(settings, "APPLY_LAYERSETS"): + if preferences["SPLIT_LAYERS"] and hasattr(settings, "APPLY_LAYERSETS"): settings.set(settings.APPLY_LAYERSETS, True) # build all needed tables - if preferences['DEBUG']: + if preferences["DEBUG"]: _msg("Building types and relationships table...") # type tables @@ -287,9 +257,9 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): floors = ifcfile.by_type("IfcBuildingStorey") openings = ifcfile.by_type("IfcOpeningElement") materials = ifcfile.by_type("IfcMaterial") - (products, - annotations) = importIFCHelper.buildRelProductsAnnotations(ifcfile, - preferences['ROOT_ELEMENT']) + (products, annotations) = importIFCHelper.buildRelProductsAnnotations( + ifcfile, preferences["ROOT_ELEMENT"] + ) # empty relation tables objects = {} # { id:object, ... } @@ -316,7 +286,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): mattable = importIFCHelper.buildRelMattable(ifcfile) colors = importIFCHelper.buildRelProductColors(ifcfile, prodrepr) colordict = {} # { objname:color tuple } for non-GUI use - if preferences['DEBUG']: + if preferences["DEBUG"]: _msg("done.") # only import a list of IDs and their children, if defined @@ -332,18 +302,19 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # start the actual import, set FreeCAD UI count = 0 from FreeCAD import Base + progressbar = Base.ProgressIndicator() progressbar.start("Importing IFC objects...", len(products)) - if preferences['DEBUG']: + if preferences["DEBUG"]: _msg("Parsing {} BIM objects...".format(len(products))) # Prepare the 3D view if applicable - if preferences['FITVIEW_ONIMPORT'] and FreeCAD.GuiUp: + if preferences["FITVIEW_ONIMPORT"] and FreeCAD.GuiUp: overallboundbox = None Gui.ActiveDocument.activeView().viewAxonometric() # Create the base project object - if not preferences['REPLACE_PROJECT']: + if not preferences["REPLACE_PROJECT"]: if len(ifcfile.by_type("IfcProject")) > 0: projectImporter = importIFCHelper.ProjectImporter(ifcfile, objects) projectImporter.execute() @@ -361,13 +332,16 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): guid = product.GlobalId ptype = product.is_a() - if preferences['DEBUG']: print(count,"/",len(products),"object #"+str(pid),":",ptype,end="") + if preferences["DEBUG"]: + print(count, "/", len(products), "object #" + str(pid), ":", ptype, end="") # build list of related property sets psets = importIFCHelper.getIfcPropertySets(ifcfile, pid) # add layer names to layers - if hasattr(product, "Representation") and hasattr(product.Representation, "Representations"): + if hasattr(product, "Representation") and hasattr( + product.Representation, "Representations" + ): if len(product.Representation.Representations) > 0: lays = product.Representation.Representations[0].LayerAssignments if len(lays) > 0: @@ -376,27 +350,32 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): layers[layer_name] = [pid] else: layers[layer_name].append(pid) - if preferences['DEBUG']: print(" layer ", layer_name, " found", ptype,end="") + if preferences["DEBUG"]: + print(" layer ", layer_name, " found", ptype, end="") else: - if preferences['DEBUG']: print(" no layer found", ptype,end="") + if preferences["DEBUG"]: + print(" no layer found", ptype, end="") # checking for full FreeCAD parametric definition, overriding everything else if psets and params.get_param_arch("IfcImportFreeCADProperties"): if "FreeCADPropertySet" in [ifcfile[pset].Name for pset in psets.keys()]: - if preferences['DEBUG']: print(" restoring from parametric definition...",end="") - obj,parametrics = importIFCHelper.createFromProperties(psets,ifcfile,parametrics) + if preferences["DEBUG"]: + print(" restoring from parametric definition...", end="") + obj, parametrics = importIFCHelper.createFromProperties(psets, ifcfile, parametrics) if obj: objects[pid] = obj - if preferences['DEBUG']: print("done") + if preferences["DEBUG"]: + print("done") continue else: - if preferences['DEBUG']: print("failed") + if preferences["DEBUG"]: + print("failed") # no parametric data, we go the good old way name = str(ptype[3:]) if product.Name: name = product.Name - if preferences['PREFIX_NUMBERS']: + if preferences["PREFIX_NUMBERS"]: name = "ID" + str(pid) + " " + name obj = None baseobj = None @@ -409,37 +388,47 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if ptype in structuralifcobjects: archobj = False structobj = True - if preferences['DEBUG']: print(" (struct)",end="") + if preferences["DEBUG"]: + print(" (struct)", end="") else: - if preferences['DEBUG']: print(" (arch)",end="") - if preferences['MERGE_MODE_ARCH'] == 4 and archobj: - if preferences['DEBUG']: print(" skipped.") + if preferences["DEBUG"]: + print(" (arch)", end="") + if preferences["MERGE_MODE_ARCH"] == 4 and archobj: + if preferences["DEBUG"]: + print(" skipped.") continue - if preferences['MERGE_MODE_STRUCT'] == 3 and not archobj: - if preferences['DEBUG']: print(" skipped.") + if preferences["MERGE_MODE_STRUCT"] == 3 and not archobj: + if preferences["DEBUG"]: + print(" skipped.") continue if pid in skip: # user given id skip list - if preferences['DEBUG']: print(" skipped.") + if preferences["DEBUG"]: + print(" skipped.") continue if ptype in skip: # user given type skip list - if preferences['DEBUG']: print(" skipped.") + if preferences["DEBUG"]: + print(" skipped.") continue - if ptype in preferences['SKIP']: # preferences-set type skip list - if preferences['DEBUG']: print(" skipped.") + if ptype in preferences["SKIP"]: # preferences-set type skip list + if preferences["DEBUG"]: + print(" skipped.") continue - if preferences['REPLACE_PROJECT']: # options-enabled project/site/building skip - if ptype in ['IfcProject','IfcSite']: - if preferences['DEBUG']: print(" skipped.") + if preferences["REPLACE_PROJECT"]: # options-enabled project/site/building skip + if ptype in ["IfcProject", "IfcSite"]: + if preferences["DEBUG"]: + print(" skipped.") continue - elif ptype in ['IfcBuilding']: + elif ptype in ["IfcBuilding"]: if len(ifcfile.by_type("IfcBuilding")) == 1: # let multiple buildings through... - if preferences['DEBUG']: print(" skipped.") + if preferences["DEBUG"]: + print(" skipped.") continue - elif ptype in ['IfcBuildingStorey']: + elif ptype in ["IfcBuildingStorey"]: if len(ifcfile.by_type("IfcBuildingStorey")) == 1: # let multiple storeys through... - if preferences['DEBUG']: print(" skipped.") + if preferences["DEBUG"]: + print(" skipped.") continue # check if this object is sharing its shape (mapped representation) @@ -449,8 +438,14 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): try: prepr = product.Representation except Exception: - if preferences['DEBUG']: print(" ERROR unable to get object representation",end="") - if prepr and (preferences['MERGE_MODE_ARCH'] == 0) and archobj and preferences['CREATE_CLONES']: + if preferences["DEBUG"]: + print(" ERROR unable to get object representation", end="") + if ( + prepr + and (preferences["MERGE_MODE_ARCH"] == 0) + and archobj + and preferences["CREATE_CLONES"] + ): for r in prepr.Representations: if r.RepresentationIdentifier.upper() == "BODY": if r.Items[0].is_a("IfcMappedItem"): @@ -462,11 +457,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): store = originalid # flag this object to be stored later # set additional setting for structural entities - if hasattr(settings,"INCLUDE_CURVES"): + if hasattr(settings, "INCLUDE_CURVES"): if structobj: - settings.set(settings.INCLUDE_CURVES,True) + settings.set(settings.INCLUDE_CURVES, True) else: - settings.set(settings.INCLUDE_CURVES,False) + settings.set(settings.INCLUDE_CURVES, False) try: cr = geom.create_shape(settings, product) brep = cr.geometry.brep_data @@ -475,27 +470,33 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # from now on we have a brep string if brep: - if preferences['DEBUG']: print(" "+str(int(len(brep)/1000))+"k ",end="") + if preferences["DEBUG"]: + print(" " + str(int(len(brep) / 1000)) + "k ", end="") # create a Part shape shape = Part.Shape() - shape.importBrepFromString(brep,False) - shape.scale(1000.0) # IfcOpenShell always outputs in meters, we convert to mm, the freecad internal unit + shape.importBrepFromString(brep, False) + shape.scale( + 1000.0 + ) # IfcOpenShell always outputs in meters, we convert to mm, the freecad internal unit - if shape.isNull() and (not preferences['ALLOW_INVALID']): - if preferences['DEBUG']: print("null shape ",end="") - elif not shape.isValid() and (not preferences['ALLOW_INVALID']): - if preferences['DEBUG']: print("invalid shape ",end="") + if shape.isNull() and (not preferences["ALLOW_INVALID"]): + if preferences["DEBUG"]: + print("null shape ", end="") + elif not shape.isValid() and (not preferences["ALLOW_INVALID"]): + if preferences["DEBUG"]: + print("invalid shape ", end="") else: # add to the global boundbox if applicable - if preferences['FITVIEW_ONIMPORT'] and FreeCAD.GuiUp: + if preferences["FITVIEW_ONIMPORT"] and FreeCAD.GuiUp: try: bb = shape.BoundBox # if preferences['DEBUG']: print(' ' + str(bb),end="") except Exception: bb = None - if preferences['DEBUG']: print(' BB could not be computed',end="") + if preferences["DEBUG"]: + print(" BB could not be computed", end="") if bb and bb.isValid(): if not overallboundbox: overallboundbox = bb @@ -503,46 +504,64 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): Gui.SendMsgToActiveView("ViewFit") overallboundbox.add(bb) - if (preferences['MERGE_MODE_ARCH'] > 0 and archobj) or structobj: + if (preferences["MERGE_MODE_ARCH"] > 0 and archobj) or structobj: # we are not using Arch objects # additional tweaks to set when not using Arch objects if ptype == "IfcSpace": # do not add spaces to compounds - if preferences['DEBUG']: print("skipping space ",pid,end="") + if preferences["DEBUG"]: + print("skipping space ", pid, end="") elif structobj: structshapes[pid] = shape - if preferences['DEBUG']: print(len(shape.Solids),"solids ",end="") + if preferences["DEBUG"]: + print(len(shape.Solids), "solids ", end="") baseobj = shape else: shapes[pid] = shape - if preferences['DEBUG']: print(len(shape.Solids),"solids ",end="") + if preferences["DEBUG"]: + print(len(shape.Solids), "solids ", end="") baseobj = shape else: # create base shape object if clone: - if preferences['DEBUG']: print("clone ",end="") + if preferences["DEBUG"]: + print("clone ", end="") else: - if preferences['GET_EXTRUSIONS'] and (preferences['MERGE_MODE_ARCH'] != 1): + if preferences["GET_EXTRUSIONS"] and (preferences["MERGE_MODE_ARCH"] != 1): # get IFC profile profileid = None sortmethod = None if product.Representation: if product.Representation.Representations: - if product.Representation.Representations[0].is_a("IfcShapeRepresentation"): + if product.Representation.Representations[0].is_a( + "IfcShapeRepresentation" + ): if product.Representation.Representations[0].Items: - if product.Representation.Representations[0].Items[0].is_a("IfcExtrudedAreaSolid"): - profileid = product.Representation.Representations[0].Items[0].SweptArea.id() - sortmethod = importIFCHelper.getProfileCenterPoint(product.Representation.Representations[0].Items[0]) + if ( + product.Representation.Representations[0] + .Items[0] + .is_a("IfcExtrudedAreaSolid") + ): + profileid = ( + product.Representation.Representations[0] + .Items[0] + .SweptArea.id() + ) + sortmethod = importIFCHelper.getProfileCenterPoint( + product.Representation.Representations[0].Items[ + 0 + ] + ) # recompose extrusions from a shape if not sortmethod: - if ptype in ["IfcWall","IfcWallStandardCase","IfcSpace"]: + if ptype in ["IfcWall", "IfcWallStandardCase", "IfcSpace"]: sortmethod = "z" else: sortmethod = "area" - ex = Arch.getExtrusionData(shape,sortmethod) # is this an extrusion? + ex = Arch.getExtrusionData(shape, sortmethod) # is this an extrusion? if ex: # check for extrusion profile @@ -551,16 +570,23 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if profileid and (profileid in profiles): # reuse existing profile if existing - print("shared extrusion ",end="") + print("shared extrusion ", end="") baseface = profiles[profileid] # calculate delta placement between stored profile and this one addplacement = FreeCAD.Placement() - r = FreeCAD.Rotation(baseface.Shape.Faces[0].normalAt(0,0),ex[0].Faces[0].normalAt(0,0)) + r = FreeCAD.Rotation( + baseface.Shape.Faces[0].normalAt(0, 0), + ex[0].Faces[0].normalAt(0, 0), + ) if r.Angle > 0.000001: # use shape methods to easily obtain a correct placement ts = Part.Shape() - ts.rotate(DraftVecUtils.tup(baseface.Shape.CenterOfMass), DraftVecUtils.tup(r.Axis), math.degrees(r.Angle)) + ts.rotate( + DraftVecUtils.tup(baseface.Shape.CenterOfMass), + DraftVecUtils.tup(r.Axis), + math.degrees(r.Angle), + ) addplacement = ts.Placement d = ex[0].CenterOfMass.sub(baseface.Shape.CenterOfMass) if d.Length > 0.000001: @@ -568,19 +594,25 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if not baseface: # this is an extrusion but we haven't built the profile yet - print("extrusion ",end="") + print("extrusion ", end="") import DraftGeomUtils + if DraftGeomUtils.hasCurves(ex[0]) or len(ex[0].Wires) != 1: # is this a circle? - if (len(ex[0].Edges) == 1) and isinstance(ex[0].Edges[0].Curve,Part.Circle): + if (len(ex[0].Edges) == 1) and isinstance( + ex[0].Edges[0].Curve, Part.Circle + ): baseface = Draft.makeCircle(ex[0].Edges[0]) else: # curves or holes? We just make a Part face - baseface = doc.addObject("Part::Feature",name+"_footprint") + baseface = doc.addObject( + "Part::Feature", name + "_footprint" + ) # bug/feature in ifcopenshell? Some faces of a shell may have non-null placement # workaround to remove the bad placement: exporting/reimporting as step if not ex[0].Placement.isNull(): import tempfile + fd, tf = tempfile.mkstemp(suffix=".stp") ex[0].exportStep(tf) f = Part.read(tf) @@ -595,14 +627,14 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # TODO verts are different if shape is made of RectangleProfileDef or not # is this a rectangle? if importIFCHelper.isRectangle(verts): - baseface = Draft.makeRectangle(verts,face=True) + baseface = Draft.makeRectangle(verts, face=True) else: # no hole and no curves, we make a Draft Wire instead - baseface = Draft.makeWire(verts,closed=True) + baseface = Draft.makeWire(verts, closed=True) if profileid: # store for possible shared use profiles[profileid] = baseface - baseobj = doc.addObject("Part::Extrusion",name+"_body") + baseobj = doc.addObject("Part::Extrusion", name + "_body") baseobj.Base = baseface if addplacement: # apply delta placement (stored profile) @@ -613,24 +645,25 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if FreeCAD.GuiUp: baseface.ViewObject.hide() if not baseobj: - baseobj = doc.addObject("Part::Feature",name+"_body") + baseobj = doc.addObject("Part::Feature", name + "_body") baseobj.Shape = shape else: # this object has no shape (storeys, etc...) - if preferences['DEBUG']: print(" no brep ",end="") + if preferences["DEBUG"]: + print(" no brep ", end="") # we now have the shape, we create the final object - if preferences['MERGE_MODE_ARCH'] == 0 and archobj: + if preferences["MERGE_MODE_ARCH"] == 0 and archobj: # full Arch objects - for freecadtype,ifctypes in typesmap.items(): + for freecadtype, ifctypes in typesmap.items(): if ptype not in ifctypes: continue if clone: - obj = getattr(Arch,"make"+freecadtype)(name=name) + obj = getattr(Arch, "make" + freecadtype)(name=name) obj.CloneOf = clone # calculate the correct distance from the cloned object @@ -643,21 +676,29 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): s2 = clone.Shape.Solids[0] else: s2 = clone.Shape - if hasattr(s1,"CenterOfMass") and hasattr(s2,"CenterOfMass"): + if hasattr(s1, "CenterOfMass") and hasattr(s2, "CenterOfMass"): v = s1.CenterOfMass.sub(s2.CenterOfMass) if product.Representation: - r = importIFCHelper.getRotation(product.Representation.Representations[0].Items[0].MappingTarget) + r = importIFCHelper.getRotation( + product.Representation.Representations[0].Items[0].MappingTarget + ) if not r.isNull(): v = v.add(s2.CenterOfMass) v = v.add(r.multVec(s2.CenterOfMass.negative())) obj.Placement.Rotation = r obj.Placement.move(v) else: - print("failed to compute placement ",) + print( + "failed to compute placement ", + ) else: # no clone - obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name) - if freecadtype in ["Wall","Structure"] and baseobj and baseobj.isDerivedFrom("Part::Extrusion"): + obj = getattr(Arch, "make" + freecadtype)(baseobj=baseobj, name=name) + if ( + freecadtype in ["Wall", "Structure"] + and baseobj + and baseobj.isDerivedFrom("Part::Extrusion") + ): # remove intermediary extrusion for types that can extrude themselves # TODO do this check earlier so we do not calculate it, to save time obj.Base = baseobj.Base @@ -666,7 +707,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): obj.Normal = FreeCAD.Vector(baseobj.Dir).normalize() bn = baseobj.Name doc.removeObject(bn) - if (freecadtype in ["Structure","Wall"]) and not baseobj: + if (freecadtype in ["Structure", "Wall"]) and not baseobj: # remove sizes to prevent auto shape creation for types that don't require a base object obj.Height = 0 obj.Width = 0 @@ -690,17 +731,18 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if not obj: # we couldn't make an object of a specific type, use default arch component - obj = Arch.makeComponent(baseobj,name=name) + obj = Arch.makeComponent(baseobj, name=name) # set additional properties obj.Label = name - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") - if hasattr(obj,"Description") and hasattr(product,"Description"): + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") + if hasattr(obj, "Description") and hasattr(product, "Description"): if product.Description: obj.Description = product.Description if FreeCAD.GuiUp and baseobj: try: - if hasattr(baseobj,"ViewObject"): + if hasattr(baseobj, "ViewObject"): baseobj.ViewObject.hide() except ReferenceError: pass @@ -708,25 +750,31 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # setting IFC type try: - if hasattr(obj,"IfcType"): - obj.IfcType = ''.join(map(lambda x: x if x.islower() else " "+x, ptype[3:]))[1:] + if hasattr(obj, "IfcType"): + obj.IfcType = "".join(map(lambda x: x if x.islower() else " " + x, ptype[3:]))[ + 1: + ] except Exception: - print("Unable to give IFC type ",ptype," to object ",obj.Label) + print("Unable to give IFC type ", ptype, " to object ", obj.Label) # setting uid - if hasattr(obj,"IfcData"): + if hasattr(obj, "IfcData"): a = obj.IfcData a["IfcUID"] = str(guid) obj.IfcData = a - # setting IFC attributes + # setting IFC attributes for attribute in ArchIFCSchema.IfcProducts[product.is_a()]["attributes"]: if attribute["name"] == "Name": continue # print("attribute:",attribute["name"]) - if hasattr(product, attribute["name"]) and getattr(product, attribute["name"]) and hasattr(obj,attribute["name"]): + if ( + hasattr(product, attribute["name"]) + and getattr(product, attribute["name"]) + and hasattr(obj, attribute["name"]) + ): # print("Setting attribute",attribute["name"],"to",getattr(product, attribute["name"])) setattr(obj, attribute["name"], getattr(product, attribute["name"])) # TODO: ArchIFCSchema.IfcProducts uses the IFC version from the FreeCAD prefs. @@ -734,76 +782,90 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if obj: # print number of solids - if preferences['DEBUG']: + if preferences["DEBUG"]: s = "" - if hasattr(obj,"Shape"): + if hasattr(obj, "Shape"): if obj.Shape.Solids: - s = str(len(obj.Shape.Solids))+" solids" - print(s,end="") + s = str(len(obj.Shape.Solids)) + " solids" + print(s, end="") objects[pid] = obj - elif (preferences['MERGE_MODE_ARCH'] == 1 and archobj) or (preferences['MERGE_MODE_STRUCT'] == 0 and not archobj): + elif (preferences["MERGE_MODE_ARCH"] == 1 and archobj) or ( + preferences["MERGE_MODE_STRUCT"] == 0 and not archobj + ): # non-parametric BIM objects (just Arch components with a shape) - if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]: - for freecadtype,ifctypes in typesmap.items(): + if ptype in ["IfcSite", "IfcBuilding", "IfcBuildingStorey"]: + for freecadtype, ifctypes in typesmap.items(): if ptype in ifctypes: - obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name) - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") + obj = getattr(Arch, "make" + freecadtype)(baseobj=baseobj, name=name) + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") if ptype == "IfcBuildingStorey": if product.Elevation: obj.Placement.Base.z = product.Elevation * ifcscale elif baseobj: - obj = Arch.makeComponent(baseobj,name=name,delete=True) + obj = Arch.makeComponent(baseobj, name=name, delete=True) obj.Label = name - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") - if hasattr(obj,"Description") and hasattr(product,"Description"): + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") + if hasattr(obj, "Description") and hasattr(product, "Description"): if product.Description: obj.Description = product.Description try: - if hasattr(obj,"IfcType"): - obj.IfcType = ''.join(map(lambda x: x if x.islower() else " "+x, ptype[3:]))[1:] + if hasattr(obj, "IfcType"): + obj.IfcType = "".join( + map(lambda x: x if x.islower() else " " + x, ptype[3:]) + )[1:] except Exception: - print("Unable to give IFC type ",ptype," to object ",obj.Label) - if hasattr(obj,"IfcData"): + print("Unable to give IFC type ", ptype, " to object ", obj.Label) + if hasattr(obj, "IfcData"): a = obj.IfcData a["IfcUID"] = str(guid) obj.IfcData = a elif pid in additions: # no baseobj but in additions, thus we make a BuildingPart container - obj = getattr(Arch,"makeBuildingPart")(name=name) - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") + obj = getattr(Arch, "makeBuildingPart")(name=name) + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") else: - if preferences['DEBUG']: print(": skipped.") + if preferences["DEBUG"]: + print(": skipped.") continue if obj and hasattr(obj, "GlobalId"): obj.GlobalId = guid - elif (preferences['MERGE_MODE_ARCH'] == 2 and archobj) or (preferences['MERGE_MODE_STRUCT'] == 1 and not archobj): + elif (preferences["MERGE_MODE_ARCH"] == 2 and archobj) or ( + preferences["MERGE_MODE_STRUCT"] == 1 and not archobj + ): # Part shapes - if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]: - for freecadtype,ifctypes in typesmap.items(): + if ptype in ["IfcSite", "IfcBuilding", "IfcBuildingStorey"]: + for freecadtype, ifctypes in typesmap.items(): if ptype in ifctypes: - obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name) - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") + obj = getattr(Arch, "make" + freecadtype)(baseobj=baseobj, name=name) + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") if ptype == "IfcBuildingStorey": if product.Elevation: obj.Placement.Base.z = product.Elevation * ifcscale elif baseobj: - obj = doc.addObject("Part::Feature",name) + obj = doc.addObject("Part::Feature", name) obj.Shape = shape elif pid in additions: # no baseobj but in additions, thus we make a BuildingPart container - obj = getattr(Arch,"makeBuildingPart")(name=name) - if preferences['DEBUG']: print(": "+obj.Label+" ",end="") + obj = getattr(Arch, "makeBuildingPart")(name=name) + if preferences["DEBUG"]: + print(": " + obj.Label + " ", end="") else: - if preferences['DEBUG']: print(": skipped.") + if preferences["DEBUG"]: + print(": skipped.") continue - if preferences['DEBUG']: print("") # newline for debug prints, print for a new object should be on a new line + if preferences["DEBUG"]: + print("") # newline for debug prints, print for a new object should be on a new line if obj: @@ -814,54 +876,81 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if psets: - if preferences['IMPORT_PROPERTIES'] and hasattr(obj,"IfcProperties"): + if preferences["IMPORT_PROPERTIES"] and hasattr(obj, "IfcProperties"): # treat as spreadsheet (pref option) - if isinstance(obj.IfcProperties,dict): + if isinstance(obj.IfcProperties, dict): # fix property type if needed obj.removeProperty("IfcProperties") - obj.addProperty("App::PropertyLink","IfcProperties","Component","Stores IFC properties as a spreadsheet", locked=True) + obj.addProperty( + "App::PropertyLink", + "IfcProperties", + "Component", + "Stores IFC properties as a spreadsheet", + locked=True, + ) ifc_spreadsheet = Arch.makeIfcSpreadsheet() n = 2 for c in psets.keys(): o = ifcfile[c] - if preferences['DEBUG']: print("propertyset Name",o.Name,type(o.Name)) + if preferences["DEBUG"]: + print("propertyset Name", o.Name, type(o.Name)) catname = o.Name for p in psets[c]: l = ifcfile[p] lname = l.Name if l.is_a("IfcPropertySingleValue"): - if preferences['DEBUG']: - print("property name",l.Name,type(l.Name)) - ifc_spreadsheet.set(str('A'+str(n)), catname) - ifc_spreadsheet.set(str('B'+str(n)), lname) + if preferences["DEBUG"]: + print("property name", l.Name, type(l.Name)) + ifc_spreadsheet.set(str("A" + str(n)), catname) + ifc_spreadsheet.set(str("B" + str(n)), lname) if l.NominalValue: - if preferences['DEBUG']: - print("property NominalValue",l.NominalValue.is_a(),type(l.NominalValue.is_a())) - print("property NominalValue.wrappedValue",l.NominalValue.wrappedValue,type(l.NominalValue.wrappedValue)) + if preferences["DEBUG"]: + print( + "property NominalValue", + l.NominalValue.is_a(), + type(l.NominalValue.is_a()), + ) + print( + "property NominalValue.wrappedValue", + l.NominalValue.wrappedValue, + type(l.NominalValue.wrappedValue), + ) # print("l.NominalValue.Unit",l.NominalValue.Unit,type(l.NominalValue.Unit)) - ifc_spreadsheet.set(str('C'+str(n)), l.NominalValue.is_a()) - if l.NominalValue.is_a() in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']: - ifc_spreadsheet.set(str('D'+str(n)), "'" + str(l.NominalValue.wrappedValue)) + ifc_spreadsheet.set(str("C" + str(n)), l.NominalValue.is_a()) + if l.NominalValue.is_a() in [ + "IfcLabel", + "IfcText", + "IfcIdentifier", + "IfcDescriptiveMeasure", + ]: + ifc_spreadsheet.set( + str("D" + str(n)), + "'" + str(l.NominalValue.wrappedValue), + ) else: - ifc_spreadsheet.set(str('D'+str(n)), str(l.NominalValue.wrappedValue)) - if hasattr(l.NominalValue,'Unit'): - ifc_spreadsheet.set(str('E'+str(n)), str(l.NominalValue.Unit)) + ifc_spreadsheet.set( + str("D" + str(n)), str(l.NominalValue.wrappedValue) + ) + if hasattr(l.NominalValue, "Unit"): + ifc_spreadsheet.set( + str("E" + str(n)), str(l.NominalValue.Unit) + ) n += 1 obj.IfcProperties = ifc_spreadsheet - elif hasattr(obj,"IfcProperties") and isinstance(obj.IfcProperties,dict): + elif hasattr(obj, "IfcProperties") and isinstance(obj.IfcProperties, dict): # 0.18 behaviour: properties are saved as pset;;type;;value in IfcProperties d = obj.IfcProperties obj.IfcProperties = importIFCHelper.getIfcProperties(ifcfile, pid, psets, d) - elif hasattr(obj,"IfcData"): + elif hasattr(obj, "IfcData"): # 0.17: properties are saved as type(value) in IfcData @@ -870,7 +959,9 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for p in psets[c]: l = ifcfile[p] if l.is_a("IfcPropertySingleValue"): - a[l.Name.encode("utf8")] = str(l.NominalValue) # no py3 support here + a[l.Name.encode("utf8")] = str( + l.NominalValue + ) # no py3 support here obj.IfcData = a # color @@ -880,13 +971,14 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if FreeCAD.GuiUp: # if preferences['DEBUG']: # print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255)) - if hasattr(obj.ViewObject,"ShapeColor"): + if hasattr(obj.ViewObject, "ShapeColor"): obj.ViewObject.ShapeColor = tuple(colors[pid][0:3]) - if hasattr(obj.ViewObject,"Transparency"): + if hasattr(obj.ViewObject, "Transparency"): obj.ViewObject.Transparency = 1.0 - colors[pid][3] # if preferences['DEBUG'] is on, recompute after each shape - if preferences['DEBUG']: doc.recompute() + if preferences["DEBUG"]: + doc.recompute() # attached 2D elements @@ -917,7 +1009,9 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if product.SiteAddress.PostalCode: obj.PostalCode = product.SiteAddress.PostalCode project = product.Decomposes[0].RelatingObject - modelRC = next((rc for rc in project.RepresentationContexts if rc.ContextType == "Model"), None) + modelRC = next( + (rc for rc in project.RepresentationContexts if rc.ContextType == "Model"), None + ) if modelRC and modelRC.TrueNorth: # If the y-part of TrueNorth is 0, then the x-part should be checked. # Declination would be -90° if x >0 and +90° if x < 0 @@ -925,13 +1019,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # But that would actually be an invalid IFC file, because the magnitude # of the (twodimensional) direction vector for TrueNorth shall be greater than zero. (x, y) = modelRC.TrueNorth.DirectionRatios[:2] - obj.Declination = ((math.degrees(math.atan2(y,x))-90+180) % 360)-180 + obj.Declination = ((math.degrees(math.atan2(y, x)) - 90 + 180) % 360) - 180 if FreeCAD.GuiUp: obj.ViewObject.CompassRotation.Value = obj.Declination try: progressbar.next(True) - except(RuntimeError): + except RuntimeError: print("Aborted.") progressbar.stop() doc.recompute() @@ -940,11 +1034,12 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): progressbar.stop() doc.recompute() - if preferences['MERGE_MODE_STRUCT'] == 2: + if preferences["MERGE_MODE_STRUCT"] == 2: - if preferences['DEBUG']: print("Joining Structural shapes...",end="") + if preferences["DEBUG"]: + print("Joining Structural shapes...", end="") - for host,children in groups.items(): # Structural + for host, children in groups.items(): # Structural if ifcfile[host].is_a("IfcStructuralAnalysisModel"): compound = [] for c in children: @@ -953,26 +1048,29 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): del structshapes[c] if compound: name = ifcfile[host].Name or "AnalysisModel" - if preferences['PREFIX_NUMBERS']: name = "ID" + str(host) + " " + name - obj = doc.addObject("Part::Feature",name) + if preferences["PREFIX_NUMBERS"]: + name = "ID" + str(host) + " " + name + obj = doc.addObject("Part::Feature", name) obj.Label = name obj.Shape = Part.makeCompound(compound) if structshapes: # remaining Structural shapes - obj = doc.addObject("Part::Feature","UnclaimedStruct") + obj = doc.addObject("Part::Feature", "UnclaimedStruct") obj.Shape = Part.makeCompound(structshapes.values()) - if preferences['DEBUG']: print("done") + if preferences["DEBUG"]: + print("done") else: - if preferences['DEBUG']: print("Processing Struct relationships...",end="") + if preferences["DEBUG"]: + print("Processing Struct relationships...", end="") # groups - for host,children in groups.items(): + for host, children in groups.items(): if ifcfile[host].is_a("IfcStructuralAnalysisModel"): # print(host, ' --> ', children) - obj = doc.addObject("App::DocumentObjectGroup","AnalysisModel") + obj = doc.addObject("App::DocumentObjectGroup", "AnalysisModel") objects[host] = obj if host in objects: cobs = [] @@ -984,13 +1082,16 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for c in childs_to_delete: children.remove(c) # to not process the child again in remaining groups if cobs: - if preferences['DEBUG']: print("adding ",len(cobs), " object(s) to ", objects[host].Label) - Arch.addComponents(cobs,objects[host]) - if preferences['DEBUG']: doc.recompute() + if preferences["DEBUG"]: + print("adding ", len(cobs), " object(s) to ", objects[host].Label) + Arch.addComponents(cobs, objects[host]) + if preferences["DEBUG"]: + doc.recompute() - if preferences['DEBUG']: print("done") + if preferences["DEBUG"]: + print("done") - if preferences['MERGE_MODE_ARCH'] > 2: # if ArchObj is compound or ArchObj not imported + if preferences["MERGE_MODE_ARCH"] > 2: # if ArchObj is compound or ArchObj not imported doc.recompute() # cleaning bad shapes @@ -1003,14 +1104,19 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): swallowed = [] remaining = {} - for host,children in groups.items(): + for host, children in groups.items(): if ifcfile[host].is_a("IfcGroup"): if ifcfile[host].Name: grp_name = ifcfile[host].Name else: - if preferences['DEBUG']: print("no group name specified for entity: #", ifcfile[host].id(), ", entity type is used!") + if preferences["DEBUG"]: + print( + "no group name specified for entity: #", + ifcfile[host].id(), + ", entity type is used!", + ) grp_name = ifcfile[host].is_a() + "_" + str(ifcfile[host].id()) - grp = doc.addObject("App::DocumentObjectGroup",grp_name) + grp = doc.addObject("App::DocumentObjectGroup", grp_name) grp.Label = grp_name objects[host] = grp for child in children: @@ -1020,13 +1126,14 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): else: remaining[child] = grp - if preferences['MERGE_MODE_ARCH'] == 3: + if preferences["MERGE_MODE_ARCH"] == 3: # One compound per storey - if preferences['DEBUG']: print("Joining Arch shapes...",end="") + if preferences["DEBUG"]: + print("Joining Arch shapes...", end="") - for host,children in additions.items(): # Arch + for host, children in additions.items(): # Arch if ifcfile[host].is_a("IfcBuildingStorey"): compound = [] for c in children: @@ -1040,63 +1147,82 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): del shapes[c2] if compound: name = ifcfile[host].Name or "Floor" - if preferences['PREFIX_NUMBERS']: name = "ID" + str(host) + " " + name - obj = doc.addObject("Part::Feature",name) + if preferences["PREFIX_NUMBERS"]: + name = "ID" + str(host) + " " + name + obj = doc.addObject("Part::Feature", name) obj.Label = name obj.Shape = Part.makeCompound(compound) if shapes: # remaining Arch shapes - obj = doc.addObject("Part::Feature","UnclaimedArch") + obj = doc.addObject("Part::Feature", "UnclaimedArch") obj.Shape = Part.makeCompound(shapes.values()) - if preferences['DEBUG']: print("done") + if preferences["DEBUG"]: + print("done") else: - if preferences['DEBUG']: print("Processing Arch relationships...",end="") + if preferences["DEBUG"]: + print("Processing Arch relationships...", end="") first = True # subtractions - if preferences['SEPARATE_OPENINGS']: + if preferences["SEPARATE_OPENINGS"]: for subtraction in subtractions: if (subtraction[0] in objects) and (subtraction[1] in objects): - if preferences['DEBUG'] and first: + if preferences["DEBUG"] and first: print("") first = False - if preferences['DEBUG']: print(" subtracting",objects[subtraction[0]].Label, "from", objects[subtraction[1]].Label) - Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]]) - if preferences['DEBUG']: doc.recompute() + if preferences["DEBUG"]: + print( + " subtracting", + objects[subtraction[0]].Label, + "from", + objects[subtraction[1]].Label, + ) + Arch.removeComponents(objects[subtraction[0]], objects[subtraction[1]]) + if preferences["DEBUG"]: + doc.recompute() # additions - for host,children in additions.items(): + for host, children in additions.items(): if host not in objects: # print(host, 'not used') # print(ifcfile[host]) continue cobs = [] for child in children: - if child in objects \ - and child not in swallowed: # don't add objects already in groups + if ( + child in objects and child not in swallowed + ): # don't add objects already in groups cobs.append(objects[child]) if not cobs: continue - if preferences['DEBUG'] and first: + if preferences["DEBUG"] and first: print("") first = False if ( - preferences['DEBUG'] + preferences["DEBUG"] and (len(cobs) > 10) - and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart","Project"])) + and ( + not ( + Draft.getType(objects[host]) + in ["Site", "Building", "Floor", "BuildingPart", "Project"] + ) + ) ): # avoid huge fusions print("more than 10 shapes to add: skipping.") else: - if preferences['DEBUG']: print(" adding",len(cobs), "object(s) to", objects[host].Label) - Arch.addComponents(cobs,objects[host]) - if preferences['DEBUG']: doc.recompute() + if preferences["DEBUG"]: + print(" adding", len(cobs), "object(s) to", objects[host].Label) + Arch.addComponents(cobs, objects[host]) + if preferences["DEBUG"]: + doc.recompute() - if preferences['DEBUG'] and first: print("done.") + if preferences["DEBUG"] and first: + print("done.") doc.recompute() @@ -1104,15 +1230,15 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for obj in objects.values(): if obj.isDerivedFrom("Part::Feature"): - if obj.Shape.isNull() and not(Draft.getType(obj) in ["Site","Project"]): + if obj.Shape.isNull() and not (Draft.getType(obj) in ["Site", "Project"]): Arch.rebuildArchShape(obj) doc.recompute() # 2D elements - if preferences['DEBUG'] and annotations: - print("Processing",len(annotations),"2D objects...") + if preferences["DEBUG"] and annotations: + print("Processing", len(annotations), "2D objects...") prodcount = count count = 0 @@ -1122,16 +1248,21 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): aid = annotation.id() count += 1 - if preferences['DEBUG']: print(count,"/",len(annotations),"object #"+str(aid),":",annotation.is_a(),end="") + if preferences["DEBUG"]: + print( + count, "/", len(annotations), "object #" + str(aid), ":", annotation.is_a(), end="" + ) if aid in skip: - if preferences['DEBUG']: print(", skipped.") + if preferences["DEBUG"]: + print(", skipped.") continue # user given id skip list - if annotation.is_a() in preferences['SKIP']: - if preferences['DEBUG']: print(", skipped.") + if annotation.is_a() in preferences["SKIP"]: + if preferences["DEBUG"]: + print(", skipped.") continue # preferences-set type skip list - anno = importIFCHelper.createAnnotation(annotation,doc,ifcscale,preferences) + anno = importIFCHelper.createAnnotation(annotation, doc, ifcscale, preferences) # placing in container if needed @@ -1139,17 +1270,19 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if aid in remaining: remaining[aid].addObject(anno) else: - for host,children in additions.items(): + for host, children in additions.items(): if (aid in children) and (host in objects): - Arch.addComponents(anno,objects[host]) + Arch.addComponents(anno, objects[host]) - if preferences['DEBUG']: print("") # add newline for 2D objects debug prints + if preferences["DEBUG"]: + print("") # add newline for 2D objects debug prints doc.recompute() # Materials - if preferences['DEBUG'] and materials: print("Creating materials...",end="") + if preferences["DEBUG"] and materials: + print("Creating materials...", end="") # print("\n") # print("colors:",colors) # print("mattable:",mattable) @@ -1180,21 +1313,23 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if material.id() in colors and colors[material.id()] is not None: mat_color = str(colors[material.id()]) else: - for o,m in mattable.items(): + for o, m in mattable.items(): if m == material.id(): if o in colors and colors[o] is not None: mat_color = str(colors[o]) if mat_color is not None: mdict["DiffuseColor"] = mat_color else: - if preferences['DEBUG']: print("/n no color for material: {}, ".format(str(material.id)),end="") + if preferences["DEBUG"]: + print("/n no color for material: {}, ".format(str(material.id)), end="") # merge materials with same name and color if setting in prefs is True add_material = True if preferences["MERGE_MATERIALS"]: for added_mat in added_mats: if ( - "Description" in added_mat.Material # Description has been set thus it is in mdict + "Description" + in added_mat.Material # Description has been set thus it is in mdict and added_mat.Material["Description"] == mdict["Description"] ): if ( @@ -1203,10 +1338,8 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): and "DiffuseColor" in mdict and added_mat.Material["DiffuseColor"] == mdict["DiffuseColor"] ) # color in added mat with the same matname and new mat is the same - or - ( - "DiffuseColor" not in added_mat.Material - and "DiffuseColor" not in mdict + or ( + "DiffuseColor" not in added_mat.Material and "DiffuseColor" not in mdict ) # there is no color in added mat with the same matname and new mat # on model imported from ArchiCAD color was not found for all IFC material objects, # thus DiffuseColor was not set for created materials, workaround to merge these too @@ -1221,10 +1354,10 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): matobj.Material = mdict added_mats.append(matobj) # fill material attribute of the objects - for o,m in mattable.items(): + for o, m in mattable.items(): if m == material.id(): if o in objects: - if hasattr(objects[o],"Material"): + if hasattr(objects[o], "Material"): objects[o].Material = matobj if FreeCAD.GuiUp: # the reason behind ... @@ -1235,22 +1368,33 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # https://forum.freecad.org/viewtopic.php?f=39&t=38440 col = objects[o].ViewObject.ShapeColor[:3] dig = 5 - ma_color = sh_color = round(col[0], dig), round(col[1], dig), round(col[2], dig) + ma_color = sh_color = ( + round(col[0], dig), + round(col[1], dig), + round(col[2], dig), + ) if "DiffuseColor" in objects[o].Material.Material: string_color = objects[o].Material.Material["DiffuseColor"] col = tuple([float(f) for f in string_color.strip("()").split(",")]) - ma_color = round(col[0], dig), round(col[1], dig), round(col[2], dig) + ma_color = ( + round(col[0], dig), + round(col[1], dig), + round(col[2], dig), + ) if ma_color != sh_color: print("\nobject color != material color for object: ", o) print(" material color is used (most software uses shape color)") - print(" obj: ", o, "label: ", objects[o].Label, " col: ", sh_color) + print( + " obj: ", o, "label: ", objects[o].Label, " col: ", sh_color + ) print(" mat: ", m, "label: ", matobj.Label, " col: ", ma_color) # print(" ", ifcfile[o]) # print(" ", ifcfile[m]) # print(" colors:") # print(" ", o, ": ", colors[o]) # print(" ", m, ": ", colors[m]) - if preferences['DEBUG'] and materials: print("done") + if preferences["DEBUG"] and materials: + print("done") # Grouping everything if required @@ -1259,17 +1403,18 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # the pure objects do not belong to any container, they will be added here # if after Layer they are linked by Layer and will not be added here if preferences["REPLACE_PROJECT"] and filename: - rootgroup = doc.addObject("App::DocumentObjectGroup","Group") + rootgroup = doc.addObject("App::DocumentObjectGroup", "Group") rootgroup.Label = os.path.basename(filename) # print(objects) - for key,obj in objects.items(): + for key, obj in objects.items(): # only add top-level objects if not obj.InList: rootgroup.addObject(obj) # Layers - if preferences['DEBUG'] and layers: print("Creating layers...", end="") + if preferences["DEBUG"] and layers: + print("Creating layers...", end="") # print(layers) for layer_name, layer_objects in layers.items(): if preferences["IMPORT_LAYER"] is False: @@ -1287,18 +1432,20 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): lay_grp.append(objects[lobj_id]) lay.Group = lay_grp doc.recompute() - if preferences['DEBUG'] and layers: print("done") + if preferences["DEBUG"] and layers: + print("done") # restore links from full parametric definitions for p in parametrics: l = doc.getObject(p[2]) if l: - setattr(p[0],p[1],l) + setattr(p[0], p[1], l) # Save colordict in non-GUI mode if colordict and not FreeCAD.GuiUp: import json + d = doc.Meta d["colordict"] = json.dumps(colordict) doc.Meta = d @@ -1308,13 +1455,15 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if FreeCAD.GuiUp and ZOOMOUT: Gui.SendMsgToActiveView("ViewFit") - endtime = time.time()-starttime + endtime = time.time() - starttime if filesize: - _msg("Finished importing {0} MB " - "in {1} seconds, or {2} s/MB".format(round(filesize, 1), - int(endtime), - int(endtime/filesize))) + _msg( + "Finished importing {0} MB " + "in {1} seconds, or {2} s/MB".format( + round(filesize, 1), int(endtime), int(endtime / filesize) + ) + ) else: _msg("Finished importing in {} seconds".format(int(endtime))) diff --git a/src/Mod/BIM/importers/importIFCHelper.py b/src/Mod/BIM/importers/importIFCHelper.py index e06c0110ed..85142c4a59 100644 --- a/src/Mod/BIM/importers/importIFCHelper.py +++ b/src/Mod/BIM/importers/importIFCHelper.py @@ -36,14 +36,16 @@ if FreeCAD.GuiUp: import FreeCADGui as Gui -PREDEFINED_RGB = {"black": (0, 0, 0), - "red": (1.0, 0, 0), - "green": (0, 1.0, 0), - "blue": (0, 0, 1.0), - "yellow": (1.0, 1.0, 0), - "magenta": (1.0, 0, 1.0), - "cyan": (0, 1.0, 1.0), - "white": (1.0, 1.0, 1.0)} +PREDEFINED_RGB = { + "black": (0, 0, 0), + "red": (1.0, 0, 0), + "green": (0, 1.0, 0), + "blue": (0, 0, 1.0), + "yellow": (1.0, 1.0, 0), + "magenta": (1.0, 0, 1.0), + "cyan": (0, 1.0, 1.0), + "white": (1.0, 1.0, 1.0), +} DEBUG_prod_repr = False @@ -63,9 +65,7 @@ def dd2dms(dd): if dd < 0: degrees = -degrees - return (int(degrees) * sign, - int(minutes) * sign, - int(seconds) * sign) + return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign) def dms2dd(degrees, minutes, seconds, milliseconds=0): @@ -73,7 +73,7 @@ def dms2dd(degrees, minutes, seconds, milliseconds=0): Used in import. """ - dd = float(degrees) + float(minutes)/60 + float(seconds)/3600 + dd = float(degrees) + float(minutes) / 60 + float(seconds) / 3600 return dd @@ -90,30 +90,32 @@ def getPreferences(): Gui.showPreferencesByName("Import-Export", ":/ui/preferences-ifc.ui") preferences = { - 'DEBUG': params.get_param_arch("ifcDebug"), - 'PREFIX_NUMBERS': params.get_param_arch("ifcPrefixNumbers"), - 'SKIP': params.get_param_arch("ifcSkip").split(","), - 'SEPARATE_OPENINGS': params.get_param_arch("ifcSeparateOpenings"), - 'ROOT_ELEMENT': params.get_param_arch("ifcRootElement"), - 'GET_EXTRUSIONS': params.get_param_arch("ifcGetExtrusions"), - 'MERGE_MATERIALS': params.get_param_arch("ifcMergeMaterials"), - 'MERGE_MODE_ARCH': params.get_param_arch("ifcImportModeArch"), - 'MERGE_MODE_STRUCT': params.get_param_arch("ifcImportModeStruct"), - 'CREATE_CLONES': params.get_param_arch("ifcCreateClones"), - 'IMPORT_PROPERTIES': params.get_param_arch("ifcImportProperties"), - 'SPLIT_LAYERS': params.get_param_arch("ifcSplitLayers"), # wall layer, not layer for visual props - 'FITVIEW_ONIMPORT': params.get_param_arch("ifcFitViewOnImport"), - 'ALLOW_INVALID': params.get_param_arch("ifcAllowInvalid"), - 'REPLACE_PROJECT': params.get_param_arch("ifcReplaceProject"), - 'MULTICORE': params.get_param_arch("ifcMulticore"), - 'IMPORT_LAYER': params.get_param_arch("ifcImportLayer") + "DEBUG": params.get_param_arch("ifcDebug"), + "PREFIX_NUMBERS": params.get_param_arch("ifcPrefixNumbers"), + "SKIP": params.get_param_arch("ifcSkip").split(","), + "SEPARATE_OPENINGS": params.get_param_arch("ifcSeparateOpenings"), + "ROOT_ELEMENT": params.get_param_arch("ifcRootElement"), + "GET_EXTRUSIONS": params.get_param_arch("ifcGetExtrusions"), + "MERGE_MATERIALS": params.get_param_arch("ifcMergeMaterials"), + "MERGE_MODE_ARCH": params.get_param_arch("ifcImportModeArch"), + "MERGE_MODE_STRUCT": params.get_param_arch("ifcImportModeStruct"), + "CREATE_CLONES": params.get_param_arch("ifcCreateClones"), + "IMPORT_PROPERTIES": params.get_param_arch("ifcImportProperties"), + "SPLIT_LAYERS": params.get_param_arch( + "ifcSplitLayers" + ), # wall layer, not layer for visual props + "FITVIEW_ONIMPORT": params.get_param_arch("ifcFitViewOnImport"), + "ALLOW_INVALID": params.get_param_arch("ifcAllowInvalid"), + "REPLACE_PROJECT": params.get_param_arch("ifcReplaceProject"), + "MULTICORE": params.get_param_arch("ifcMulticore"), + "IMPORT_LAYER": params.get_param_arch("ifcImportLayer"), } - if preferences['MERGE_MODE_ARCH'] > 0: - preferences['SEPARATE_OPENINGS'] = False - preferences['GET_EXTRUSIONS'] = False - if not preferences['SEPARATE_OPENINGS']: - preferences['SKIP'].append("IfcOpeningElement") + if preferences["MERGE_MODE_ARCH"] > 0: + preferences["SEPARATE_OPENINGS"] = False + preferences["GET_EXTRUSIONS"] = False + if not preferences["SEPARATE_OPENINGS"]: + preferences["SKIP"].append("IfcOpeningElement") return preferences @@ -148,7 +150,9 @@ class ProjectImporter: # this class should be derived from that class to inherit # this method; otherwise a simple function (not tied to a class) # should be used. - ArchIFC.IfcRoot.setObjIfcComplexAttributeValue(self, self.object, "RepresentationContexts", data) + ArchIFC.IfcRoot.setObjIfcComplexAttributeValue( + self, self.object, "RepresentationContexts", data + ) except Exception: # This scenario occurs validly in IFC2X3, # as the mapConversion does not exist @@ -161,7 +165,7 @@ class ProjectImporter: "geodetic_datum": "GeodeticDatum", "vertical_datum": "VerticalDatum", "map_projection": "MapProjection", - "map_zone": "MapZone" + "map_zone": "MapZone", } data = {} for attributeName, ifcName in mappings.items(): @@ -181,21 +185,22 @@ class ProjectImporter: "orthogonal_height": "OrthogonalHeight", "x_axis_abscissa": "XAxisAbscissa", "x_axis_ordinate": "XAxisOrdinate", - "scale": "Scale" + "scale": "Scale", } data = {} for attributeName, ifcName in mappings.items(): data[attributeName] = str(getattr(mapConversion, ifcName)) - data["true_north"] = str(self.calculateTrueNorthAngle(mapConversion.XAxisAbscissa, - mapConversion.XAxisOrdinate)) + data["true_north"] = str( + self.calculateTrueNorthAngle(mapConversion.XAxisAbscissa, mapConversion.XAxisOrdinate) + ) return data def calculateTrueNorthAngle(self, x, y): return round(math.degrees(math.atan2(y, x)) - 90, 6) -def buildRelProductsAnnotations(ifcfile, root_element='IfcProduct'): +def buildRelProductsAnnotations(ifcfile, root_element="IfcProduct"): """Build the products and annotations relation table.""" products = ifcfile.by_type(root_element) @@ -225,8 +230,7 @@ def buildRelProductRepresentation(ifcfile): for p in ifcfile.by_type("IfcProduct"): if hasattr(p, "Representation") and p.Representation: if DEBUG_prod_repr: - _msg("{}: {}, {}, '{}'".format(i, p.id(), - p.is_a(), p.Name)) + _msg("{}: {}, {}, '{}'".format(i, p.id(), p.is_a(), p.Name)) for it in p.Representation.Representations: for it1 in it.Items: @@ -234,7 +238,9 @@ def buildRelProductRepresentation(ifcfile): if it1.is_a("IfcBooleanResult"): prodrepr.setdefault(p.id(), []).append(it1.FirstOperand.id()) elif it.Items[0].is_a("IfcMappedItem"): - prodrepr.setdefault(p.id(), []).append(it1.MappingSource.MappedRepresentation.id()) + prodrepr.setdefault(p.id(), []).append( + it1.MappingSource.MappedRepresentation.id() + ) if it1.MappingSource.MappedRepresentation.is_a("IfcShapeRepresentation"): for it2 in it1.MappingSource.MappedRepresentation.Items: prodrepr.setdefault(p.id(), []).append(it2.id()) @@ -247,7 +253,9 @@ def buildRelAdditions(ifcfile): additions = {} # { host:[child,...], ... } for r in ifcfile.by_type("IfcRelContainedInSpatialStructure"): - additions.setdefault(r.RelatingStructure.id(), []).extend([e.id() for e in r.RelatedElements]) + additions.setdefault(r.RelatingStructure.id(), []).extend( + [e.id() for e in r.RelatedElements] + ) for r in ifcfile.by_type("IfcRelAggregates"): additions.setdefault(r.RelatingObject.id(), []).extend([e.id() for e in r.RelatedObjects]) @@ -290,7 +298,9 @@ def buildRelMattable(ifcfile): elif r.RelatingMaterial.is_a("IfcMaterialLayerSet"): mattable[o.id()] = r.RelatingMaterial.MaterialLayers[0].Material.id() elif r.RelatingMaterial.is_a("IfcMaterialLayerSetUsage"): - mattable[o.id()] = r.RelatingMaterial.ForLayerSet.MaterialLayers[0].Material.id() + mattable[o.id()] = r.RelatingMaterial.ForLayerSet.MaterialLayers[ + 0 + ].Material.id() return mattable @@ -322,15 +332,13 @@ def buildRelColors(ifcfile, prodrepr): if style2.is_a("IfcSurfaceStyleRendering"): if style2.SurfaceColour: c = style2.SurfaceColour - style_color_rgb[r.id()] = (c.Red, - c.Green, - c.Blue) + style_color_rgb[r.id()] = (c.Red, c.Green, c.Blue) # Nova # FIXME: style_entity_id = { style_entity_id: product_id } not material_id ??? # see https://forum.freecad.org/viewtopic.php?f=39&t=37940&start=10#p329491 # last code change in these color code https://github.com/FreeCAD/FreeCAD/commit/2d1f6ab1 - ''' + """ if r.Item: # print(r.id()) # print(r.Item) # IfcRepresentationItem or IfcShapeRepresentation @@ -339,7 +347,7 @@ def buildRelColors(ifcfile, prodrepr): style_material_id[r.id()] = p # print(p) # print(ifcfile[p]) # product - ''' + """ # A much faster version for Nova style_material_id with product_ids # no material colors, Nova ifc files often do not have materials at all @@ -398,10 +406,11 @@ def buildRelProductColors(ifcfile, prodrepr): repr_item = _body.Items[0] if DEBUG_prod_colors: - _msg("{}: {}, {}, '{}', rep_item {}".format(i, ifcfile[p].id(), - ifcfile[p].is_a(), - ifcfile[p].Name, - repr_item)) + _msg( + "{}: {}, {}, '{}', rep_item {}".format( + i, ifcfile[p].id(), ifcfile[p].is_a(), ifcfile[p].Name, repr_item + ) + ) # Get the geometric representations which have a presentation style. # All representation items have the inverse attribute `StyledByItem` # for this. @@ -443,7 +452,7 @@ def getColorFromMaterial(material): if material.HasRepresentation: rep = material.HasRepresentation[0] - if hasattr(rep,"Representations") and rep.Representations: + if hasattr(rep, "Representations") and rep.Representations: rep = rep.Representations[0] if rep.is_a("IfcStyledRepresentation"): return getColorFromStyledItem(rep) @@ -456,9 +465,9 @@ def color2colorRGB(color_data): return None color_rgb = [ - int(round(color_data[0]*255, 0)), - int(round(color_data[1]*255, 0)), - int(round(color_data[2]*255, 0)) + int(round(color_data[0] * 255, 0)), + int(round(color_data[1] * 255, 0)), + int(round(color_data[2] * 255, 0)), ] # int(159.99) would return 159 not 160, thus round return color_rgb @@ -517,7 +526,7 @@ def getColorFromStyledItem(styled_item): # see https://forum.freecad.org/viewtopic.php?f=39&t=33560&p=437056#p437056 # Get the `IfcPresentationStyleAssignment`, there should only be one, - if styled_item.Styles[0].is_a('IfcPresentationStyleAssignment'): + if styled_item.Styles[0].is_a("IfcPresentationStyleAssignment"): assign_style = styled_item.Styles[0] else: # `IfcPresentationStyleAssignment` is deprecated in IFC4, @@ -535,13 +544,14 @@ def getColorFromStyledItem(styled_item): # `IfcColourRgb` rgb_color = _style.Styles[0].SurfaceColour # print(rgb_color) - if (_style.Styles[0].is_a('IfcSurfaceStyleShading') - and hasattr(_style.Styles[0], 'Transparency') - and _style.Styles[0].Transparency): + if ( + _style.Styles[0].is_a("IfcSurfaceStyleShading") + and hasattr(_style.Styles[0], "Transparency") + and _style.Styles[0].Transparency + ): transparency = _style.Styles[0].Transparency * 100 elif assign_style.Styles[0].is_a("IfcCurveStyle"): - if (len(assign_style.Styles) == 2 - and assign_style.Styles[1].is_a("IfcSurfaceStyle")): + if len(assign_style.Styles) == 2 and assign_style.Styles[1].is_a("IfcSurfaceStyle"): # Allplan, new IFC export started in 2017 # `IfcDraughtingPreDefinedColour` # print(assign_style.Styles[0].CurveColour) @@ -556,19 +566,21 @@ def getColorFromStyledItem(styled_item): rgb_color = assign_style.Styles[0].CurveColour if rgb_color: - if rgb_color.is_a('IfcDraughtingPreDefinedColour'): + if rgb_color.is_a("IfcDraughtingPreDefinedColour"): if DEBUG_prod_colors: _msg(" '{}'= ".format(rgb_color.Name)) col = predefined_to_rgb(rgb_color) if col: - col = col + (0, ) + col = col + (0,) else: - col = (rgb_color.Red, - rgb_color.Green, - rgb_color.Blue, - int(transparency) if transparency else 0) + col = ( + rgb_color.Red, + rgb_color.Green, + rgb_color.Blue, + int(transparency) if transparency else 0, + ) else: col = None @@ -588,13 +600,16 @@ def predefined_to_rgb(rgb_color): if name not in PREDEFINED_RGB: _wrn("Color name not in 'IfcDraughtingPreDefinedColour'.") - if name == 'by layer': - _wrn("'IfcDraughtingPreDefinedColour' set 'by layer'; " - "currently not handled, set to 'None'.") + if name == "by layer": + _wrn( + "'IfcDraughtingPreDefinedColour' set 'by layer'; " + "currently not handled, set to 'None'." + ) return None return PREDEFINED_RGB[name] + # ************************************************************************************************ # property related methods @@ -629,7 +644,7 @@ def getIfcPropertySets(ifcfile, pid): for rel in ifcfile[pid].IsDefinedBy: # the following if condition is needed in IFC2x3 only # https://forum.freecad.org/viewtopic.php?f=39&t=37892#p322884 - if rel.is_a('IfcRelDefinesByProperties'): + if rel.is_a("IfcRelDefinesByProperties"): props = [] if rel.RelatingPropertyDefinition.is_a("IfcPropertySet"): props.extend([prop.id() for prop in rel.RelatingPropertyDefinition.HasProperties]) @@ -649,20 +664,20 @@ def getIfcProperties(ifcfile, pid, psets, d): if e.is_a("IfcPropertySingleValue"): if e.NominalValue: ptype = e.NominalValue.is_a() - if ptype in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']: + if ptype in ["IfcLabel", "IfcText", "IfcIdentifier", "IfcDescriptiveMeasure"]: pvalue = e.NominalValue.wrappedValue else: pvalue = str(e.NominalValue.wrappedValue) - if hasattr(e.NominalValue,'Unit'): + if hasattr(e.NominalValue, "Unit"): if e.NominalValue.Unit: pvalue += e.NominalValue.Unit - d[pname+";;"+psetname] = ptype+";;"+pvalue + d[pname + ";;" + psetname] = ptype + ";;" + pvalue # print("adding property: ",pname,ptype,pvalue," pset ",psetname) return d def getIfcPsetProperties(ifcfile, pid): - """ directly build the property table from pid and ifcfile for FreeCAD""" + """directly build the property table from pid and ifcfile for FreeCAD""" return getIfcProperties(ifcfile, pid, getIfcPropertySets(ifcfile, pid), {}) @@ -719,36 +734,37 @@ def getRotation(entity): return FreeCAD.Rotation(u, v, w, "ZYX") -def getPlacement(entity,scaling=1000): +def getPlacement(entity, scaling=1000): """returns a placement from the given entity""" if not entity: return None import DraftVecUtils + pl = None if entity.is_a("IfcAxis2Placement3D"): - x = getVector(entity.RefDirection,scaling) - z = getVector(entity.Axis,scaling) + x = getVector(entity.RefDirection, scaling) + z = getVector(entity.Axis, scaling) if x and z: y = z.cross(x) - m = DraftVecUtils.getPlaneRotation(x,y,z) + m = DraftVecUtils.getPlaneRotation(x, y, z) pl = FreeCAD.Placement(m) else: pl = FreeCAD.Placement() - loc = getVector(entity.Location,scaling) + loc = getVector(entity.Location, scaling) if loc: pl.move(loc) elif entity.is_a("IfcAxis2Placement2D"): _wrn("not implemented IfcAxis2Placement2D, ", end="") elif entity.is_a("IfcLocalPlacement"): - pl = getPlacement(entity.PlacementRelTo,1) # original placement - relpl = getPlacement(entity.RelativePlacement,1) # relative transf + pl = getPlacement(entity.PlacementRelTo, 1) # original placement + relpl = getPlacement(entity.RelativePlacement, 1) # relative transf if pl and relpl: pl = pl.multiply(relpl) elif relpl: pl = relpl elif entity.is_a("IfcCartesianPoint"): - loc = getVector(entity,scaling) + loc = getVector(entity, scaling) pl = FreeCAD.Placement() pl.move(loc) if pl: @@ -756,7 +772,7 @@ def getPlacement(entity,scaling=1000): return pl -def getVector(entity,scaling=1000): +def getVector(entity, scaling=1000): """returns a vector from the given entity""" if not entity: @@ -766,18 +782,18 @@ def getVector(entity,scaling=1000): if len(entity.DirectionRatios) == 3: v = FreeCAD.Vector(tuple(entity.DirectionRatios)) else: - v = FreeCAD.Vector(tuple(entity.DirectionRatios+[0])) + v = FreeCAD.Vector(tuple(entity.DirectionRatios + [0])) elif entity.is_a("IfcCartesianPoint"): if len(entity.Coordinates) == 3: v = FreeCAD.Vector(tuple(entity.Coordinates)) else: - v = FreeCAD.Vector(tuple(entity.Coordinates+[0])) + v = FreeCAD.Vector(tuple(entity.Coordinates + [0])) # if v: # v.multiply(scaling) return v -def get2DShape(representation,scaling=1000,notext=False): +def get2DShape(representation, scaling=1000, notext=False): """Returns a shape from a 2D IfcShapeRepresentation if notext is True, no Draft text is created""" @@ -789,13 +805,13 @@ def get2DShape(representation,scaling=1000,notext=False): pts = [] for p in ent.Points: c = p.Coordinates - c = FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) + c = FreeCAD.Vector(c[0], c[1], c[2] if len(c) > 2 else 0) c.multiply(scaling) pts.append(c) return Part.makePolygon(pts) def getRectangle(ent): - return Part.makePlane(ent.XDim,ent.YDim) + return Part.makePlane(ent.XDim, ent.YDim) def getLine(ent): pts = [] @@ -810,16 +826,22 @@ def get2DShape(representation,scaling=1000,notext=False): def getCircle(ent): c = ent.Position.Location.Coordinates - c = FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) + c = FreeCAD.Vector(c[0], c[1], c[2] if len(c) > 2 else 0) c.multiply(scaling) - r = ent.Radius*scaling - return Part.makeCircle(r,c) + r = ent.Radius * scaling + return Part.makeCircle(r, c) def getCurveSet(ent): result = [] - if ent.is_a() in ["IfcGeometricCurveSet","IfcGeometricSet"]: + if ent.is_a() in ["IfcGeometricCurveSet", "IfcGeometricSet"]: elts = ent.Elements - elif ent.is_a() in ["IfcLine","IfcPolyline","IfcCircle","IfcTrimmedCurve","IfcRectangleProfileDef"]: + elif ent.is_a() in [ + "IfcLine", + "IfcPolyline", + "IfcCircle", + "IfcTrimmedCurve", + "IfcRectangleProfileDef", + ]: elts = [ent] else: print("getCurveSet: unhandled entity: ", ent) @@ -839,17 +861,17 @@ def get2DShape(representation,scaling=1000,notext=False): t1 = el.Trim1[0].wrappedValue t2 = el.Trim2[0].wrappedValue if not el.SenseAgreement: - t1,t2 = t2,t1 + t1, t2 = t2, t1 if base.is_a("IfcPolyline"): bc = getPolyline(base) result.append(bc) elif base.is_a("IfcCircle"): bc = getCircle(base) - e = Part.ArcOfCircle(bc.Curve,math.radians(t1),math.radians(t2)).toShape() + e = Part.ArcOfCircle(bc.Curve, math.radians(t1), math.radians(t2)).toShape() d = base.Position.RefDirection.DirectionRatios - v = FreeCAD.Vector(d[0],d[1],d[2] if len(d) > 2 else 0) + v = FreeCAD.Vector(d[0], d[1], d[2] if len(d) > 2 else 0) a = -DraftVecUtils.angle(v) - e.rotate(bc.Curve.Center,FreeCAD.Vector(0,0,1),math.degrees(a)) + e.rotate(bc.Curve.Center, FreeCAD.Vector(0, 0, 1), math.degrees(a)) result.append(e) elif el.is_a("IfcCompositeCurve"): for base in el.Segments: @@ -858,11 +880,11 @@ def get2DShape(representation,scaling=1000,notext=False): result.append(bc) elif base.ParentCurve.is_a("IfcCircle"): bc = getCircle(base.ParentCurve) - e = Part.ArcOfCircle(bc.Curve,math.radians(t1),math.radians(t2)).toShape() + e = Part.ArcOfCircle(bc.Curve, math.radians(t1), math.radians(t2)).toShape() d = base.Position.RefDirection.DirectionRatios - v = FreeCAD.Vector(d[0],d[1],d[2] if len(d) > 2 else 0) + v = FreeCAD.Vector(d[0], d[1], d[2] if len(d) > 2 else 0) a = -DraftVecUtils.angle(v) - e.rotate(bc.Curve.Center,FreeCAD.Vector(0,0,1),math.degrees(a)) + e.rotate(bc.Curve.Center, FreeCAD.Vector(0, 0, 1), math.degrees(a)) result.append(e) elif el.is_a("IfcIndexedPolyCurve"): coords = el.Points.CoordList @@ -870,15 +892,15 @@ def get2DShape(representation,scaling=1000,notext=False): def index2points(segment): pts = [] for i in segment.wrappedValue: - c = coords[i-1] - c = FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) + c = coords[i - 1] + c = FreeCAD.Vector(c[0], c[1], c[2] if len(c) > 2 else 0) c.multiply(scaling) pts.append(c) return pts if not el.Segments: # use all points - verts = [FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) for c in coords] + verts = [FreeCAD.Vector(c[0], c[1], c[2] if len(c) > 2 else 0) for c in coords] verts = [v.multiply(scaling) for v in verts] result.append(Part.makePolygon(verts)) else: @@ -889,7 +911,7 @@ def get2DShape(representation,scaling=1000,notext=False): [p1, p2, p3] = index2points(s) result.append(Part.Arc(p1, p2, p3)) else: - raise RuntimeError("Illegal IfcIndexedPolyCurve segment: "+s.is_a()) + raise RuntimeError("Illegal IfcIndexedPolyCurve segment: " + s.is_a()) else: print("importIFCHelper.getCurveSet: unhandled element: ", el) @@ -898,11 +920,11 @@ def get2DShape(representation,scaling=1000,notext=False): result = [] if representation.is_a("IfcShapeRepresentation"): for item in representation.Items: - if item.is_a() in ["IfcGeometricCurveSet","IfcGeometricSet"]: + if item.is_a() in ["IfcGeometricCurveSet", "IfcGeometricSet"]: result = getCurveSet(item) elif item.is_a("IfcMappedItem"): - preresult = get2DShape(item.MappingSource.MappedRepresentation,scaling) - pla = getPlacement(item.MappingSource.MappingOrigin,scaling) + preresult = get2DShape(item.MappingSource.MappedRepresentation, scaling) + pla = getPlacement(item.MappingSource.MappingOrigin, scaling) rot = getRotation(item.MappingTarget) if pla: if rot.Angle: @@ -922,24 +944,29 @@ def get2DShape(representation,scaling=1000,notext=False): if item.Path == "RIGHT": t.ViewObject.Justification = "Right" # do not return because there might be more than one representation - #return [] # TODO dirty hack... Object creation should not be done here - elif representation.is_a() in ["IfcPolyline","IfcCircle","IfcTrimmedCurve","IfcRectangleProfileDef"]: + # return [] # TODO dirty hack... Object creation should not be done here + elif representation.is_a() in [ + "IfcPolyline", + "IfcCircle", + "IfcTrimmedCurve", + "IfcRectangleProfileDef", + ]: result = getCurveSet(representation) return result def getProfileCenterPoint(sweptsolid): """returns the center point of the profile of an extrusion""" - v = FreeCAD.Vector(0,0,0) - if hasattr(sweptsolid,"SweptArea"): + v = FreeCAD.Vector(0, 0, 0) + if hasattr(sweptsolid, "SweptArea"): profile = get2DShape(sweptsolid.SweptArea) if profile: profile = profile[0] - if hasattr(profile,"CenterOfMass"): + if hasattr(profile, "CenterOfMass"): v = profile.CenterOfMass - elif hasattr(profile,"BoundBox"): + elif hasattr(profile, "BoundBox"): v = profile.BoundBox.Center - if hasattr(sweptsolid,"Position"): + if hasattr(sweptsolid, "Position"): pos = getPlacement(sweptsolid.Position) v = pos.multVec(v) return v @@ -953,16 +980,16 @@ def isRectangle(verts): v2 = verts[2].sub(verts[1]) v3 = verts[3].sub(verts[2]) v4 = verts[0].sub(verts[3]) - if abs(v2.getAngle(v1)-math.pi/2) > 0.01: + if abs(v2.getAngle(v1) - math.pi / 2) > 0.01: return False - if abs(v3.getAngle(v2)-math.pi/2) > 0.01: + if abs(v3.getAngle(v2) - math.pi / 2) > 0.01: return False - if abs(v4.getAngle(v3)-math.pi/2) > 0.01: + if abs(v4.getAngle(v3) - math.pi / 2) > 0.01: return False return True -def createFromProperties(propsets,ifcfile,parametrics): +def createFromProperties(propsets, ifcfile, parametrics): """ Creates a FreeCAD parametric object from a set of properties. """ @@ -987,83 +1014,97 @@ def createFromProperties(propsets,ifcfile,parametrics): otype = None if "FreeCADType" in appset: if "FreeCADName" in appset: - obj = FreeCAD.ActiveDocument.addObject(appset["FreeCADType"],appset["FreeCADName"]) + obj = FreeCAD.ActiveDocument.addObject(appset["FreeCADType"], appset["FreeCADName"]) if "FreeCADAppObject" in appset: - mod,cla = appset["FreeCADAppObject"].split(".") + mod, cla = appset["FreeCADAppObject"].split(".") if "'" in mod: mod = mod.split("'")[-1] if "'" in cla: cla = cla.split("'")[0] import importlib + mod = importlib.import_module(mod) - getattr(mod,cla)(obj) - sets.append(("App",appset)) + getattr(mod, cla)(obj) + sets.append(("App", appset)) if FreeCAD.GuiUp: if guiset: if "FreeCADGuiObject" in guiset: - mod,cla = guiset["FreeCADGuiObject"].split(".") + mod, cla = guiset["FreeCADGuiObject"].split(".") if "'" in mod: mod = mod.split("'")[-1] if "'" in cla: cla = cla.split("'")[0] import importlib + mod = importlib.import_module(mod) - getattr(mod,cla)(obj.ViewObject) - sets.append(("Gui",guiset)) + getattr(mod, cla)(obj.ViewObject) + sets.append(("Gui", guiset)) if obj and sets: - for realm,pset in sets: + for realm, pset in sets: if realm == "App": target = obj else: target = obj.ViewObject - for key,val in pset.items(): + for key, val in pset.items(): if key.startswith("FreeCAD_") or key.startswith("FreeCADGui_"): name = key.split("_")[1] if name in target.PropertiesList: if not target.getEditorMode(name): ptype = target.getTypeIdOfProperty(name) - if ptype in ["App::PropertyString","App::PropertyEnumeration","App::PropertyInteger","App::PropertyFloat"]: - setattr(target,name,val) - elif ptype in ["App::PropertyLength","App::PropertyDistance"]: - setattr(target,name,val*1000) + if ptype in [ + "App::PropertyString", + "App::PropertyEnumeration", + "App::PropertyInteger", + "App::PropertyFloat", + ]: + setattr(target, name, val) + elif ptype in ["App::PropertyLength", "App::PropertyDistance"]: + setattr(target, name, val * 1000) elif ptype == "App::PropertyBool": - if val in [".T.",True]: - setattr(target,name,True) + if val in [".T.", True]: + setattr(target, name, True) else: - setattr(target,name,False) + setattr(target, name, False) elif ptype == "App::PropertyVector": - setattr(target,name,FreeCAD.Vector([float(s) for s in val.split("(")[1].strip(")").split(",")])) + setattr( + target, + name, + FreeCAD.Vector( + [float(s) for s in val.split("(")[1].strip(")").split(",")] + ), + ) elif ptype == "App::PropertyArea": - setattr(target,name,val*1000000) + setattr(target, name, val * 1000000) elif ptype == "App::PropertyPlacement": data = val.split("[")[1].strip("]").split("(") - data = [data[1].split(")")[0],data[2].strip(")")] + data = [data[1].split(")")[0], data[2].strip(")")] v = FreeCAD.Vector([float(s) for s in data[0].split(",")]) r = FreeCAD.Rotation(*[float(s) for s in data[1].split(",")]) - setattr(target,name,FreeCAD.Placement(v,r)) + setattr(target, name, FreeCAD.Placement(v, r)) elif ptype == "App::PropertyLink": link = val.split("_")[1] - parametrics.append([target,name,link]) + parametrics.append([target, name, link]) else: - print("Unhandled FreeCAD property:",name," of type:",ptype) - return obj,parametrics + print("Unhandled FreeCAD property:", name, " of type:", ptype) + return obj, parametrics -def applyColorDict(doc,colordict=None): +def applyColorDict(doc, colordict=None): """applies the contents of a color dict to the objects in the given doc. If no colordict is given, the doc Meta property is searched for a "colordict" entry.""" if not colordict: if "colordict" in doc.Meta: import json + colordict = json.loads(doc.Meta["colordict"]) if colordict: for obj in doc.Objects: if obj.Name in colordict: color = colordict[obj.Name] - if hasattr(obj.ViewObject,"ShapeColor"): + if hasattr(obj.ViewObject, "ShapeColor"): obj.ViewObject.ShapeColor = tuple(color[0:3]) - if hasattr(obj.ViewObject,"Transparency") and (len(color) >= 4): + if hasattr(obj.ViewObject, "Transparency") and (len(color) >= 4): obj.ViewObject.Transparency = 1.0 - color[3] else: print("No valid color dict to apply") @@ -1073,21 +1114,21 @@ def getParents(ifcobj): """finds the parent entities of an IFC entity""" parentlist = [] - if hasattr(ifcobj,"ContainedInStructure"): + if hasattr(ifcobj, "ContainedInStructure"): for rel in ifcobj.ContainedInStructure: parentlist.append(rel.RelatingStructure) - elif hasattr(ifcobj,"Decomposes"): + elif hasattr(ifcobj, "Decomposes"): for rel in ifcobj.Decomposes: if rel.is_a("IfcRelAggregates"): parentlist.append(rel.RelatingObject) return parentlist -def createAnnotation(annotation,doc,ifcscale,preferences): +def createAnnotation(annotation, doc, ifcscale, preferences): """creates an annotation object""" anno = None - aid = annotation.id() + aid = annotation.id() if annotation.is_a("IfcGrid"): axes = [] uvwaxes = () @@ -1099,14 +1140,16 @@ def createAnnotation(annotation,doc,ifcscale,preferences): uvwaxes = uvwaxes + annotation.WAxes for axis in uvwaxes: if axis.AxisCurve: - sh = get2DShape(axis.AxisCurve,ifcscale) + sh = get2DShape(axis.AxisCurve, ifcscale) if sh and (len(sh[0].Vertexes) == 2): # currently only straight axes are supported sh = sh[0] l = sh.Length pl = FreeCAD.Placement() pl.Base = sh.Vertexes[0].Point - pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),sh.Vertexes[-1].Point.sub(sh.Vertexes[0].Point)) - o = Arch.makeAxis(1,l) + pl.Rotation = FreeCAD.Rotation( + FreeCAD.Vector(0, 1, 0), sh.Vertexes[-1].Point.sub(sh.Vertexes[0].Point) + ) + o = Arch.makeAxis(1, l) o.Length = l o.Placement = pl o.CustomNumber = axis.AxisTag @@ -1118,10 +1161,10 @@ def createAnnotation(annotation,doc,ifcscale,preferences): name = annotation.Name if annotation.ObjectPlacement: # https://forum.freecad.org/viewtopic.php?f=39&t=40027 - grid_placement = getPlacement(annotation.ObjectPlacement,scaling=1) - if preferences['PREFIX_NUMBERS']: + grid_placement = getPlacement(annotation.ObjectPlacement, scaling=1) + if preferences["PREFIX_NUMBERS"]: name = "ID" + str(aid) + " " + name - anno = Arch.makeAxisSystem(axes,name) + anno = Arch.makeAxisSystem(axes, name) if grid_placement: anno.Placement = grid_placement print(" axis") @@ -1131,11 +1174,12 @@ def createAnnotation(annotation,doc,ifcscale,preferences): name = annotation.Name if "annotation" not in name.lower(): name = "Annotation " + name - if preferences['PREFIX_NUMBERS']: name = "ID" + str(aid) + " " + name + if preferences["PREFIX_NUMBERS"]: + name = "ID" + str(aid) + " " + name shapes2d = [] for rep in annotation.Representation.Representations: - if rep.RepresentationIdentifier in ["Annotation","FootPrint","Axis"]: - sh = get2DShape(rep,ifcscale) + if rep.RepresentationIdentifier in ["Annotation", "FootPrint", "Axis"]: + sh = get2DShape(rep, ifcscale) if sh in doc.Objects: # dirty hack: get2DShape might return an object directly if non-shape based (texts for ex) anno = sh @@ -1143,14 +1187,15 @@ def createAnnotation(annotation,doc,ifcscale,preferences): shapes2d.extend(sh) if shapes2d: import Part + sh = Part.makeCompound(shapes2d) - #if preferences['DEBUG']: print(" shape") - anno = doc.addObject("Part::Feature",name) + # if preferences['DEBUG']: print(" shape") + anno = doc.addObject("Part::Feature", name) anno.Shape = sh - p = getPlacement(annotation.ObjectPlacement,ifcscale) + p = getPlacement(annotation.ObjectPlacement, ifcscale) if p: # and annotation.is_a("IfcAnnotation"): anno.Placement = p - #else: - #if preferences['DEBUG']: print(" no shape") + # else: + # if preferences['DEBUG']: print(" no shape") return anno diff --git a/src/Mod/BIM/importers/importIFClegacy.py b/src/Mod/BIM/importers/importIFClegacy.py index 97ba9a9ef8..54c9a63311 100644 --- a/src/Mod/BIM/importers/importIFClegacy.py +++ b/src/Mod/BIM/importers/importIFClegacy.py @@ -28,7 +28,7 @@ # # ############################################################################ -__title__="FreeCAD IFC importer" +__title__ = "FreeCAD IFC importer" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" @@ -50,36 +50,70 @@ from draftutils import params from draftutils.translate import translate # config -subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents -SCHEMA = "http://www.steptools.com/support/stdev_docs/ifcbim/ifc4.exp" # only for internal parser -MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files -DEBUG = True # this is only for the python console, this value is overridden when importing through the GUI -SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"] # default. overwritten by the GUI options +subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents +SCHEMA = "http://www.steptools.com/support/stdev_docs/ifcbim/ifc4.exp" # only for internal parser +MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files +DEBUG = True # this is only for the python console, this value is overridden when importing through the GUI +SKIP = [ + "IfcBuildingElementProxy", + "IfcFlowTerminal", + "IfcFurnishingElement", +] # default. overwritten by the GUI options IFCLINE_RE = re.compile(r"#(\d+)[ ]?=[ ]?(.*?)\((.*)\);[\\r]?$") -APPLYFIX = True # if true, the ifcopenshell bug-fixing function is applied when saving files +APPLYFIX = True # if true, the ifcopenshell bug-fixing function is applied when saving files # end config # supported ifc products (export only): -supportedIfcTypes = ["IfcSite", "IfcBuilding", "IfcBuildingStorey", "IfcBeam", "IfcBeamStandardCase", - "IfcChimney", "IfcColumn", "IfcColumnStandardCase", "IfcCovering", "IfcCurtainWall", - "IfcDoor", "IfcDoorStandardCase", "IfcMember", "IfcMemberStandardCase", "IfcPlate", - "IfcPlateStandardCase", "IfcRailing", "IfcRamp", "IfcRampFlight", "IfcRoof", - "IfcSlab", "IfcStair", "IfcStairFlight", "IfcWall","IfcSpace", - "IfcWallStandardCase", "IfcWindow", "IfcWindowStandardCase", "IfcBuildingElementProxy", - "IfcPile", "IfcFooting", "IfcReinforcingBar", "IfcTendon"] +supportedIfcTypes = [ + "IfcSite", + "IfcBuilding", + "IfcBuildingStorey", + "IfcBeam", + "IfcBeamStandardCase", + "IfcChimney", + "IfcColumn", + "IfcColumnStandardCase", + "IfcCovering", + "IfcCurtainWall", + "IfcDoor", + "IfcDoorStandardCase", + "IfcMember", + "IfcMemberStandardCase", + "IfcPlate", + "IfcPlateStandardCase", + "IfcRailing", + "IfcRamp", + "IfcRampFlight", + "IfcRoof", + "IfcSlab", + "IfcStair", + "IfcStairFlight", + "IfcWall", + "IfcSpace", + "IfcWallStandardCase", + "IfcWindow", + "IfcWindowStandardCase", + "IfcBuildingElementProxy", + "IfcPile", + "IfcFooting", + "IfcReinforcingBar", + "IfcTendon", +] # TODO : shading device not supported? -def open(filename,skip=None): + +def open(filename, skip=None): "called when freecad opens a file" docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) doc.Label = docname FreeCAD.ActiveDocument = doc getConfig() - read(filename,skip) + read(filename, skip) return doc -def insert(filename,docname,skip=None): + +def insert(filename, docname, skip=None): "called when freecad wants to import a file" try: doc = FreeCAD.getDocument(docname) @@ -87,9 +121,10 @@ def insert(filename,docname,skip=None): doc = FreeCAD.newDocument(docname) FreeCAD.ActiveDocument = doc getConfig() - read(filename,skip) + read(filename, skip) return doc + def getConfig(): "Gets Arch IFC import preferences" global SKIP, CREATE_IFC_GROUPS, ASMESH, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS, JOINSOLIDS, AGGREGATE_WINDOWS @@ -110,6 +145,7 @@ def getConfig(): if asmeshlist: ASMESH = asmeshlist.split(",") + def getIfcOpenShell(): "locates and imports ifcopenshell" global IFCOPENSHELL5 @@ -121,17 +157,18 @@ def getIfcOpenShell(): try: import ifc_wrapper as IfcImport except ImportError: - FreeCAD.Console.PrintMessage(translate("Arch","Could not locate IfcOpenShell")+"\n") + FreeCAD.Console.PrintMessage(translate("Arch", "Could not locate IfcOpenShell") + "\n") return False else: IFCOPENSHELL5 = True return True else: - if hasattr(IfcImport,"IfcFile"): + if hasattr(IfcImport, "IfcFile"): IFCOPENSHELL5 = True return True -def read(filename,skip=None): + +def read(filename, skip=None): "Parses an IFC file" # parsing the IFC file @@ -141,32 +178,34 @@ def read(filename,skip=None): skipIds = skip if not skipIds: skipIds = [] - elif isinstance(skipIds,int): + elif isinstance(skipIds, int): skipIds = [skipIds] if getIfcOpenShell() and not FORCE_PYTHON_PARSER: # use the IfcOpenShell parser # preparing IfcOpenShell - if DEBUG: global ifcObjects,ifcParents - ifcObjects = {} # a table to relate ifc id with freecad object - ifcParents = {} # a table to relate ifc id with parent id + if DEBUG: + global ifcObjects, ifcParents + ifcObjects = {} # a table to relate ifc id with freecad object + ifcParents = {} # a table to relate ifc id with parent id if SEPARATE_OPENINGS: if not IFCOPENSHELL5: - if hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): - IfcImport.Settings(IfcImport.DISABLE_OPENING_SUBTRACTIONS,True) + if hasattr(IfcImport, "DISABLE_OPENING_SUBTRACTIONS"): + IfcImport.Settings(IfcImport.DISABLE_OPENING_SUBTRACTIONS, True) else: SKIP.append("IfcOpeningElement") useShapes = False if IFCOPENSHELL5: useShapes = True - if hasattr(IfcImport,"clean"): + if hasattr(IfcImport, "clean"): IfcImport.clean() - elif hasattr(IfcImport,"USE_BREP_DATA"): - IfcImport.Settings(IfcImport.USE_BREP_DATA,True) + elif hasattr(IfcImport, "USE_BREP_DATA"): + IfcImport.Settings(IfcImport.USE_BREP_DATA, True) useShapes = True else: - if DEBUG: print("Warning: IfcOpenShell version very old, unable to handle Brep data") + if DEBUG: + print("Warning: IfcOpenShell version very old, unable to handle Brep data") # opening file if IFCOPENSHELL5: @@ -174,7 +213,11 @@ def read(filename,skip=None): ifc = IfcImport.open(filename) objects = ifc.by_type("IfcProduct") num_lines = len(objects) - relations = ifc.by_type("IfcRelAggregates") + ifc.by_type("IfcRelContainedInSpatialStructure") + ifc.by_type("IfcRelVoidsElement") + relations = ( + ifc.by_type("IfcRelAggregates") + + ifc.by_type("IfcRelContainedInSpatialStructure") + + ifc.by_type("IfcRelVoidsElement") + ) if not objects: print("Error opening IFC file") return @@ -196,19 +239,33 @@ def read(filename,skip=None): objtype = str(obj).split("=")[1].split("(")[0] for r in relations: if r.is_a("IfcRelAggregates"): - for c in getAttr(r,"RelatedObjects"): + for c in getAttr(r, "RelatedObjects"): if str(obj) == str(c): - objparentid.append(int(str(getAttr(r,"RelatingObject")).split("=")[0].strip("#"))) + objparentid.append( + int(str(getAttr(r, "RelatingObject")).split("=")[0].strip("#")) + ) elif r.is_a("IfcRelContainedInSpatialStructure"): - for c in getAttr(r,"RelatedElements"): + for c in getAttr(r, "RelatedElements"): if str(obj) == str(c): - objparentid.append(int(str(getAttr(r,"RelatingStructure")).split("=")[0].strip("#"))) + objparentid.append( + int( + str(getAttr(r, "RelatingStructure")) + .split("=")[0] + .strip("#") + ) + ) elif r.is_a("IfcRelVoidsElement"): - if str(obj) == str(getAttr(r,"RelatedOpeningElement")): - objparentid.append(int(str(getAttr(r,"RelatingBuildingElement")).split("=")[0].strip("#"))) + if str(obj) == str(getAttr(r, "RelatedOpeningElement")): + objparentid.append( + int( + str(getAttr(r, "RelatingBuildingElement")) + .split("=")[0] + .strip("#") + ) + ) else: - if hasattr(IfcImport, 'GetBrepData'): + if hasattr(IfcImport, "GetBrepData"): obj = IfcImport.GetBrepData() else: obj = IfcImport.Get() @@ -217,55 +274,66 @@ def read(filename,skip=None): objname = obj.name objtype = obj.type objparentid.append(obj.parent_id) - if DEBUG: print("["+str(int((float(idx)/num_lines)*100))+"%] parsing ",objid,": ",objname," of type ",objtype) + if DEBUG: + print( + "[" + str(int((float(idx) / num_lines) * 100)) + "%] parsing ", + objid, + ": ", + objname, + " of type ", + objtype, + ) # retrieving name - n = getCleanName(objname,objid,objtype) + n = getCleanName(objname, objid, objtype) # skip IDs if objid in skipIds: - if DEBUG: print(" skipping because object ID is in skip list") + if DEBUG: + print(" skipping because object ID is in skip list") nobj = None # skip types elif objtype in SKIP: - if DEBUG: print(" skipping because type is in skip list") + if DEBUG: + print(" skipping because type is in skip list") nobj = None # check if object was already processed, to workaround an ifcopenshell bug elif objid in processedIds: - if DEBUG: print(" skipping because this object was already processed") + if DEBUG: + print(" skipping because this object was already processed") else: # build shape shape = None if useShapes: - shape = getShape(obj,objid) + shape = getShape(obj, objid) # walls - if objtype in ["IfcWallStandardCase","IfcWall"]: - nobj = makeWall(objid,shape,n) + if objtype in ["IfcWallStandardCase", "IfcWall"]: + nobj = makeWall(objid, shape, n) # windows - elif objtype in ["IfcWindow","IfcDoor"]: - nobj = makeWindow(objid,shape,n) + elif objtype in ["IfcWindow", "IfcDoor"]: + nobj = makeWindow(objid, shape, n) # structs - elif objtype in ["IfcBeam","IfcColumn","IfcSlab","IfcFooting"]: - nobj = makeStructure(objid,shape,objtype,n) + elif objtype in ["IfcBeam", "IfcColumn", "IfcSlab", "IfcFooting"]: + nobj = makeStructure(objid, shape, objtype, n) # roofs elif objtype in ["IfcRoof"]: - nobj = makeRoof(objid,shape,n) + nobj = makeRoof(objid, shape, n) # furniture elif objtype in ["IfcFurnishingElement"]: - nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) + nobj = FreeCAD.ActiveDocument.addObject("Part::Feature", n) nobj.Shape = shape # sites elif objtype in ["IfcSite"]: - nobj = makeSite(objid,shape,n) + nobj = makeSite(objid, shape, n) # floors elif objtype in ["IfcBuildingStorey"]: @@ -279,34 +347,37 @@ def read(filename,skip=None): # spaces elif objtype in ["IfcSpace"]: - nobj = makeSpace(objid,shape,n) + nobj = makeSpace(objid, shape, n) elif shape: # treat as dumb parts - if DEBUG: print("Fixme: Shape-containing object not handled: ",objid, " ", objtype) - nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) + if DEBUG: + print("Fixme: Shape-containing object not handled: ", objid, " ", objtype) + nobj = FreeCAD.ActiveDocument.addObject("Part::Feature", n) nobj.Label = n nobj.Shape = shape else: # treat as meshes - if DEBUG: print("Warning: Object without shape: ",objid, " ", objtype) - if hasattr(obj,"mesh"): - if not hasattr(obj.mesh, 'verts'): - obj = IfcImport.Get() # Get triangulated rep of same product - me,pl = getMesh(obj) - nobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",n) + if DEBUG: + print("Warning: Object without shape: ", objid, " ", objtype) + if hasattr(obj, "mesh"): + if not hasattr(obj.mesh, "verts"): + obj = IfcImport.Get() # Get triangulated rep of same product + me, pl = getMesh(obj) + nobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature", n) nobj.Label = n nobj.Mesh = me nobj.Placement = pl else: - if DEBUG: print("Error: Skipping object without mesh: ",objid, " ", objtype) + if DEBUG: + print("Error: Skipping object without mesh: ", objid, " ", objtype) # registering object number and parent if objparentid: ifcParents[objid] = [] for p in objparentid: - ifcParents[objid].append([p,not (objtype in subtractiveTypes)]) + ifcParents[objid].append([p, not (objtype in subtractiveTypes)]) ifcObjects[objid] = nobj processedIds.append(objid) @@ -317,11 +388,11 @@ def read(filename,skip=None): if not IfcImport.Next(): break - # processing non-geometry and relationships parents_temp = dict(ifcParents) import ArchCommands - #print(parents_temp) + + # print(parents_temp) while parents_temp: id, comps = parents_temp.popitem() @@ -354,8 +425,8 @@ def read(filename,skip=None): parentid = obj.id parentname = obj.name parenttype = obj.type - #if DEBUG: print("["+str(int((float(idx)/num_lines)*100))+"%] parsing ",parentid,": ",parentname," of type ",parenttype) - n = getCleanName(parentname,parentid,parenttype) + # if DEBUG: print("["+str(int((float(idx)/num_lines)*100))+"%] parsing ",parentid,": ",parentname," of type ",parenttype) + n = getCleanName(parentname, parentid, parenttype) if parentid <= 0: parent = None elif parenttype == "IfcBuildingStorey": @@ -373,13 +444,14 @@ def read(filename,skip=None): elif parenttype == "IfcProject": parent = None else: - if DEBUG: print("Fixme: skipping unhandled parent: ", parentid, " ", parenttype) + if DEBUG: + print("Fixme: skipping unhandled parent: ", parentid, " ", parenttype) parent = None # registering object number and parent if not IFCOPENSHELL5: if parent_ifcobj.parent_id > 0: - ifcParents[parentid] = [parent_ifcobj.parent_id,True] - parents_temp[parentid] = [parent_ifcobj.parent_id,True] + ifcParents[parentid] = [parent_ifcobj.parent_id, True] + parents_temp[parentid] = [parent_ifcobj.parent_id, True] if parent and (not parentid in ifcObjects): ifcObjects[parentid] = parent @@ -387,171 +459,206 @@ def read(filename,skip=None): if parent and (id in ifcObjects): if ifcObjects[id] and (ifcObjects[id].Name != parent.Name): if additive: - if DEBUG: print("adding ",ifcObjects[id].Name, " to ",parent.Name) - ArchCommands.addComponents(ifcObjects[id],parent) + if DEBUG: + print("adding ", ifcObjects[id].Name, " to ", parent.Name) + ArchCommands.addComponents(ifcObjects[id], parent) else: - if DEBUG: print("removing ",ifcObjects[id].Name, " from ",parent.Name) - ArchCommands.removeComponents(ifcObjects[id],parent) + if DEBUG: + print("removing ", ifcObjects[id].Name, " from ", parent.Name) + ArchCommands.removeComponents(ifcObjects[id], parent) if not IFCOPENSHELL5: IfcImport.CleanUp() else: # use only the internal python parser - FreeCAD.Console.PrintWarning(translate("Arch","IfcOpenShell not found or disabled, falling back on internal parser.")+"\n") - schema=getSchema() + FreeCAD.Console.PrintWarning( + translate( + "Arch", "IfcOpenShell not found or disabled, falling back on internal parser." + ) + + "\n" + ) + schema = getSchema() if schema: - if DEBUG: print("opening",filename,"...") - ifc = IfcDocument(filename,schema=schema) + if DEBUG: + print("opening", filename, "...") + ifc = IfcDocument(filename, schema=schema) else: - FreeCAD.Console.PrintWarning(translate("Arch","IFC Schema not found, IFC import disabled.")+"\n") + FreeCAD.Console.PrintWarning( + translate("Arch", "IFC Schema not found, IFC import disabled.") + "\n" + ) return None t2 = time.time() - if DEBUG: print("Successfully loaded",ifc,"in %s s" % ((t2-t1))) + if DEBUG: + print("Successfully loaded", ifc, "in %s s" % ((t2 - t1))) # getting walls for w in ifc.getEnt("IfcWallStandardCase"): nobj = makeWall(w) # getting windows and doors - for w in (ifc.getEnt("IfcWindow") + ifc.getEnt("IfcDoor")): + for w in ifc.getEnt("IfcWindow") + ifc.getEnt("IfcDoor"): nobj = makeWindow(w) # getting structs - for w in (ifc.getEnt("IfcSlab") + ifc.getEnt("IfcBeam") + ifc.getEnt("IfcColumn") \ - + ifc.getEnt("IfcFooting")): + for w in ( + ifc.getEnt("IfcSlab") + + ifc.getEnt("IfcBeam") + + ifc.getEnt("IfcColumn") + + ifc.getEnt("IfcFooting") + ): nobj = makeStructure(w) # getting floors for f in ifc.getEnt("IfcBuildingStorey"): - group(f,ifc,"Floor") + group(f, ifc, "Floor") # getting buildings for b in ifc.getEnt("IfcBuilding"): - group(b,ifc,"Building") + group(b, ifc, "Building") # getting sites for s in ifc.getEnt("IfcSite"): - group(s,ifc,"Site") + group(s, ifc, "Site") - if DEBUG: print("done parsing. Recomputing...") + if DEBUG: + print("done parsing. Recomputing...") FreeCAD.ActiveDocument.recompute() t3 = time.time() - if DEBUG: print("done processing IFC file in %s s" % ((t3-t1))) + if DEBUG: + print("done processing IFC file in %s s" % ((t3 - t1))) return None -def getCleanName(name,ifcid,ifctype): +def getCleanName(name, ifcid, ifctype): "Get a clean name from an ifc object" - #print("getCleanName called",name,ifcid,ifctype) + # print("getCleanName called",name,ifcid,ifctype) n = name if not n: n = ifctype if PREFIX_NUMBERS: - n = "ID"+str(ifcid)+" "+n - #for c in ",.!?;:": + n = "ID" + str(ifcid) + " " + n + # for c in ",.!?;:": # n = n.replace(c,"_") return n -def makeWall(entity,shape=None,name="Wall"): +def makeWall(entity, shape=None, name="Wall"): "makes a wall in the freecad document" try: if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + if isinstance(shape, Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature", name + "_body") body.Shape = shape else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature", name + "_body") body.Mesh = shape - wall = Arch.makeWall(body,name=name) + wall = Arch.makeWall(body, name=name) wall.Label = name - if DEBUG: print(" made wall object ",entity,":",wall) + if DEBUG: + print(" made wall object ", entity, ":", wall) return wall # use internal parser - if DEBUG: print("=====> making wall",entity.id) + if DEBUG: + print("=====> making wall", entity.id) placement = wall = wire = body = width = height = None placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print(" got wall placement",entity.id,":",placement) + if DEBUG: + print(" got wall placement", entity.id, ":", placement) width = entity.getProperty("Width") height = entity.getProperty("Height") if width and height: - if DEBUG: print(" got width, height ",entity.id,":",width,"/",height) - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Axis": - wire = getWire(r.Items,placement) - wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id)) + if DEBUG: + print(" got width, height ", entity.id, ":", width, "/", height) + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Axis": + wire = getWire(r.Items, placement) + wall = Arch.makeWall( + wire, width, height, align="Center", name="Wall" + str(entity.id) + ) else: - if DEBUG: print(" no height or width properties found...") - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Body": - for b in r.Items: - if b.type == "IFCEXTRUDEDAREASOLID": - norm = getVector(b.ExtrudedDirection) - norm.normalize() - wire = getWire(b.SweptArea,placement) - wall = Arch.makeWall(wire,width=0,height=b.Depth,name="Wall"+str(entity.id)) - wall.Normal = norm + if DEBUG: + print(" no height or width properties found...") + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Body": + for b in r.Items: + if b.type == "IFCEXTRUDEDAREASOLID": + norm = getVector(b.ExtrudedDirection) + norm.normalize() + wire = getWire(b.SweptArea, placement) + wall = Arch.makeWall( + wire, width=0, height=b.Depth, name="Wall" + str(entity.id) + ) + wall.Normal = norm if wall: - if DEBUG: print(" made wall object ",entity.id,":",wall) + if DEBUG: + print(" made wall object ", entity.id, ":", wall) return wall - if DEBUG: print(" error: skipping wall",entity.id) + if DEBUG: + print(" error: skipping wall", entity.id) return None except Exception: - if DEBUG: print(" error: skipping wall",entity) + if DEBUG: + print(" error: skipping wall", entity) return None -def makeWindow(entity,shape=None,name="Window"): +def makeWindow(entity, shape=None, name="Window"): "makes a window in the freecad document" try: if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): + if isinstance(shape, Part.Shape): window = Arch.makeWindow(name=name) window.Shape = shape window.Label = name - if DEBUG: print(" made window object ",entity,":",window) + if DEBUG: + print(" made window object ", entity, ":", window) return window # use internal parser - if DEBUG: print("=====> making window",entity.id) + if DEBUG: + print("=====> making window", entity.id) placement = window = wire = body = width = height = None placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print("got window placement",entity.id,":",placement) + if DEBUG: + print("got window placement", entity.id, ":", placement) width = entity.getProperty("Width") height = entity.getProperty("Height") for r in entity.Representation.Representations: if r.RepresentationIdentifier == "Body": for b in r.Items: if b.type == "IFCEXTRUDEDAREASOLID": - wire = getWire(b.SweptArea,placement) - window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id)) + wire = getWire(b.SweptArea, placement) + window = Arch.makeWindow(wire, width=b.Depth, name=objtype + str(entity.id)) if window: - if DEBUG: print(" made window object ",entity.id,":",window) + if DEBUG: + print(" made window object ", entity.id, ":", window) return window - if DEBUG: print(" error: skipping window",entity.id) + if DEBUG: + print(" error: skipping window", entity.id) return None except Exception: - if DEBUG: print(" error: skipping window",entity) + if DEBUG: + print(" error: skipping window", entity) return None -def makeStructure(entity,shape=None,ifctype=None,name="Structure"): +def makeStructure(entity, shape=None, ifctype=None, name="Structure"): "makes a structure in the freecad document" try: if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + if isinstance(shape, Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature", name + "_body") body.Shape = shape else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature", name + "_body") body.Mesh = shape - structure = Arch.makeStructure(body,name=name) + structure = Arch.makeStructure(body, name=name) structure.Label = name if ifctype == "IfcBeam": structure.Role = "Beam" @@ -561,93 +668,110 @@ def makeStructure(entity,shape=None,ifctype=None,name="Structure"): structure.Role = "Slab" elif ifctype == "IfcFooting": structure.Role = "Foundation" - if DEBUG: print(" made structure object ",entity,":",structure," (type: ",ifctype,")") + if DEBUG: + print( + " made structure object ", entity, ":", structure, " (type: ", ifctype, ")" + ) return structure # use internal parser - if DEBUG: print("=====> making struct",entity.id) + if DEBUG: + print("=====> making struct", entity.id) placement = structure = wire = body = width = height = None placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print("got window placement",entity.id,":",placement) + if DEBUG: + print("got window placement", entity.id, ":", placement) width = entity.getProperty("Width") height = entity.getProperty("Height") for r in entity.Representation.Representations: if r.RepresentationIdentifier == "Body": for b in r.Items: if b.type == "IFCEXTRUDEDAREASOLID": - wire = getWire(b.SweptArea,placement) - structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id)) + wire = getWire(b.SweptArea, placement) + structure = Arch.makeStructure( + wire, height=b.Depth, name=objtype + str(entity.id) + ) if structure: - if DEBUG: print(" made structure object ",entity.id,":",structure) + if DEBUG: + print(" made structure object ", entity.id, ":", structure) return structure - if DEBUG: print(" error: skipping structure",entity.id) + if DEBUG: + print(" error: skipping structure", entity.id) return None except Exception: - if DEBUG: print(" error: skipping structure",entity) + if DEBUG: + print(" error: skipping structure", entity) return None -def makeSite(entity,shape=None,name="Site"): +def makeSite(entity, shape=None, name="Site"): "makes a site in the freecad document" try: body = None if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + if isinstance(shape, Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature", name + "_body") body.Shape = shape else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature", name + "_body") body.Mesh = shape site = Arch.makeSite(name=name) site.Label = name if body: site.Terrain = body - if DEBUG: print(" made site object ",entity,":",site) + if DEBUG: + print(" made site object ", entity, ":", site) return site except Exception: return None -def makeSpace(entity,shape=None,name="Space"): + +def makeSpace(entity, shape=None, name="Space"): "makes a space in the freecad document" try: if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): + if isinstance(shape, Part.Shape): space = Arch.makeSpace(name=name) space.Label = name - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + body = FreeCAD.ActiveDocument.addObject("Part::Feature", name + "_body") body.Shape = shape space.Base = body body.ViewObject.hide() - if DEBUG: print(" made space object ",entity,":",space) + if DEBUG: + print(" made space object ", entity, ":", space) return space except Exception: return None -def makeRoof(entity,shape=None,name="Roof"): +def makeRoof(entity, shape=None, name="Roof"): "makes a roof in the freecad document" try: if shape: # use ifcopenshell - if isinstance(shape,Part.Shape): + if isinstance(shape, Part.Shape): roof = Arch.makeRoof(name=name) roof.Label = name roof.Shape = shape - if DEBUG: print(" made roof object ",entity,":",roof) + if DEBUG: + print(" made roof object ", entity, ":", roof) return roof except Exception: return None + # geometry helpers ################################################################### + def getMesh(obj): "gets mesh and placement from an IfcOpenShell object" if IFCOPENSHELL5: - return None,None - print("fixme: mesh data not yet supported") # TODO implement this with OCC tessellate + return None, None + print("fixme: mesh data not yet supported") # TODO implement this with OCC tessellate import Mesh + meshdata = [] print(obj.mesh.faces) print(obj.mesh.verts) @@ -656,8 +780,8 @@ def getMesh(obj): for i in range(0, len(f), 3): face = [] for j in range(3): - vi = f[i+j]*3 - face.append([v[vi],v[vi+1],v[vi+2]]) + vi = f[i + j] * 3 + face.append([v[vi], v[vi + 1], v[vi + 2]]) meshdata.append(face) print(meshdata) me = Mesh.Mesh(meshdata) @@ -670,28 +794,37 @@ def getMesh(obj): 0, 0, 0, 1) # fmt: on pl = FreeCAD.Placement(mat) - return me,pl + return me, pl -def getShape(obj,objid): + +def getShape(obj, objid): "gets a shape from an IfcOpenShell object" - #print("retrieving shape from obj ",objid) + # print("retrieving shape from obj ",objid) import Part - sh=Part.Shape() + + sh = Part.Shape() brep_data = None if IFCOPENSHELL5: try: - if hasattr(IfcImport,"SEW_SHELLS"): + if hasattr(IfcImport, "SEW_SHELLS"): ss = IfcImport.SEW_SHELLS else: ss = 0 - if SEPARATE_OPENINGS and hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): - if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | IfcImport.DISABLE_OBJECT_PLACEMENT | ss) + if SEPARATE_OPENINGS and hasattr(IfcImport, "DISABLE_OPENING_SUBTRACTIONS"): + if SEPARATE_PLACEMENTS and hasattr(IfcImport, "DISABLE_OBJECT_PLACEMENT"): + brep_data = IfcImport.create_shape( + obj, + IfcImport.DISABLE_OPENING_SUBTRACTIONS + | IfcImport.DISABLE_OBJECT_PLACEMENT + | ss, + ) else: - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | ss) + brep_data = IfcImport.create_shape( + obj, IfcImport.DISABLE_OPENING_SUBTRACTIONS | ss + ) else: - if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OBJECT_PLACEMENT | ss) + if SEPARATE_PLACEMENTS and hasattr(IfcImport, "DISABLE_OBJECT_PLACEMENT"): + brep_data = IfcImport.create_shape(obj, IfcImport.DISABLE_OBJECT_PLACEMENT | ss) else: brep_data = IfcImport.create_shape(obj, ss) except Exception: @@ -702,8 +835,9 @@ def getShape(obj,objid): try: if MAKETEMPFILES: import tempfile - th,tf = tempfile.mkstemp(suffix=".brp") - of = pyopen(tf,"wb") + + th, tf = tempfile.mkstemp(suffix=".brp") + of = pyopen(tf, "wb") of.write(brep_data) of.close() os.close(th) @@ -716,23 +850,26 @@ def getShape(obj,objid): return None else: if IFCOPENSHELL5 and SEPARATE_PLACEMENTS: - p = getPlacement(getAttr(obj,"ObjectPlacement")) + p = getPlacement(getAttr(obj, "ObjectPlacement")) if p: sh.Placement = p if not sh.Solids: # try to extract a solid shape if sh.Faces: try: - if DEBUG: print(" malformed solid. Attempting to fix...") + if DEBUG: + print(" malformed solid. Attempting to fix...") shell = Part.makeShell(sh.Faces) if shell: solid = Part.makeSolid(shell) if solid: sh = solid except Exception: - if DEBUG: print(" failed to retrieve solid from object ",objid) + if DEBUG: + print(" failed to retrieve solid from object ", objid) else: - if DEBUG: print(" object ", objid, " does not contain any geometry") + if DEBUG: + print(" object ", objid, " does not contain any geometry") if not IFCOPENSHELL5: m = obj.matrix # fmt: off @@ -743,20 +880,22 @@ def getShape(obj,objid): # fmt: on sh.Placement = FreeCAD.Placement(mat) # if DEBUG: print("getting Shape from ",obj) - #print("getting shape: ",sh,sh.Solids,sh.Volume,sh.isValid(),sh.isNull()) - #for v in sh.Vertexes: print(v.Point) + # print("getting shape: ",sh,sh.Solids,sh.Volume,sh.isValid(),sh.isNull()) + # for v in sh.Vertexes: print(v.Point) if sh: if not sh.isNull(): return sh return None + def getPlacement(entity): "returns a placement from the given entity" if not entity: return None - if DEBUG: print(" getting placement ",entity) + if DEBUG: + print(" getting placement ", entity) if IFCOPENSHELL5: - if isinstance(entity,int): + if isinstance(entity, int): entity = ifc.by_id(entity) entitytype = str(entity).split("=")[1].split("(")[0].upper() entityid = int(str(entity).split("=")[0].strip("#")) @@ -765,18 +904,18 @@ def getPlacement(entity): entityid = entity.id pl = None if entitytype == "IFCAXIS2PLACEMENT3D": - x = getVector(getAttr(entity,"RefDirection")) - z = getVector(getAttr(entity,"Axis")) - if not(x) or not(z): + x = getVector(getAttr(entity, "RefDirection")) + z = getVector(getAttr(entity, "Axis")) + if not (x) or not (z): return None y = z.cross(x) - loc = getVector(getAttr(entity,"Location")) - m = DraftVecUtils.getPlaneRotation(x,y,z) + loc = getVector(getAttr(entity, "Location")) + m = DraftVecUtils.getPlaneRotation(x, y, z) pl = FreeCAD.Placement(m) pl.move(loc) elif entitytype == "IFCLOCALPLACEMENT": - pl = getPlacement(getAttr(entity,"PlacementRelTo")) - relpl = getPlacement(getAttr(entity,"RelativePlacement")) + pl = getPlacement(getAttr(entity, "PlacementRelTo")) + relpl = getPlacement(getAttr(entity, "RelativePlacement")) if pl and relpl: pl = relpl.multiply(pl) elif relpl: @@ -785,114 +924,133 @@ def getPlacement(entity): loc = getVector(entity) pl = FreeCAD.Placement() pl.move(loc) - if DEBUG: print(" made placement for ",entityid,":",pl) + if DEBUG: + print(" made placement for ", entityid, ":", pl) return pl -def getAttr(entity,attr): + +def getAttr(entity, attr): "returns the given attribute from the given entity" if IFCOPENSHELL5: - if isinstance(entity,int): + if isinstance(entity, int): entity = ifc.by_id(entity) i = entity.get_argument_index(attr) return entity.get_argument(i) else: - return getattr(entity,attr) + return getattr(entity, attr) + def getVector(entity): "returns a vector from the given entity" if not entity: return None - if DEBUG: print(" getting point from ",entity) + if DEBUG: + print(" getting point from ", entity) if IFCOPENSHELL5: - if isinstance(entity,int): + if isinstance(entity, int): entity = ifc.by_id(entity) entitytype = str(entity).split("=")[1].split("(")[0].upper() else: entitytype = entity.type.upper() if entitytype == "IFCDIRECTION": - DirectionRatios = getAttr(entity,"DirectionRatios") + DirectionRatios = getAttr(entity, "DirectionRatios") if len(DirectionRatios) == 3: return FreeCAD.Vector(tuple(DirectionRatios)) else: - return FreeCAD.Vector(tuple(DirectionRatios+[0])) + return FreeCAD.Vector(tuple(DirectionRatios + [0])) elif entitytype == "IFCCARTESIANPOINT": - Coordinates = getAttr(entity,"Coordinates") + Coordinates = getAttr(entity, "Coordinates") if len(Coordinates) == 3: return FreeCAD.Vector(tuple(Coordinates)) else: - return FreeCAD.Vector(tuple(Coordinates+[0])) + return FreeCAD.Vector(tuple(Coordinates + [0])) return None + # below is only used by the internal parser ######################################### + def getSchema(): "retrieves the express schema" custom = params.get_param_arch("CustomIfcSchema") if custom: if os.path.exists(custom): - if DEBUG: print("Using custom schema: ",custom.split(os.sep)[-1]) + if DEBUG: + print("Using custom schema: ", custom.split(os.sep)[-1]) return custom p = None - p = os.path.join(FreeCAD.ConfigGet("UserAppData"),SCHEMA.split(os.sep)[-1]) + p = os.path.join(FreeCAD.ConfigGet("UserAppData"), SCHEMA.split(os.sep)[-1]) if os.path.exists(p): return p import ArchCommands + p = ArchCommands.download(SCHEMA) if p: return p return None -def group(entity,ifc,mode=None): + +def group(entity, ifc, mode=None): "gathers the children of the given entity" # only used by the internal parser try: - if DEBUG: print("=====> making group",entity.id) + if DEBUG: + print("=====> making group", entity.id) placement = None placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print("got cell placement",entity.id,":",placement) - subelements = ifc.find("IFCRELCONTAINEDINSPATIALSTRUCTURE","RelatingStructure",entity) - subelements.extend(ifc.find("IFCRELAGGREGATES","RelatingObject",entity)) + if DEBUG: + print("got cell placement", entity.id, ":", placement) + subelements = ifc.find("IFCRELCONTAINEDINSPATIALSTRUCTURE", "RelatingStructure", entity) + subelements.extend(ifc.find("IFCRELAGGREGATES", "RelatingObject", entity)) elts = [] for s in subelements: - if hasattr(s,"RelatedElements"): + if hasattr(s, "RelatedElements"): s = s.RelatedElements - if not isinstance(s,list): s = [s] + if not isinstance(s, list): + s = [s] elts.extend(s) - elif hasattr(s,"RelatedObjects"): + elif hasattr(s, "RelatedObjects"): s = s.RelatedObjects - if not isinstance(s,list): s = [s] + if not isinstance(s, list): + s = [s] elts.extend(s) - elif hasattr(s,"RelatedObject"): + elif hasattr(s, "RelatedObject"): s = s.RelatedObject - if not isinstance(s,list): s = [s] + if not isinstance(s, list): + s = [s] elts.extend(s) - print("found dependent elements: ",elts) + print("found dependent elements: ", elts) - groups = [['Wall',['IfcWallStandardCase'],[]], - ['Window',['IfcWindow','IfcDoor'],[]], - ['Structure',['IfcSlab','IfcFooting','IfcBeam','IfcColumn'],[]], - ['Floor',['IfcBuildingStorey'],[]], - ['Building',['IfcBuilding'],[]], - ['Furniture',['IfcFurnishingElement'],[]]] + groups = [ + ["Wall", ["IfcWallStandardCase"], []], + ["Window", ["IfcWindow", "IfcDoor"], []], + ["Structure", ["IfcSlab", "IfcFooting", "IfcBeam", "IfcColumn"], []], + ["Floor", ["IfcBuildingStorey"], []], + ["Building", ["IfcBuilding"], []], + ["Furniture", ["IfcFurnishingElement"], []], + ] for e in elts: for g in groups: for t in g[1]: if e.type.upper() == t.upper(): - if hasattr(FreeCAD.ActiveDocument,g[0]+str(e.id)): - g[2].append(FreeCAD.ActiveDocument.getObject(g[0]+str(e.id))) - print("groups:",groups) + if hasattr(FreeCAD.ActiveDocument, g[0] + str(e.id)): + g[2].append(FreeCAD.ActiveDocument.getObject(g[0] + str(e.id))) + print("groups:", groups) comps = [] if CREATE_IFC_GROUPS: - if DEBUG:wprint("creating subgroups") + if DEBUG: + wprint("creating subgroups") for g in groups: if g[2]: - if g[0] in ['Building','Floor']: + if g[0] in ["Building", "Floor"]: comps.extend(g[2]) else: - fcg = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",g[0]+"s") + fcg = FreeCAD.ActiveDocument.addObject( + "App::DocumentObjectGroup", g[0] + "s" + ) for o in g[2]: fcg.addObject(o) comps.append(fcg) @@ -904,36 +1062,41 @@ def group(entity,ifc,mode=None): name = mode + str(entity.id) cell = None if mode == "Site": - cell = Arch.makeSite(comps,name=name) + cell = Arch.makeSite(comps, name=name) elif mode == "Floor": - cell = Arch.makeFloor(comps,name=name) + cell = Arch.makeFloor(comps, name=name) elif mode == "Building": - cell = Arch.makeBuilding(comps,name=name) + cell = Arch.makeBuilding(comps, name=name) if label and cell: cell.Label = label except Exception: - if DEBUG: print("error: skipping group ",entity.id) + if DEBUG: + print("error: skipping group ", entity.id) -def getWire(entity,placement=None): + +def getWire(entity, placement=None): "returns a wire (created in the freecad document) from the given entity" # only used by the internal parser - if DEBUG: print("making Wire from :",entity) - if not entity: return None + if DEBUG: + print("making Wire from :", entity) + if not entity: + return None if entity.type == "IFCPOLYLINE": pts = [] for p in entity.Points: pts.append(getVector(p)) - return Draft.getWire(pts,placement=placement) + return Draft.getWire(pts, placement=placement) elif entity.type == "IFCARBITRARYCLOSEDPROFILEDEF": pts = [] for p in entity.OuterCurve.Points: pts.append(getVector(p)) - return Draft.getWire(pts,closed=True,placement=placement) + return Draft.getWire(pts, closed=True, placement=placement) # EXPORT ########################################################## -def export(exportList,filename): + +def export(exportList, filename): "called when freecad exports a file" global ifcw ifcw = None @@ -943,17 +1106,25 @@ def export(exportList,filename): try: import ifc_wrapper as ifcw except ImportError: - FreeCAD.Console.PrintError(translate("Arch","Error: IfcOpenShell is not installed")+"\n") - print("""importIFC: ifcOpenShell is not installed. IFC export is unavailable. + FreeCAD.Console.PrintError( + translate("Arch", "Error: IfcOpenShell is not installed") + "\n" + ) + print( + """importIFC: ifcOpenShell is not installed. IFC export is unavailable. Note: IFC export currently requires an experimental version of IfcOpenShell - available from https://github.com/aothms/IfcOpenShell""") + available from https://github.com/aothms/IfcOpenShell""" + ) return - if (not hasattr(ifcw,"IfcFile")) and (not hasattr(ifcw,"file")): - FreeCAD.Console.PrintError(translate("Arch","Error: your IfcOpenShell version is too old")+"\n") - print("""importIFC: The version of IfcOpenShell installed on this system does not + if (not hasattr(ifcw, "IfcFile")) and (not hasattr(ifcw, "file")): + FreeCAD.Console.PrintError( + translate("Arch", "Error: your IfcOpenShell version is too old") + "\n" + ) + print( + """importIFC: The version of IfcOpenShell installed on this system does not have IFC export capabilities. IFC export currently requires an experimental - version of IfcOpenShell available from https://github.com/aothms/IfcOpenShell""") + version of IfcOpenShell available from https://github.com/aothms/IfcOpenShell""" + ) return import Draft import Arch @@ -965,16 +1136,15 @@ def export(exportList,filename): forcebrep = params.get_param_arch("ifcExportAsBrep") application = "FreeCAD" ver = FreeCAD.Version() - version = ver[0]+"."+ver[1]+" build"+ver[2] + version = ver[0] + "." + ver[1] + " build" + ver[2] owner = FreeCAD.ActiveDocument.CreatedBy company = FreeCAD.ActiveDocument.Company project = FreeCAD.ActiveDocument.Name - ifc = IfcWriter(filename,project,owner,company,application,version) + ifc = IfcWriter(filename, project, owner, company, application, version) txt = [] # get all children and reorder list to get buildings and floors processed first - objectslist = Draft.get_group_contents(exportList, walls=True, - addgroups=True) + objectslist = Draft.get_group_contents(exportList, walls=True, addgroups=True) objectslist = Arch.pruneIncluded(objectslist, strict=True) sites = [] @@ -995,7 +1165,8 @@ def export(exportList,filename): else: others.append(obj) objectslist = buildings + floors + others - if DEBUG: print("adding ", len(objectslist), " objects") + if DEBUG: + print("adding ", len(objectslist), " objects") global unprocessed unprocessed = [] @@ -1015,8 +1186,8 @@ def export(exportList,filename): extra = None # setting the IFC type - if hasattr(obj,"Role"): - ifctype = obj.Role.replace(" ","") + if hasattr(obj, "Role"): + ifctype = obj.Role.replace(" ", "") elif otype == "Foundation": ifctype = "Footing" elif otype == "Rebar": @@ -1030,26 +1201,27 @@ def export(exportList,filename): # getting the "Force BREP" flag brepflag = False - if hasattr(obj,"IfcAttributes"): + if hasattr(obj, "IfcAttributes"): if "FlagForceBrep" in obj.IfcAttributes: if obj.IfcAttributes["FlagForceBrep"] == "True": brepflag = True - if DEBUG: print("Adding " + obj.Label + " as Ifc" + ifctype) + if DEBUG: + print("Adding " + obj.Label + " as Ifc" + ifctype) # writing IFC data if obj.isDerivedFrom("App::DocumentObjectGroup"): # getting parent building if parent: - parent = ifc.findByName("IfcBuilding",str(parent.Label)) + parent = ifc.findByName("IfcBuilding", str(parent.Label)) if otype == "Site": - print(" Skipping (not implemented yet)") # TODO manage sites + print(" Skipping (not implemented yet)") # TODO manage sites elif otype == "Building": - ifc.addBuilding( name=name ) + ifc.addBuilding(name=name) elif otype == "Floor": - ifc.addStorey( building=parent, name=name ) + ifc.addStorey(building=parent, name=name) elif obj.isDerivedFrom("Part::Feature"): @@ -1059,29 +1231,34 @@ def export(exportList,filename): # get parent floor if parent: - parent = ifc.findByName("IfcBuildingStorey",str(parent.Label)) + parent = ifc.findByName("IfcBuildingStorey", str(parent.Label)) # get representation if (not forcebrep) and (not brepflag): - gdata = getIfcExtrusionData(obj,scaling,SEPARATE_OPENINGS) - #if DEBUG: print(" extrusion data for ",obj.Label," : ",gdata) + gdata = getIfcExtrusionData(obj, scaling, SEPARATE_OPENINGS) + # if DEBUG: print(" extrusion data for ",obj.Label," : ",gdata) if not gdata: - fdata = getIfcBrepFacesData(obj,scaling) - #if DEBUG: print(" brep data for ",obj.Label," : ",fdata) + fdata = getIfcBrepFacesData(obj, scaling) + # if DEBUG: print(" brep data for ",obj.Label," : ",fdata) if not fdata: if obj.isDerivedFrom("Part::Feature"): print(" Error retrieving the shape of object ", obj.Label) unprocessed.append(obj) continue else: - if DEBUG: print(" No geometry") + if DEBUG: + print(" No geometry") else: - if DEBUG: print(" Brep") + if DEBUG: + print(" Brep") else: - if DEBUG: print(" Extrusion") + if DEBUG: + print(" Extrusion") if gdata: # gdata = [ type, profile data, extrusion data, placement data ] - placement = ifc.addPlacement(origin=gdata[3][0],xaxis=gdata[3][1],zaxis=gdata[3][2]) + placement = ifc.addPlacement( + origin=gdata[3][0], xaxis=gdata[3][1], zaxis=gdata[3][2] + ) if gdata[0] == "polyline": representation = ifc.addExtrudedPolyline(gdata[1], gdata[2], color=color) elif gdata[0] == "circle": @@ -1097,40 +1274,60 @@ def export(exportList,filename): # create ifc object ifctype = "Ifc" + ifctype - if hasattr(obj,"Description"): + if hasattr(obj, "Description"): descr = obj.Description if otype == "Wall": if gdata: if gdata[0] == "polyline": ifctype = "IfcWallStandardCase" elif otype == "Structure": - if ifctype in ["IfcSlab","IfcFooting"]: + if ifctype in ["IfcSlab", "IfcFooting"]: extra = ["NOTDEFINED"] elif otype == "Window": - extra = [obj.Width.Value*scaling, obj.Height.Value*scaling] + extra = [obj.Width.Value * scaling, obj.Height.Value * scaling] elif otype == "Space": - extra = ["ELEMENT","INTERNAL",getIfcElevation(obj)] + extra = ["ELEMENT", "INTERNAL", getIfcElevation(obj)] elif otype.startswith("Part::"): extra = ["ELEMENT"] if not ifctype in supportedIfcTypes: - if DEBUG: print(" Type ",ifctype," is not supported yet. Exporting as IfcBuildingElementProxy instead") + if DEBUG: + print( + " Type ", + ifctype, + " is not supported yet. Exporting as IfcBuildingElementProxy instead", + ) ifctype = "IfcBuildingElementProxy" extra = ["ELEMENT"] - product = ifc.addProduct( ifctype, representation, storey=parent, placement=placement, name=name, description=descr, extra=extra ) + product = ifc.addProduct( + ifctype, + representation, + storey=parent, + placement=placement, + name=name, + description=descr, + extra=extra, + ) if product: # removing openings if SEPARATE_OPENINGS and gdata: for o in obj.Subtractions: - print("Subtracting ",o.Label) - fdata = getIfcBrepFacesData(o,scaling,sub=True) + print("Subtracting ", o.Label) + fdata = getIfcBrepFacesData(o, scaling, sub=True) representation = [ifc.addFacetedBrep(f, color=color) for f in fdata] - p2 = ifc.addProduct( "IfcOpeningElement", representation, storey=product, placement=None, name=str(o.Label), description=None) + p2 = ifc.addProduct( + "IfcOpeningElement", + representation, + storey=product, + placement=None, + name=str(o.Label), + description=None, + ) # writing text log spacer = "" - for i in range(36-len(obj.Label)): + for i in range(36 - len(obj.Label)): spacer += " " txt.append(obj.Label + spacer + ifctype) @@ -1145,21 +1342,24 @@ def export(exportList,filename): else: unprocessed.append(obj) else: - if DEBUG: print("Object type ", otype, " is not supported yet.") + if DEBUG: + print("Object type ", otype, " is not supported yet.") # processing groups - for name,entities in groups.items(): + for name, entities in groups.items(): if entities: o = FreeCAD.ActiveDocument.getObject(name) if o: - if DEBUG: print("Adding group ", o.Label, " with ",len(entities)," elements") - grp = ifc.addGroup( entities, o.Label ) + if DEBUG: + print("Adding group ", o.Label, " with ", len(entities), " elements") + grp = ifc.addGroup(entities, o.Label) ifc.write() if exporttxt: import os import time + txtstring = "List of objects exported by FreeCAD in file\n" txtstring += filename + "\n" txtstring += "On " + time.ctime() + "\n" @@ -1169,47 +1369,53 @@ def export(exportList,filename): txtstring += "Nr Name Type\n" txtstring += "\n" for i in range(len(txt)): - idx = str(i+1) + idx = str(i + 1) sp = "" - for j in range(8-len(idx)): + for j in range(8 - len(idx)): sp += " " txtstring += idx + sp + txt[i] + "\n" - txtfile = os.path.splitext(filename)[0]+".txt" - f = pyopen(txtfile,"wb") + txtfile = os.path.splitext(filename)[0] + ".txt" + f = pyopen(txtfile, "wb") f.write(txtstring) f.close() FreeCAD.ActiveDocument.recompute() if unprocessed: - print("\nWARNING: " + str(len(unprocessed)) + " objects were not exported (stored in importIFC.unprocessed):") + print( + "\nWARNING: " + + str(len(unprocessed)) + + " objects were not exported (stored in importIFC.unprocessed):" + ) for o in unprocessed: print(" " + o.Label) -def getTuples(data,scale=1,placement=None,normal=None,close=True): +def getTuples(data, scale=1, placement=None, normal=None, close=True): """getTuples(data,[scale,placement,normal,close]): returns a tuple or a list of tuples from a vector or from the vertices of a shape. Scale can indicate a scale factor""" rnd = False import Part - if isinstance(data,FreeCAD.Vector): + + if isinstance(data, FreeCAD.Vector): if placement: data = placement.multVec(data) if rnd: data = DraftVecUtils.rounded(data) - return (data.x*scale,data.y*scale,data.z*scale) - elif isinstance(data,Part.Shape): + return (data.x * scale, data.y * scale, data.z * scale) + elif isinstance(data, Part.Shape): t = [] if len(data.Wires) == 1: import Part import DraftGeomUtils + data = Part.Wire(Part.__sortEdges__(data.Wires[0].Edges)) verts = data.Vertexes try: c = data.CenterOfMass v1 = verts[0].Point.sub(c) v2 = verts[1].Point.sub(c) - if DraftVecUtils.angle(v2,v1,normal) >= 0: + if DraftVecUtils.angle(v2, v1, normal) >= 0: # inverting verts order if the direction is couterclockwise verts.reverse() except Exception: @@ -1221,108 +1427,142 @@ def getTuples(data,scale=1,placement=None,normal=None,close=True): pt = placement.multVec(pt) if rnd: pt = DraftVecUtils.rounded(pt) - t.append((pt.x*scale,pt.y*scale,pt.z*scale)) + t.append((pt.x * scale, pt.y * scale, pt.z * scale)) - if close: # faceloops must not be closed, but ifc profiles must. + if close: # faceloops must not be closed, but ifc profiles must. t.append(t[0]) else: print("Arch.getTuples(): Wrong profile data") return t -def getIfcExtrusionData(obj,scale=1,nosubs=False): + +def getIfcExtrusionData(obj, scale=1, nosubs=False): """getIfcExtrusionData(obj,[scale,nosubs]): returns a closed path (a list of tuples), a tuple expressing an extrusion vector, and a list of 3 tuples for base position, x axis and z axis. Or returns None, if a base loop and an extrusion direction cannot be extracted. Scale can indicate a scale factor.""" - CURVEMODE = "PARAMETER" # For trimmed curves. CARTESIAN or PARAMETER + CURVEMODE = "PARAMETER" # For trimmed curves. CARTESIAN or PARAMETER - if hasattr(obj,"Additions"): + if hasattr(obj, "Additions"): if obj.Additions: # TODO provisorily treat objs with additions as breps return None - if hasattr(obj,"Subtractions") and not nosubs: + if hasattr(obj, "Subtractions") and not nosubs: if obj.Subtractions: return None - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getProfiles"): - p = obj.Proxy.getProfiles(obj,noplacement=True) - v = obj.Proxy.getExtrusionVector(obj,noplacement=True) + if hasattr(obj, "Proxy"): + if hasattr(obj.Proxy, "getProfiles"): + p = obj.Proxy.getProfiles(obj, noplacement=True) + v = obj.Proxy.getExtrusionVector(obj, noplacement=True) if (len(p) == 1) and v: p = p[0] r = FreeCAD.Placement() - #b = p.CenterOfMass + # b = p.CenterOfMass r = obj.Proxy.getPlacement(obj) - #b = obj.Placement.multVec(FreeCAD.Vector()) - #r.Rotation = DraftVecUtils.getRotation(v,FreeCAD.Vector(0,0,1)) - d = [r.Base,DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(1,0,0))),DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))] - #r = r.inverse() - #print("getExtrusionData: computed placement:",r) + # b = obj.Placement.multVec(FreeCAD.Vector()) + # r.Rotation = DraftVecUtils.getRotation(v,FreeCAD.Vector(0,0,1)) + d = [ + r.Base, + DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(1, 0, 0))), + DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(0, 0, 1))), + ] + # r = r.inverse() + # print("getExtrusionData: computed placement:",r) import Part + if len(p.Edges) == 1: - if isinstance(p.Edges[0].Curve,Part.Circle): + if isinstance(p.Edges[0].Curve, Part.Circle): # Circle profile - r1 = p.Edges[0].Curve.Radius*scale - return "circle", [getTuples(p.Edges[0].Curve.Center,scale), r1], getTuples(v,scale), d - elif isinstance(p.Edges[0].Curve,Part.Ellipse): + r1 = p.Edges[0].Curve.Radius * scale + return ( + "circle", + [getTuples(p.Edges[0].Curve.Center, scale), r1], + getTuples(v, scale), + d, + ) + elif isinstance(p.Edges[0].Curve, Part.Ellipse): # Ellipse profile - r1 = p.Edges[0].Curve.MajorRadius*scale - r2 = p.Edges[0].Curve.MinorRadius*scale - return "ellipse", [getTuples(p.Edges[0].Curve.Center,scale), r1, r2], getTuples(v,scale), d + r1 = p.Edges[0].Curve.MajorRadius * scale + r2 = p.Edges[0].Curve.MinorRadius * scale + return ( + "ellipse", + [getTuples(p.Edges[0].Curve.Center, scale), r1, r2], + getTuples(v, scale), + d, + ) curves = False for e in p.Edges: - if isinstance(e.Curve,Part.Circle): + if isinstance(e.Curve, Part.Circle): curves = True - elif not isinstance(e.Curve,Part.LineSegment): + elif not isinstance(e.Curve, Part.LineSegment): print("Arch.getIfcExtrusionData: Warning: unsupported edge type in profile") if curves: # Composite profile ecurves = [] last = None import DraftGeomUtils + edges = Part.__sortEdges__(p.Edges) for e in edges: - if isinstance(e.Curve,Part.Circle): + if isinstance(e.Curve, Part.Circle): follow = True if last: - if not DraftVecUtils.equals(last,e.Vertexes[0].Point): + if not DraftVecUtils.equals(last, e.Vertexes[0].Point): follow = False last = e.Vertexes[0].Point else: last = e.Vertexes[-1].Point else: last = e.Vertexes[-1].Point - p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center))) - p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center))) - da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center)) + p1 = math.degrees( + -DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center)) + ) + p2 = math.degrees( + -DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center)) + ) + da = DraftVecUtils.angle( + e.valueAt(e.FirstParameter + 0.1).sub(e.Curve.Center), + e.Vertexes[0].Point.sub(e.Curve.Center), + ) if p1 < 0: p1 = 360 + p1 if p2 < 0: p2 = 360 + p2 if da > 0: - follow = not(follow) + follow = not (follow) if CURVEMODE == "CARTESIAN": # BUGGY - p1 = getTuples(e.Vertexes[0].Point,scale) - p2 = getTuples(e.Vertexes[-1].Point,scale) - ecurves.append(["arc",getTuples(e.Curve.Center,scale),e.Curve.Radius*scale,[p1,p2],follow,CURVEMODE]) + p1 = getTuples(e.Vertexes[0].Point, scale) + p2 = getTuples(e.Vertexes[-1].Point, scale) + ecurves.append( + [ + "arc", + getTuples(e.Curve.Center, scale), + e.Curve.Radius * scale, + [p1, p2], + follow, + CURVEMODE, + ] + ) else: verts = [vertex.Point for vertex in e.Vertexes] if last: - if not DraftVecUtils.equals(last,verts[0]): + if not DraftVecUtils.equals(last, verts[0]): verts.reverse() last = e.Vertexes[0].Point else: last = e.Vertexes[-1].Point else: last = e.Vertexes[-1].Point - ecurves.append(["line",[getTuples(vert,scale) for vert in verts]]) - return "composite", ecurves, getTuples(v,scale), d + ecurves.append(["line", [getTuples(vert, scale) for vert in verts]]) + return "composite", ecurves, getTuples(v, scale), d else: # Polyline profile - return "polyline", getTuples(p,scale), getTuples(v,scale), d + return "polyline", getTuples(p, scale), getTuples(v, scale), d return None -def getIfcBrepFacesData(obj,scale=1,sub=False,tessellation=1): + +def getIfcBrepFacesData(obj, scale=1, sub=False, tessellation=1): """getIfcBrepFacesData(obj,[scale,tessellation]): returns a list(0) of lists(1) of lists(2) of lists(3), list(3) being a list of vertices defining a loop, list(2) describing a face from one or more loops, list(1) being the whole solid made of several faces, list(0) being the list @@ -1330,24 +1570,25 @@ def getIfcBrepFacesData(obj,scale=1,sub=False,tessellation=1): factor to apply on curved faces.""" shape = None if sub: - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getSubVolume"): + if hasattr(obj, "Proxy"): + if hasattr(obj.Proxy, "getSubVolume"): shape = obj.Proxy.getSubVolume(obj) if not shape: - if hasattr(obj,"Shape"): + if hasattr(obj, "Shape"): if obj.Shape: if not obj.Shape.isNull(): - #if obj.Shape.isValid(): + # if obj.Shape.isValid(): shape = obj.Shape - elif hasattr(obj,"Terrain"): + elif hasattr(obj, "Terrain"): if obj.Terrain: - if hasattr(obj.Terrain,"Shape"): + if hasattr(obj.Terrain, "Shape"): if obj.Terrain.Shape: if not obj.Terrain.Shape.isNull(): if obj.Terrain.Shape.isValid(): shape = obj.Terrain.Shape if shape: import Part + sols = [] if shape.Solids: dataset = shape.Solids @@ -1359,27 +1600,37 @@ def getIfcBrepFacesData(obj,scale=1,sub=False,tessellation=1): curves = False for face in sol.Faces: for e in face.Edges: - if not isinstance(e.Curve,Part.LineSegment): + if not isinstance(e.Curve, Part.LineSegment): curves = True if curves: tris = sol.tessellate(tessellation) for tri in tris[1]: f = [] for i in tri: - f.append(getTuples(tris[0][i],scale)) + f.append(getTuples(tris[0][i], scale)) s.append([f]) else: for face in sol.Faces: f = [] - f.append(getTuples(face.OuterWire,scale,normal=face.normalAt(0,0),close=False)) + f.append( + getTuples(face.OuterWire, scale, normal=face.normalAt(0, 0), close=False) + ) for wire in face.Wires: if wire.hashCode() != face.OuterWire.hashCode(): - f.append(getTuples(wire,scale,normal=DraftVecUtils.neg(face.normalAt(0,0)),close=False)) + f.append( + getTuples( + wire, + scale, + normal=DraftVecUtils.neg(face.normalAt(0, 0)), + close=False, + ) + ) s.append(f) sols.append(s) return sols return None + def getIfcElevation(obj): """getIfcElevation(obj): Returns the lowest height (Z coordinate) of this object""" if obj.isDerivedFrom("Part::Feature"): @@ -1392,21 +1643,26 @@ def explore(filename=None): "explore the contents of an ifc file in a Qt dialog" if not filename: from PySide import QtGui - filename = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(),'Open IFC file',None,'IFC files (*.ifc *.IFC)') + + filename = QtGui.QFileDialog.getOpenFileName( + QtGui.QApplication.activeWindow(), "Open IFC file", None, "IFC files (*.ifc *.IFC)" + ) if filename: filename = filename[0] if filename: getConfig() - schema=getSchema() - d = explorer(filename,schema) + schema = getSchema() + d = explorer(filename, schema) d.show() return d + # IfcReader ############################################# + class IfcSchema: SIMPLETYPES = ["INTEGER", "REAL", "STRING", "NUMBER", "LOGICAL", "BOOLEAN"] - NO_ATTR = ["WHERE", "INVERSE","WR2","WR3", "WR4", "WR5", "UNIQUE", "DERIVE"] + NO_ATTR = ["WHERE", "INVERSE", "WR2", "WR3", "WR4", "WR5", "UNIQUE", "DERIVE"] def __init__(self, filename): self.filename = filename @@ -1419,7 +1675,11 @@ class IfcSchema: self.data = self.file.read() self.types = self.readTypes() self.entities = self.readEntities() - if DEBUG: print("Parsed from schema %s: %s entities and %s types" % (self.filename, len(self.entities), len(self.types))) + if DEBUG: + print( + "Parsed from schema %s: %s entities and %s types" + % (self.filename, len(self.entities), len(self.types)) + ) def readTypes(self): """ @@ -1462,16 +1722,15 @@ class IfcSchema: # first occurrence of a NO_ATTR string (when it occurs on a new line) inner_str = re.search(";(.*?)$", raw_entity_str, re.DOTALL).groups()[0] - attrs_str = min([inner_str.partition("\r\n "+a)[0] for a in self.NO_ATTR]) + attrs_str = min([inner_str.partition("\r\n " + a)[0] for a in self.NO_ATTR]) attrs = [] for am in re.finditer("(.*?) : (.*?);", attrs_str, re.DOTALL): - name, attr_type = [s.replace("\r\n\t","") for s in am.groups()] + name, attr_type = [s.replace("\r\n\t", "") for s in am.groups()] attrs.append((name, attr_type)) entity["attributes"] = attrs entities[entity["name"]] = entity - return entities def getAttributes(self, name): @@ -1498,6 +1757,7 @@ class IfcSchema: name = self.data[i1:i2] return name + class IfcFile: """ Parses an ifc file given by filename, entities can be retrieved by name and id @@ -1507,13 +1767,14 @@ class IfcFile: entsById = {} entsByName = {} - def __init__(self, filename,schema): + def __init__(self, filename, schema): self.filename = filename self.schema = IfcSchema(schema) self.file = open(self.filename) self.entById, self.entsByName, self.header = self.read() self.file.close() - if DEBUG: print("Parsed from file %s: %s entities" % (self.filename, len(self.entById))) + if DEBUG: + print("Parsed from file %s: %s entities" % (self.filename, len(self.entById))) def getEntityById(self, id): return self.entById.get(id, None) @@ -1527,19 +1788,19 @@ class IfcFile: """ entById = {} entsByName = {} - header = 'HEADER ' + header = "HEADER " readheader = False for line in self.file: e = self.parseLine(line) if e: entById[int(e["id"])] = e - ids = e.get(e["name"],[]) + ids = e.get(e["name"], []) ids.append(e["id"]) entsByName[e["name"]] = list(set(ids)) - elif 'HEADER' in line: + elif "HEADER" in line: readheader = True elif readheader: - if 'ENDSEC' in line: + if "ENDSEC" in line: readheader = False else: header += line @@ -1570,14 +1831,18 @@ class IfcFile: while lastpos < len(attrs_str): newpos = self.nextString(attrs_str, lastpos) - parts.extend(self.parseAttribute(attrs_str[lastpos:newpos-1])) + parts.extend(self.parseAttribute(attrs_str[lastpos : newpos - 1])) lastpos = newpos schema_attributes = self.schema.getAttributes(ent_name) - assert len(schema_attributes) == len(parts), \ - "Expected %s attributes, got %s (entity: %s" % \ - (len(schema_attributes), len(parts), ent_name) + assert len(schema_attributes) == len( + parts + ), "Expected %s attributes, got %s (entity: %s" % ( + len(schema_attributes), + len(parts), + ent_name, + ) attribute_names = [a[0] for a in schema_attributes] @@ -1591,25 +1856,24 @@ class IfcFile: lastpos = 0 while lastpos < len(attr_str): newpos = self.nextString(attr_str, lastpos) - s = attr_str[lastpos:newpos-1] - if (s[0] == "(" and s[-1] == ")"): # list, recurse + s = attr_str[lastpos : newpos - 1] + if s[0] == "(" and s[-1] == ")": # list, recurse parts.append(self.parseAttribute(s[1:-1])) else: try: - parts.append(float(s)) # number, any kind + parts.append(float(s)) # number, any kind except ValueError: - if s[0] == "'" and s[-1] == "'": # string + if s[0] == "'" and s[-1] == "'": # string parts.append(s[1:-1]) elif s == "$": parts.append(None) else: - parts.append(s) # ref, enum or other + parts.append(s) # ref, enum or other lastpos = newpos return parts - def nextString(self, s, start): """ Parse the data part of a line @@ -1617,175 +1881,192 @@ class IfcFile: parens = 0 quotes = 0 - for pos in range(start,len(s)): + for pos in range(start, len(s)): c = s[pos] if c == "," and parens == 0 and quotes == 0: - return pos+1 + return pos + 1 elif c == "(" and quotes == 0: parens += 1 elif c == ")" and quotes == 0: parens -= 1 - elif c == "\'" and quotes == 0: + elif c == "'" and quotes == 0: quotes = 1 - elif c =="\'" and quotes == 1: + elif c == "'" and quotes == 1: quotes = 0 - return len(s)+1 + return len(s) + 1 + class IfcEntity: "a container for an IFC entity" - def __init__(self,ent,doc=None): + + def __init__(self, ent, doc=None): self.data = ent - self.id = int(ent['id']) - self.type = ent['name'].upper().strip(",[]()") - self.attributes = ent['attributes'] + self.id = int(ent["id"]) + self.type = ent["name"].upper().strip(",[]()") + self.attributes = ent["attributes"] self.doc = doc def __repr__(self): - return str(self.id) + ' : ' + self.type + ' ' + str(self.attributes) + return str(self.id) + " : " + self.type + " " + str(self.attributes) def getProperties(self): - return self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) + return self.doc.find("IFCRELDEFINESBYPROPERTIES", "RelatedObjects", self) - def getProperty(self,propName): + def getProperty(self, propName): "finds the value of the given property or quantity in this object, if exists" - propsets = self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) - if not propsets: return None + propsets = self.doc.find("IFCRELDEFINESBYPROPERTIES", "RelatedObjects", self) + if not propsets: + return None propset = [] for p in propsets: - if hasattr(p.RelatingPropertyDefinition,"HasProperties"): + if hasattr(p.RelatingPropertyDefinition, "HasProperties"): propset.extend(p.RelatingPropertyDefinition.HasProperties) - elif hasattr(p.RelatingPropertyDefinition,"Quantities"): + elif hasattr(p.RelatingPropertyDefinition, "Quantities"): propset.extend(p.RelatingPropertyDefinition.Quantities) for prop in propset: if prop.Name == propName: - print("found valid",prop) - if hasattr(prop,"LengthValue"): + print("found valid", prop) + if hasattr(prop, "LengthValue"): return prop.LengthValue - elif hasattr(prop,"AreaValue"): + elif hasattr(prop, "AreaValue"): return prop.AreaValue - elif hasattr(prop,"VolumeValue"): + elif hasattr(prop, "VolumeValue"): return prop.VolumeValue - elif hasattr(prop,"NominalValue"): + elif hasattr(prop, "NominalValue"): return prop.NominalValue return None - def getAttribute(self,attr): + def getAttribute(self, attr): "returns the value of the given attribute, if exists" - if hasattr(self,attr): + if hasattr(self, attr): return self.__dict__[attr] return None + class IfcDocument: "an object representing an IFC document" - def __init__(self,filename,schema="IFC2X3_TC1.exp"): - f = IfcFile(filename,schema) + + def __init__(self, filename, schema="IFC2X3_TC1.exp"): + f = IfcFile(filename, schema) self.filename = filename self.data = f.entById - self.Entities = {0:f.header} - for k,e in self.data.items(): - eid = int(e['id']) - self.Entities[eid] = IfcEntity(e,self) - if DEBUG: print(len(self.Entities),"entities created. Creating attributes...") - for k,ent in self.Entities.items(): - if DEBUG: print("attributing entity ",ent) - if hasattr(ent,"attributes"): - for k,v in ent.attributes.items(): - if DEBUG: print("parsing attribute: ",k," value ",v) - if isinstance(v,str): + self.Entities = {0: f.header} + for k, e in self.data.items(): + eid = int(e["id"]) + self.Entities[eid] = IfcEntity(e, self) + if DEBUG: + print(len(self.Entities), "entities created. Creating attributes...") + for k, ent in self.Entities.items(): + if DEBUG: + print("attributing entity ", ent) + if hasattr(ent, "attributes"): + for k, v in ent.attributes.items(): + if DEBUG: + print("parsing attribute: ", k, " value ", v) + if isinstance(v, str): val = self.__clean__(v) - elif isinstance(v,list): + elif isinstance(v, list): val = [] for item in v: - if isinstance(item,str): + if isinstance(item, str): val.append(self.__clean__(item)) else: val.append(item) else: val = v - setattr(ent,k.strip(),val) - if DEBUG: print("Document successfully created") + setattr(ent, k.strip(), val) + if DEBUG: + print("Document successfully created") - def __clean__(self,value): + def __clean__(self, value): "turns an attribute value into something usable" try: val = value.strip(" ()'") if val[:3].upper() == "IFC": if "IFCTEXT" in val.upper(): l = val.split("'") - if len(l) == 3: val = l[1] + if len(l) == 3: + val = l[1] elif "IFCBOOLEAN" in value.upper(): l = val.split(".") - if len(l) == 3: val = l[1] - if val.upper() == "F": val = False - elif val.upper() == "T": val = True + if len(l) == 3: + val = l[1] + if val.upper() == "F": + val = False + elif val.upper() == "T": + val = True elif "IFCREAL" in val.upper(): l = val.split("(") - if len(l) == 2: val = float(l[1].strip(")")) + if len(l) == 2: + val = float(l[1].strip(")")) else: - if '#' in val: + if "#" in val: if "," in val: val = val.split(",") l = [] for subval in val: - if '#' in subval: + if "#" in subval: s = subval.strip(" #") - if DEBUG: print("referencing ",s," : ",self.getEnt(int(s))) + if DEBUG: + print("referencing ", s, " : ", self.getEnt(int(s))) l.append(self.getEnt(int(s))) val = l else: val = val.strip() - val = val.replace("#","") - if DEBUG: print("referencing ",val," : ",self.getEnt(int(val))) - val = self.getEnt(int(val)) + val = val.replace("#", "") + if DEBUG: + print("referencing ", val, " : ", self.getEnt(int(val))) + val = self.getEnt(int(val)) if not val: val = value except Exception: - if DEBUG: print("error parsing attribute",value) + if DEBUG: + print("error parsing attribute", value) val = value return val def __repr__(self): - return "IFC Document: " + self.filename + ', ' + str(len(self.Entities)) + " entities " + return "IFC Document: " + self.filename + ", " + str(len(self.Entities)) + " entities " - def getEnt(self,ref): + def getEnt(self, ref): "gets an entity by id number, or a list of entities by type" - if isinstance(ref,int): + if isinstance(ref, int): if ref in self.Entities: return self.Entities[ref] - elif isinstance(ref,str): + elif isinstance(ref, str): l = [] ref = ref.upper() - for k,ob in self.Entities.items(): - if hasattr(ob,"type"): + for k, ob in self.Entities.items(): + if hasattr(ob, "type"): if ob.type == ref: l.append(ob) return l return None - def search(self,pat): + def search(self, pat): "searches entities types for partial match" l = [] pat = pat.upper() - for k,ob in self.Entities.items(): - if hasattr(ob,"type"): + for k, ob in self.Entities.items(): + if hasattr(ob, "type"): if pat in ob.type: if not ob.type in l: l.append(ob.type) return l - def find(self,pat1,pat2=None,pat3=None): - '''finds objects in the current IFC document. + def find(self, pat1, pat2=None, pat3=None): + """finds objects in the current IFC document. arguments can be of the following form: - (pattern): returns object types matching the given pattern (same as search) - (type,property,value): finds, in all objects of type "type", those whose property "property" has the given value - ''' + """ if pat3: bobs = self.getEnt(pat1) obs = [] for bob in bobs: - if hasattr(bob,pat2): + if hasattr(bob, pat2): if bob.getAttribute(pat2) == pat3: obs.append(bob) return obs @@ -1797,17 +2078,19 @@ class IfcDocument: return obs return None -def explorer(filename,schema="IFC2X3_TC1.exp"): + +def explorer(filename, schema="IFC2X3_TC1.exp"): "returns a PySide dialog showing the contents of an IFC file" from PySide import QtGui - ifc = IfcDocument(filename,schema) + + ifc = IfcDocument(filename, schema) schema = IfcSchema(schema) tree = QtGui.QTreeWidget() tree.setColumnCount(3) tree.setWordWrap(True) tree.header().setDefaultSectionSize(60) - tree.header().resizeSection(0,60) - tree.header().resizeSection(1,30) + tree.header().resizeSection(0, 60) + tree.header().resizeSection(1, 30) tree.header().setStretchLastSection(True) tree.headerItem().setText(0, "ID") tree.headerItem().setText(1, "") @@ -1815,57 +2098,57 @@ def explorer(filename,schema="IFC2X3_TC1.exp"): bold = QtGui.QFont() bold.setBold(True) - #print(ifc.Entities) + # print(ifc.Entities) for i in ifc.Entities.keys(): e = ifc.Entities[i] item = QtGui.QTreeWidgetItem(tree) - if hasattr(e,"id"): - item.setText(0,str(e.id)) - if e.type in ["IFCWALL","IFCWALLSTANDARDCASE"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Wall_Tree.svg")) - elif e.type in ["IFCCOLUMN","IFCBEAM","IFCSLAB","IFCFOOTING"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Structure_Tree.svg")) + if hasattr(e, "id"): + item.setText(0, str(e.id)) + if e.type in ["IFCWALL", "IFCWALLSTANDARDCASE"]: + item.setIcon(1, QtGui.QIcon(":icons/Arch_Wall_Tree.svg")) + elif e.type in ["IFCCOLUMN", "IFCBEAM", "IFCSLAB", "IFCFOOTING"]: + item.setIcon(1, QtGui.QIcon(":icons/Arch_Structure_Tree.svg")) elif e.type in ["IFCSITE"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Site_Tree.svg")) + item.setIcon(1, QtGui.QIcon(":icons/Arch_Site_Tree.svg")) elif e.type in ["IFCBUILDING"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Building_Tree.svg")) + item.setIcon(1, QtGui.QIcon(":icons/Arch_Building_Tree.svg")) elif e.type in ["IFCSTOREY"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Floor_Tree.svg")) + item.setIcon(1, QtGui.QIcon(":icons/Arch_Floor_Tree.svg")) elif e.type in ["IFCWINDOW"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Window_Tree.svg")) + item.setIcon(1, QtGui.QIcon(":icons/Arch_Window_Tree.svg")) elif e.type in ["IFCROOF"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Roof_Tree.svg")) - elif e.type in ["IFCEXTRUDEDAREASOLID","IFCCLOSEDSHELL"]: + item.setIcon(1, QtGui.QIcon(":icons/Arch_Roof_Tree.svg")) + elif e.type in ["IFCEXTRUDEDAREASOLID", "IFCCLOSEDSHELL"]: item.setIcon(1, QtGui.QIcon(":/icons/Part_3D_object.svg")) elif e.type in ["IFCFACE"]: - item.setIcon(1,QtGui.QIcon(":icons/Draft_SwitchMode.svg")) - elif e.type in ["IFCARBITRARYCLOSEDPROFILEDEF","IFCPOLYLOOP"]: - item.setIcon(1,QtGui.QIcon(":icons/Draft_Draft.svg")) - item.setText(2,str(schema.capitalize(e.type))) - item.setFont(2,bold); + item.setIcon(1, QtGui.QIcon(":icons/Draft_SwitchMode.svg")) + elif e.type in ["IFCARBITRARYCLOSEDPROFILEDEF", "IFCPOLYLOOP"]: + item.setIcon(1, QtGui.QIcon(":icons/Draft_Draft.svg")) + item.setText(2, str(schema.capitalize(e.type))) + item.setFont(2, bold) for a in e.attributes.keys(): - if hasattr(e,a): + if hasattr(e, a): if not a.upper() in ["ID", "GLOBALID"]: - v = getattr(e,a) - if isinstance(v,IfcEntity): + v = getattr(e, a) + if isinstance(v, IfcEntity): t = "Entity #" + str(v.id) + ": " + str(v.type) - elif isinstance(v,list): + elif isinstance(v, list): t = "" else: t = str(v) t = " " + str(a) + " : " + str(t) item = QtGui.QTreeWidgetItem(tree) - item.setText(2,str(t)) - if isinstance(v,list): + item.setText(2, str(t)) + if isinstance(v, list): for vi in v: - if isinstance(vi,IfcEntity): + if isinstance(vi, IfcEntity): t = "Entity #" + str(vi.id) + ": " + str(vi.type) else: t = vi t = " " + str(t) item = QtGui.QTreeWidgetItem(tree) - item.setText(2,str(t)) + item.setText(2, str(t)) d = QtGui.QDialog() d.setObjectName("IfcExplorer") @@ -1875,22 +2158,28 @@ def explorer(filename,schema="IFC2X3_TC1.exp"): layout.addWidget(tree) return d + # IfcWriter ######################################## + class _tempEntityHolder: """a temporary object to store entity references to be made into something nicer later...""" + def __init__(self): self.refs = [] + holder = _tempEntityHolder() + def uid(): """returns a suitable GlobalID""" u = str(uuid.uuid4())[:22] - u = u.replace("-","_") + u = u.replace("-", "_") return u + def now(string=False): "returns a suitable Ifc Time" if string: @@ -1898,12 +2187,13 @@ def now(string=False): else: return int(time.time()) + def getPropertyNames(entity): """getPropertyNames(entity): Returns a dictionary with the numbers and names of the pythonproperties available for this entity""" ents = {} - if hasattr(entity,"get_argument_count"): + if hasattr(entity, "get_argument_count"): l = entity.get_argument_count() else: l = len(entity) @@ -1911,40 +2201,45 @@ def getPropertyNames(entity): ents[i] = entity.get_argument_name(i) return ents + def getTuple(vec): """getTuple(vec): returns a tuple from other coordinate structures: tuple, list, 3d vector, or occ vertex""" + def fmt(t): t = float(t) - t = round(t,Draft.precision()) + t = round(t, Draft.precision()) return t - if isinstance(vec,tuple): + + if isinstance(vec, tuple): return tuple([fmt(v) for v in vec]) - elif isinstance(vec,list): + elif isinstance(vec, list): return tuple([fmt(v) for v in vec]) - elif hasattr(vec,"x") and hasattr(vec,"y") and hasattr(vec,"z"): - return (fmt(vec.x),fmt(vec.y),fmt(vec.z)) - elif hasattr(vec,"X") and hasattr(vec,"Y") and hasattr(vec,"Z"): - return (fmt(vec.X),fmt(vec.Y),fmt(vec.Z)) + elif hasattr(vec, "x") and hasattr(vec, "y") and hasattr(vec, "z"): + return (fmt(vec.x), fmt(vec.y), fmt(vec.z)) + elif hasattr(vec, "X") and hasattr(vec, "Y") and hasattr(vec, "Z"): + return (fmt(vec.X), fmt(vec.Y), fmt(vec.Z)) + def getValueAndDirection(vec): """getValueAndDirection(vec): returns a length and a tuple representing a normalized vector from a tuple""" pre = Draft.precision() vec = getTuple(vec) - length = round(math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2),pre) - ratio = 1/length - x = round(vec[0]*ratio,pre) - y = round(vec[1]*ratio,pre) - z = round(vec[2]*ratio,pre) - normal = (x,y,z) - return length,normal + length = round(math.sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2), pre) + ratio = 1 / length + x = round(vec[0] * ratio, pre) + y = round(vec[1] * ratio, pre) + z = round(vec[2] * ratio, pre) + normal = (x, y, z) + return length, normal -def create(ifcdoc=None,ifcname=None,arguments=[]): + +def create(ifcdoc=None, ifcname=None, arguments=[]): """create(ifcdoc,ifcname,[arguments]):creates an entity of the given name in the given document and optionally gives it an ordered list of arguments""" - if hasattr(ifcw,"Entity"): + if hasattr(ifcw, "Entity"): entity = ifcw.Entity(ifcname) else: entity = ifcw.entity_instance(ifcname) @@ -1952,17 +2247,17 @@ def create(ifcdoc=None,ifcname=None,arguments=[]): ifcdoc.add(entity) # this is a temporary hack while ifcopenshell has no ref counting holder.refs.append(entity) - if not isinstance(arguments,list): + if not isinstance(arguments, list): arguments = [arguments] for i in range(len(arguments)): arg = arguments[i] - if isinstance(arg,tuple): - if len(arg) in [2,3]: - if hasattr(ifcw,"Doubles"): + if isinstance(arg, tuple): + if len(arg) in [2, 3]: + if hasattr(ifcw, "Doubles"): arg = ifcw.Doubles(arg) else: arg = ifcw.doubles(arg) - entity.set_argument(i,arg) + entity.set_argument(i, arg) return entity @@ -1970,29 +2265,51 @@ class IfcWriter(object): """IfcWriter([filepath,name,owner,organization,application,version]) Creates an empty IFC document.""" - def __init__(self,filepath="",name="",owner="",organization="",application="Python IFC exporter",version="0.0"): - if hasattr(ifcw,"IfcFile"): + def __init__( + self, + filepath="", + name="", + owner="", + organization="", + application="Python IFC exporter", + version="0.0", + ): + if hasattr(ifcw, "IfcFile"): self._fileobject = ifcw.IfcFile() else: self._fileobject = ifcw.file() - self._person = create(self._fileobject,"IfcPerson",[None,None,"",None,None,None,None,None]) - self._org = create(self._fileobject,"IfcOrganization",[None,"",None,None,None]) - pno = create(self._fileobject,"IfcPersonAndOrganization",[self._person,self._org,None]) - app = create(self._fileobject,"IfcApplication",[self._org,version,application,uid()]) - self._owner = create(self._fileobject,"IfcOwnerHistory",[pno,app,None,"ADDED",None,pno,app,now()]) + self._person = create( + self._fileobject, "IfcPerson", [None, None, "", None, None, None, None, None] + ) + self._org = create(self._fileobject, "IfcOrganization", [None, "", None, None, None]) + pno = create(self._fileobject, "IfcPersonAndOrganization", [self._person, self._org, None]) + app = create(self._fileobject, "IfcApplication", [self._org, version, application, uid()]) + self._owner = create( + self._fileobject, "IfcOwnerHistory", [pno, app, None, "ADDED", None, pno, app, now()] + ) axp = self.addPlacement(local=False) - dim0 = create(self._fileobject,"IfcDirection",getTuple((0,1,0))) - self._repcontext = create(self._fileobject,"IfcGeometricRepresentationContext",['Plan','Model',3,1.E-05,axp,dim0]) - dim1 = create(self._fileobject,"IfcDimensionalExponents",[0,0,0,0,0,0,0]) - dim2 = create(self._fileobject,"IfcSIUnit",[dim1,"LENGTHUNIT","MILLI","METRE"]) - dim3 = create(self._fileobject,"IfcSIUnit",[dim1,"AREAUNIT",None,"SQUARE_METRE"]) - dim4 = create(self._fileobject,"IfcSIUnit",[dim1,"VOLUMEUNIT",None,"CUBIC_METRE"]) - dim6 = create(self._fileobject,"IfcSIUnit",[dim1,"PLANEANGLEUNIT",None,"RADIAN"]) - dim7 = create(None,"IfcPlaneAngleMeasure",[1.745E-2]) - dim8 = create(self._fileobject,"IfcMeasureWithUnit",[dim7,dim6]) - dim9 = create(self._fileobject,"IfcConversionBasedUnit",[dim1,"PLANEANGLEUNIT","DEGREE",dim8]) - units = create(self._fileobject,"IfcUnitAssignment",[[dim2,dim3,dim4,dim9]]) - self.Project = create(self._fileobject,"IfcProject",[uid(),self._owner,None,None,None,None,None,[self._repcontext],units]) + dim0 = create(self._fileobject, "IfcDirection", getTuple((0, 1, 0))) + self._repcontext = create( + self._fileobject, + "IfcGeometricRepresentationContext", + ["Plan", "Model", 3, 1.0e-05, axp, dim0], + ) + dim1 = create(self._fileobject, "IfcDimensionalExponents", [0, 0, 0, 0, 0, 0, 0]) + dim2 = create(self._fileobject, "IfcSIUnit", [dim1, "LENGTHUNIT", "MILLI", "METRE"]) + dim3 = create(self._fileobject, "IfcSIUnit", [dim1, "AREAUNIT", None, "SQUARE_METRE"]) + dim4 = create(self._fileobject, "IfcSIUnit", [dim1, "VOLUMEUNIT", None, "CUBIC_METRE"]) + dim6 = create(self._fileobject, "IfcSIUnit", [dim1, "PLANEANGLEUNIT", None, "RADIAN"]) + dim7 = create(None, "IfcPlaneAngleMeasure", [1.745e-2]) + dim8 = create(self._fileobject, "IfcMeasureWithUnit", [dim7, dim6]) + dim9 = create( + self._fileobject, "IfcConversionBasedUnit", [dim1, "PLANEANGLEUNIT", "DEGREE", dim8] + ) + units = create(self._fileobject, "IfcUnitAssignment", [[dim2, dim3, dim4, dim9]]) + self.Project = create( + self._fileobject, + "IfcProject", + [uid(), self._owner, None, None, None, None, None, [self._repcontext], units], + ) self.Site = None self._storeyRelations = {} self.BuildingProducts = [] @@ -2004,23 +2321,23 @@ class IfcWriter(object): self.Name = name def __repr__(self): - return "IFC document " + self.Name #+ " containing " + str(len(holder)) + " entities" + return "IFC document " + self.Name # + " containing " + str(len(holder)) + " entities" - def __setattr__(self,key,value): + def __setattr__(self, key, value): if value: if key == "Owner": - self._person.set_argument(2,str(value)) + self._person.set_argument(2, str(value)) elif key == "Organization": - self._org.set_argument(1,str(value)) + self._org.set_argument(1, str(value)) elif key == "Name": - self.Project.set_argument(2,str(value)) - self.__dict__.__setitem__(key,value) + self.Project.set_argument(2, str(value)) + self.__dict__.__setitem__(key, value) - def findByName(self,ifctype,name): + def findByName(self, ifctype, name): "finds an entity of a given ifctype by name" objs = self._fileobject.by_type(ifctype) for obj in objs: - if hasattr(obj,"get_argument_count"): + if hasattr(obj, "get_argument_count"): l = obj.get_argument_count() else: l = len(obj) @@ -2030,7 +2347,7 @@ class IfcWriter(object): return obj return None - def write(self,fp=None): + def write(self, fp=None): "writes the document to its file" if fp: path = fp @@ -2043,126 +2360,223 @@ class IfcWriter(object): print("IfcWriter: Applying fix...") self._fix(path) except Exception: - print("IfcWriter: Error writing to "+path) + print("IfcWriter: Error writing to " + path) else: - print("IfcWriter: Successfully written to "+path) + print("IfcWriter: Successfully written to " + path) else: print("IfcWriter: Error: File path is not defined, unable to save") - def _fix(self,path): + def _fix(self, path): "hack to fix early bugs in ifcopenshell" import os + if os.path.exists(path): - f = pyopen(path,"rb") + f = pyopen(path, "rb") lines = [] for l in f.readlines(): if "(=IFC" in l: # adding an ifc entity without ID adds an unwanted = sign - l = l.replace("(=IFC","(IFC") + l = l.replace("(=IFC", "(IFC") elif "IFCSIUNIT" in l: # no way to insert * character - l = l.replace("IFCSIUNIT(#12,","IFCSIUNIT(*,") + l = l.replace("IFCSIUNIT(#12,", "IFCSIUNIT(*,") lines.append(l) f.close() - f = pyopen(path,"wb") + f = pyopen(path, "wb") for l in lines: f.write(l) f.close() - def union(self,solids): + def union(self, solids): """union(solids): creates a boolean union between all the solids of the list""" if len(solids) == 1: return solids[0] else: s1 = solids.pop(0) s2 = solids.pop(0) - base = create(self._fileobject,"IfcBooleanResult",["UNION",s1,s2]) + base = create(self._fileobject, "IfcBooleanResult", ["UNION", s1, s2]) for s in solids: - base = create(self._fileobject,"IfcBooleanResult",["UNION",base,s]) + base = create(self._fileobject, "IfcBooleanResult", ["UNION", base, s]) return base - def addPlacement(self,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True,flat=False): + def addPlacement( + self, + reference=None, + origin=(0, 0, 0), + xaxis=(1, 0, 0), + zaxis=(0, 0, 1), + local=True, + flat=False, + ): """addPlacement([reference,origin,xaxis,zaxis,local]): adds a placement. origin, xaxis and zaxis can be either tuples or 3d vectors. If local is False, a global placement is returned, otherwise a local one.""" if flat: - xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)[:2]) - ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)[:2]) - gpl = create(self._fileobject,"IfcAxis2Placement2D",[ovc,xvc]) + xvc = create(self._fileobject, "IfcDirection", getTuple(xaxis)[:2]) + ovc = create(self._fileobject, "IfcCartesianPoint", getTuple(origin)[:2]) + gpl = create(self._fileobject, "IfcAxis2Placement2D", [ovc, xvc]) else: - xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)) - zvc = create(self._fileobject,"IfcDirection",getTuple(zaxis)) - ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)) - gpl = create(self._fileobject,"IfcAxis2Placement3D",[ovc,zvc,xvc]) + xvc = create(self._fileobject, "IfcDirection", getTuple(xaxis)) + zvc = create(self._fileobject, "IfcDirection", getTuple(zaxis)) + ovc = create(self._fileobject, "IfcCartesianPoint", getTuple(origin)) + gpl = create(self._fileobject, "IfcAxis2Placement3D", [ovc, zvc, xvc]) if local: - lpl = create(self._fileobject,"IfcLocalPlacement",[reference,gpl]) + lpl = create(self._fileobject, "IfcLocalPlacement", [reference, gpl]) return lpl else: return gpl - def addSite(self,placement=None,name="Site",description=None,latitude=None,longitude=None,elevation=None,landtitlenumber=None,address=None): + def addSite( + self, + placement=None, + name="Site", + description=None, + latitude=None, + longitude=None, + elevation=None, + landtitlenumber=None, + address=None, + ): """makeSite(ifcdoc,project,owner,[placement,name,description]): creates a site in the given ifc document""" if self.Site: return if not placement: placement = self.addPlacement() - self.Site = create(self._fileobject,"IfcSite",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",latitude,longitude,elevation,landtitlenumber,address]) - self._relate(self.Project,self.Site) + self.Site = create( + self._fileobject, + "IfcSite", + [ + uid(), + self._owner, + str(name), + description, + None, + placement, + None, + None, + "ELEMENT", + latitude, + longitude, + elevation, + landtitlenumber, + address, + ], + ) + self._relate(self.Project, self.Site) - def addBuilding(self,placement=None,name="Default building",description=None): + def addBuilding(self, placement=None, name="Default building", description=None): """addBuilding([placement,name,description]): adds a building""" if not placement: placement = self.addPlacement() if not self.Site: self.addSite() - bdg = create(self._fileobject,"IfcBuilding",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None,None,None]) - self._relate(self.Site,bdg) + bdg = create( + self._fileobject, + "IfcBuilding", + [ + uid(), + self._owner, + str(name), + description, + None, + placement, + None, + None, + "ELEMENT", + None, + None, + None, + ], + ) + self._relate(self.Site, bdg) self.Buildings.append(bdg) return bdg - def addStorey(self,building=None,placement=None,name="Default storey",description=None): + def addStorey(self, building=None, placement=None, name="Default storey", description=None): """addStorey([building,placement,name,description]): adds a storey""" if not placement: placement = self.addPlacement() - sto = create(self._fileobject,"IfcBuildingStorey",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None]) + sto = create( + self._fileobject, + "IfcBuildingStorey", + [ + uid(), + self._owner, + str(name), + description, + None, + placement, + None, + None, + "ELEMENT", + None, + ], + ) if not building: if self.Buildings: building = self.Buildings[0] else: building = self.addBuilding() - self._relate(building,sto) + self._relate(building, sto) self.Storeys.append(sto) return sto - def addGroup(self,entities,name="Default group",description=None): + def addGroup(self, entities, name="Default group", description=None): """addGroup(entities,[name,description]): adds a group with the given entities""" - if not isinstance(entities,list): + if not isinstance(entities, list): entities = [entities] - gro = create(self._fileobject,"IfcGroup",[uid(),self._owner,str(name),description,None]) - rel = create(self._fileobject,"IfcRelAssignsToGroup",[uid(),self._owner,str(name)+"-relation",None,entities,"PRODUCT",gro]) + gro = create( + self._fileobject, "IfcGroup", [uid(), self._owner, str(name), description, None] + ) + rel = create( + self._fileobject, + "IfcRelAssignsToGroup", + [uid(), self._owner, str(name) + "-relation", None, entities, "PRODUCT", gro], + ) return gro - def _relate(self,container,entities): + def _relate(self, container, entities): """relate(container,entities): relates the given entities to the given container""" - if not isinstance(entities,list): + if not isinstance(entities, list): entities = [entities] if container.is_a("IfcBuildingStorey"): sid = container.get_argument(0) if sid in self._storeyRelations: prods = self._storeyRelations[sid].get_argument(4) - self._storeyRelations[sid].set_argument(4,prods+entities) + self._storeyRelations[sid].set_argument(4, prods + entities) else: - rel = create(self._fileobject,"IfcRelContainedInSpatialStructure",[uid(),self._owner,'StoreyLink','',entities,container]) + rel = create( + self._fileobject, + "IfcRelContainedInSpatialStructure", + [uid(), self._owner, "StoreyLink", "", entities, container], + ) self._storeyRelations[sid] = rel else: if entities[0].is_a("IfcOpeningElement"): - create(self._fileobject,"IfcRelVoidsElement",[uid(),self._owner,'Opening','',container,entities[0]]) + create( + self._fileobject, + "IfcRelVoidsElement", + [uid(), self._owner, "Opening", "", container, entities[0]], + ) else: - create(self._fileobject,"IfcRelAggregates",[uid(),self._owner,'Relationship','',container,entities]) + create( + self._fileobject, + "IfcRelAggregates", + [uid(), self._owner, "Relationship", "", container, entities], + ) - def addProduct(self,elttype,shapes,storey=None,placement=None,name="Unnamed element",description=None,extra=None): + def addProduct( + self, + elttype, + shapes, + storey=None, + placement=None, + name="Unnamed element", + description=None, + extra=None, + ): """addProduct(elttype,representations,[storey,placement,name,description,extra]): creates an element of the given type (IfcWall, IfcBeam, etc...) with the given attributes, plus the given extra attributes.""" elttype = str(elttype) @@ -2173,21 +2587,30 @@ class IfcWriter(object): if not placement: placement = self.addPlacement() representations = self.addRepresentations(shapes) - prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,representations]) + prd = create(self._fileobject, "IfcProductDefinitionShape", [None, None, representations]) try: - elt = create(self._fileobject,elttype,[uid(),self._owner,name,description,None,placement,prd,None]+extra) + elt = create( + self._fileobject, + elttype, + [uid(), self._owner, name, description, None, placement, prd, None] + extra, + ) except Exception: - print("unable to create an ",elttype, " with attributes: ",[uid(),self._owner,str(name),description,None,placement,prd,None]+extra) + print( + "unable to create an ", + elttype, + " with attributes: ", + [uid(), self._owner, str(name), description, None, placement, prd, None] + extra, + ) try: - if hasattr(ifcw,"Entity"): + if hasattr(ifcw, "Entity"): o = ifcw.Entity(elttype) else: o = ifcw.entity_instance(elttype) print("supported attributes are: ") print(getPropertyNames(o)) except Exception: - print("unable to create an element of type '"+elttype+"'") - print("WARNING: skipping object '"+name+"' of type "+elttype) + print("unable to create an element of type '" + elttype + "'") + print("WARNING: skipping object '" + name + "' of type " + elttype) return None self.BuildingProducts.append(elt) if not storey: @@ -2195,29 +2618,39 @@ class IfcWriter(object): storey = self.Storeys[0] else: storey = self.addStorey() - self._relate(storey,elt) + self._relate(storey, elt) return elt - def addRepresentations(self,shapes): + def addRepresentations(self, shapes): """addRepresentations(shapes,[solidType]): creates a representation from the given shape""" solidType = "Brep" - if not isinstance(shapes,list): + if not isinstance(shapes, list): if shapes.is_a("IfcExtrudedAreaSolid"): solidType = "SweptSolid" shapes = [shapes] - reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape for shape in shapes]])] + reps = [ + create( + self._fileobject, + "IfcShapeRepresentation", + [self._repcontext, "Body", solidType, [shape for shape in shapes]], + ) + ] return reps - def addColor(self,rgb,rep): + def addColor(self, rgb, rep): """addColor(rgb,rep): adds a RGB color definition tuple (float,float,float) to a given representation""" - col = create(self._fileobject,"IfcColourRgb",[None]+list(rgb)) - ssr = create(self._fileobject,"IfcSurfaceStyleRendering",[col,None,None,None,None,None,None,None,"FLAT"]) - iss = create(self._fileobject,"IfcSurfaceStyle",[None,"BOTH",[ssr]]) - psa = create(self._fileobject,"IfcPresentationStyleAssignment",[[iss]]) - isi = create(self._fileobject,"IfcStyledItem",[rep,[psa],None]) + col = create(self._fileobject, "IfcColourRgb", [None] + list(rgb)) + ssr = create( + self._fileobject, + "IfcSurfaceStyleRendering", + [col, None, None, None, None, None, None, None, "FLAT"], + ) + iss = create(self._fileobject, "IfcSurfaceStyle", [None, "BOTH", [ssr]]) + psa = create(self._fileobject, "IfcPresentationStyleAssignment", [[iss]]) + isi = create(self._fileobject, "IfcStyledItem", [rep, [psa], None]) return isi - def addProfile(self,ifctype,data,curvetype="AREA"): + def addProfile(self, ifctype, data, curvetype="AREA"): """addProfile(ifctype,data): creates a 2D profile of the given type, with the given data as arguments, which must be formatted correctly according to the type.""" @@ -2230,132 +2663,146 @@ class IfcWriter(object): # IfcEllipseProfileDef: [ (0,0,0), 15, 7 ] # center, radiusX, radiusY if ifctype == "IfcPolyline": - pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in data] - pol = create(self._fileobject,"IfcPolyline",[pts]) - profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,pol]) + pts = [create(self._fileobject, "IfcCartesianPoint", getTuple(p)[:2]) for p in data] + pol = create(self._fileobject, "IfcPolyline", [pts]) + profile = create( + self._fileobject, "IfcArbitraryClosedProfileDef", [curvetype, None, pol] + ) elif ifctype == "IfcCompositeCurve": curves = [] for curve in data: cur = None if curve[0] == "line": - pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in curve[1]] - cur = create(self._fileobject,"IfcPolyline",[pts]) + pts = [ + create(self._fileobject, "IfcCartesianPoint", getTuple(p)[:2]) + for p in curve[1] + ] + cur = create(self._fileobject, "IfcPolyline", [pts]) elif curve[0] == "arc": - pla = self.addPlacement(origin=curve[1],local=False,flat=True) - cir = create(self._fileobject,"IfcCircle",[pla,curve[2]]) + pla = self.addPlacement(origin=curve[1], local=False, flat=True) + cir = create(self._fileobject, "IfcCircle", [pla, curve[2]]) if curve[5] == "CARTESIAN": # BUGGY! Impossible to add cartesian points as "embedded" entity - trim1 = create(None,"IfcCartesianPoint",getTuple(curve[3][0])[:2]) - trim2 = create(None,"IfcCartesianPoint",getTuple(curve[3][1])[:2]) + trim1 = create(None, "IfcCartesianPoint", getTuple(curve[3][0])[:2]) + trim2 = create(None, "IfcCartesianPoint", getTuple(curve[3][1])[:2]) else: - trim1 = create(None,"IfcParameterValue",[curve[3][0]]) - trim2 = create(None,"IfcParameterValue",[curve[3][1]]) - cur = create(self._fileobject,"IfcTrimmedCurve",[cir,[trim1],[trim2],curve[4],curve[5]]) + trim1 = create(None, "IfcParameterValue", [curve[3][0]]) + trim2 = create(None, "IfcParameterValue", [curve[3][1]]) + cur = create( + self._fileobject, + "IfcTrimmedCurve", + [cir, [trim1], [trim2], curve[4], curve[5]], + ) if cur: - seg = create(self._fileobject,"IfcCompositeCurveSegment",["CONTINUOUS",True,cur]) + seg = create( + self._fileobject, "IfcCompositeCurveSegment", ["CONTINUOUS", True, cur] + ) curves.append(seg) - ccu = create(self._fileobject,"IfcCompositeCurve",[curves,False]) - profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,ccu]) + ccu = create(self._fileobject, "IfcCompositeCurve", [curves, False]) + profile = create( + self._fileobject, "IfcArbitraryClosedProfileDef", [curvetype, None, ccu] + ) else: - if not isinstance(data,list): + if not isinstance(data, list): data = [data] - p = self.addPlacement(local=False,flat=True) - profile = create(self._fileobject,ifctype,[curvetype,None,p]+data) + p = self.addPlacement(local=False, flat=True) + profile = create(self._fileobject, ifctype, [curvetype, None, p] + data) return profile - def addExtrusion(self,profile,extrusion,placement=None): + def addExtrusion(self, profile, extrusion, placement=None): """addExtrusion(profile,extrusion,[placement]): makes an extrusion of the given polyline with the given extrusion vector""" if not placement: placement = self.addPlacement(local=False) - value,norm = getValueAndDirection(extrusion) - edir = create(self._fileobject,"IfcDirection",[norm]) - solid = create(self._fileobject,"IfcExtrudedAreaSolid",[profile,placement,edir,value]) + value, norm = getValueAndDirection(extrusion) + edir = create(self._fileobject, "IfcDirection", [norm]) + solid = create(self._fileobject, "IfcExtrudedAreaSolid", [profile, placement, edir, value]) return solid - def addExtrudedPolyline(self,points,extrusion,placement=None,color=None): + def addExtrudedPolyline(self, points, extrusion, placement=None, color=None): """addExtrudedPolyline(points,extrusion,[placement,color]): makes an extruded polyline from the given points and the given extrusion vector""" - pol = self.addProfile("IfcPolyline",points) + pol = self.addProfile("IfcPolyline", points) if not placement: placement = self.addPlacement(local=False) - exp = self.addExtrusion(pol,extrusion,placement) + exp = self.addExtrusion(pol, extrusion, placement) if color: - self.addColor(color,exp) + self.addColor(color, exp) return exp - def addExtrudedCircle(self,data,extrusion,placement=None,color=None): + def addExtrudedCircle(self, data, extrusion, placement=None, color=None): """addExtrudedCircle(data,extrusion,[placement,color]): makes an extruded circle from the given data (center,radius) and the given extrusion vector""" - cir = self.addProfile("IfcCircleProfileDef",data[1]) + cir = self.addProfile("IfcCircleProfileDef", data[1]) if not placement: - placement = self.addPlacement(origin=data[0],local=False) - exp = self.addExtrusion(cir,extrusion,placement) + placement = self.addPlacement(origin=data[0], local=False) + exp = self.addExtrusion(cir, extrusion, placement) if color: - self.addColor(color,exp) + self.addColor(color, exp) return exp - def addExtrudedEllipse(self,data,extrusion,placement=None,color=None): + def addExtrudedEllipse(self, data, extrusion, placement=None, color=None): """addExtrudedEllipse(data,extrusion,[placement,color]): makes an extruded ellipse from the given data (center,radiusx,radiusy) and the given extrusion vector""" - cir = self.addProfile("IfcEllipseProfileDef",[data[1],data[2]]) + cir = self.addProfile("IfcEllipseProfileDef", [data[1], data[2]]) if not placement: - placement = self.addPlacement(origin=data[0],local=False) - exp = self.addExtrusion(cir,extrusion,placement) + placement = self.addPlacement(origin=data[0], local=False) + exp = self.addExtrusion(cir, extrusion, placement) if color: - self.addColor(color,exp) + self.addColor(color, exp) return exp - def addExtrudedCompositeCurve(self,curves,extrusion,placement=None,color=None): + def addExtrudedCompositeCurve(self, curves, extrusion, placement=None, color=None): """addExtrudedCompositeCurve(curves,extrusion,[placement,color]): makes an extruded polyline from the given curves and the given extrusion vector""" if not placement: placement = self.addPlacement(local=False) - ccu = self.addProfile("IfcCompositeCurve",curves) - exp = self.addExtrusion(ccu,extrusion,placement) + ccu = self.addProfile("IfcCompositeCurve", curves) + exp = self.addExtrusion(ccu, extrusion, placement) if color: - self.addColor(color,exp) + self.addColor(color, exp) return exp - def addFace(self,face): + def addFace(self, face): """addFace(face): creates a face from the given face data (a list of lists of points). - The first is the outer wire, the next are optional inner wires. They must be reversed in order""" + The first is the outer wire, the next are optional inner wires. They must be reversed in order + """ ifb = [] idx = 0 for f in face: pts = [] for p in f: - #print(p) + # print(p) if p in self.fpoints: - #print(self.fpoints.index(p)) - #print(self.frefs) + # print(self.fpoints.index(p)) + # print(self.frefs) pts.append(self.frefs[self.fpoints.index(p)]) else: - pt = create(self._fileobject,"IfcCartesianPoint",getTuple(p)) + pt = create(self._fileobject, "IfcCartesianPoint", getTuple(p)) pts.append(pt) self.fpoints.append(p) self.frefs.append(pt) - #print(pts) - loop = create(self._fileobject,"IfcPolyLoop",[pts]) + # print(pts) + loop = create(self._fileobject, "IfcPolyLoop", [pts]) if idx == 0: - fb = create(self._fileobject,"IfcFaceOuterBound",[loop,True]) + fb = create(self._fileobject, "IfcFaceOuterBound", [loop, True]) else: - fb = create(self._fileobject,"IfcFaceBound",[loop,True]) + fb = create(self._fileobject, "IfcFaceBound", [loop, True]) ifb.append(fb) idx += 1 - iface = create(self._fileobject,"IfcFace",[ifb]) + iface = create(self._fileobject, "IfcFace", [ifb]) return iface - def addFacetedBrep(self,faces,color=None): + def addFacetedBrep(self, faces, color=None): """addFacetedBrep(self,faces,[color]): creates a faceted brep object from the given list of faces (each face is a list of lists of points, inner wires are reversed)""" self.fpoints = [] self.frefs = [] - #print("adding ",len(faces)," faces") - #print(faces) + # print("adding ",len(faces)," faces") + # print(faces) ifaces = [self.addFace(face) for face in faces] - sh = create(self._fileobject,"IfcClosedShell",[ifaces]) - brp = create(self._fileobject,"IfcFacetedBrep",[sh]) + sh = create(self._fileobject, "IfcClosedShell", [ifaces]) + brp = create(self._fileobject, "IfcFacetedBrep", [sh]) if color: - self.addColor(color,brp) + self.addColor(color, brp) return brp diff --git a/src/Mod/BIM/importers/importIFCmulticore.py b/src/Mod/BIM/importers/importIFCmulticore.py index 8aeea1eadb..8fcda13fd2 100644 --- a/src/Mod/BIM/importers/importIFCmulticore.py +++ b/src/Mod/BIM/importers/importIFCmulticore.py @@ -39,23 +39,21 @@ from importers import importIFCHelper # global dicts to store ifc object/freecad object relationships -layers = {} # ifcid : Draft_Layer -materials = {} #ifcid : Arch_Material -objects = {} #ifcid : Arch_Component -subs = {} #host_ifcid: [child_ifcid,...] -adds = {} #host_ifcid: [child_ifcid,...] -colors = {} # objname : (r,g,b) +layers = {} # ifcid : Draft_Layer +materials = {} # ifcid : Arch_Material +objects = {} # ifcid : Arch_Component +subs = {} # host_ifcid: [child_ifcid,...] +adds = {} # host_ifcid: [child_ifcid,...] +colors = {} # objname : (r,g,b) def open(filename): - "opens an IFC file in a new document" return insert(filename) -def insert(filename,docname=None,preferences=None): - +def insert(filename, docname=None, preferences=None): """imports the contents of an IFC file in the given document""" import ifcopenshell @@ -74,21 +72,21 @@ def insert(filename,docname=None,preferences=None): subs = {} # statistics - starttime = time.time() # in seconds - filesize = os.path.getsize(filename) * 0.000001 # in megabytes - print("Opening",filename+",",round(filesize,2),"Mb") + starttime = time.time() # in seconds + filesize = os.path.getsize(filename) * 0.000001 # in megabytes + print("Opening", filename + ",", round(filesize, 2), "Mb") # setup ifcopenshell if not preferences: preferences = importIFCHelper.getPreferences() settings = ifcopenshell.geom.settings() - settings.set(settings.USE_BREP_DATA,True) - settings.set(settings.SEW_SHELLS,True) - settings.set(settings.USE_WORLD_COORDS,True) - if preferences['SEPARATE_OPENINGS']: - settings.set(settings.DISABLE_OPENING_SUBTRACTIONS,True) - if preferences['SPLIT_LAYERS'] and hasattr(settings,"APPLY_LAYERSETS"): - settings.set(settings.APPLY_LAYERSETS,True) + settings.set(settings.USE_BREP_DATA, True) + settings.set(settings.SEW_SHELLS, True) + settings.set(settings.USE_WORLD_COORDS, True) + if preferences["SEPARATE_OPENINGS"]: + settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True) + if preferences["SPLIT_LAYERS"] and hasattr(settings, "APPLY_LAYERSETS"): + settings.set(settings.APPLY_LAYERSETS, True) # setup document if not FreeCAD.ActiveDocument: @@ -102,9 +100,9 @@ def insert(filename,docname=None,preferences=None): ifcfile = ifcopenshell.open(filename) progressbar = Base.ProgressIndicator() productscount = len(ifcfile.by_type("IfcProduct")) - progressbar.start("Importing "+str(productscount)+" products...",productscount) + progressbar.start("Importing " + str(productscount) + " products...", productscount) cores = preferences["MULTICORE"] - iterator = ifcopenshell.geom.iterator(settings,ifcfile,cores) + iterator = ifcopenshell.geom.iterator(settings, ifcfile, cores) iterator.initialize() count = 0 @@ -112,18 +110,20 @@ def insert(filename,docname=None,preferences=None): for item in iterator: brep = item.geometry.brep_data ifcproduct = ifcfile.by_id(item.guid) - obj = createProduct(ifcproduct,brep) + obj = createProduct(ifcproduct, brep) progressbar.next(True) - writeProgress(count,productscount,starttime) + writeProgress(count, productscount, starttime) count += 1 # process 2D annotations annotations = ifcfile.by_type("IfcAnnotation") if annotations: - print("Processing",str(len(annotations)),"annotations...") + print("Processing", str(len(annotations)), "annotations...") ifcscale = importIFCHelper.getScaling(ifcfile) for annotation in annotations: - importIFCHelper.createAnnotation(annotation,FreeCAD.ActiveDocument,ifcscale,preferences) + importIFCHelper.createAnnotation( + annotation, FreeCAD.ActiveDocument, ifcscale, preferences + ) # post-processing processRelationships() @@ -132,62 +132,59 @@ def insert(filename,docname=None,preferences=None): # finished progressbar.stop() FreeCAD.ActiveDocument.recompute() - endtime = round(time.time()-starttime,1) - fs = round(filesize,1) - ratio = int(endtime/filesize) + endtime = round(time.time() - starttime, 1) + fs = round(filesize, 1) + ratio = int(endtime / filesize) endtime = "%02d:%02d" % (divmod(endtime, 60)) - writeProgress() # this cleans the line - print("Finished importing",fs,"Mb in",endtime,"s, or",ratio,"s/Mb") + writeProgress() # this cleans the line + print("Finished importing", fs, "Mb in", endtime, "s, or", ratio, "s/Mb") return FreeCAD.ActiveDocument -def writeProgress(count=None,total=None,starttime=None): - +def writeProgress(count=None, total=None, starttime=None): """write progress to console""" if not FreeCAD.GuiUp: if count is None: sys.stdout.write("\r") return - r = count/total - elapsed = round(time.time()-starttime,1) + r = count / total + elapsed = round(time.time() - starttime, 1) if r: - rest = elapsed*((1-r)/r) + rest = elapsed * ((1 - r) / r) eta = "%02d:%02d" % (divmod(rest, 60)) else: eta = "--:--" - hashes = '#'*int(r*10)+' '*int(10-r*10) - fstring = '\rImporting '+str(total)+' products [{0}] {1}%, ETA: {2}' - sys.stdout.write(fstring.format(hashes, int(r*100),eta)) + hashes = "#" * int(r * 10) + " " * int(10 - r * 10) + fstring = "\rImporting " + str(total) + " products [{0}] {1}%, ETA: {2}" + sys.stdout.write(fstring.format(hashes, int(r * 100), eta)) -def createProduct(ifcproduct,brep): - +def createProduct(ifcproduct, brep): """creates an Arch object from an IFC product""" import Part shape = Part.Shape() - shape.importBrepFromString(brep,False) - shape.scale(1000.0) # IfcOpenShell outputs in meters + shape.importBrepFromString(brep, False) + shape.scale(1000.0) # IfcOpenShell outputs in meters if ifcproduct.is_a("IfcSpace"): obj = Arch.makeSpace() else: obj = Arch.makeComponent() obj.Shape = shape objects[ifcproduct.id()] = obj - setAttributes(obj,ifcproduct) - setProperties(obj,ifcproduct) - createLayer(obj,ifcproduct) - createMaterial(obj,ifcproduct) - createModelStructure(obj,ifcproduct) - setRelationships(obj,ifcproduct) - setColor(obj,ifcproduct) + setAttributes(obj, ifcproduct) + setProperties(obj, ifcproduct) + createLayer(obj, ifcproduct) + createMaterial(obj, ifcproduct) + createModelStructure(obj, ifcproduct) + setRelationships(obj, ifcproduct) + setColor(obj, ifcproduct) return obj -def setAttributes(obj,ifcproduct): - +def setAttributes(obj, ifcproduct): """sets the IFC attributes of a component""" ifctype = ArchIFC.uncamel(ifcproduct.is_a()) @@ -197,16 +194,15 @@ def setAttributes(obj,ifcproduct): obj.IfcType = ifctype for attr in dir(ifcproduct): if attr in obj.PropertiesList: - value = getattr(ifcproduct,attr) + value = getattr(ifcproduct, attr) if value: try: - setattr(obj,attr,value) + setattr(obj, attr, value) except Exception: pass -def setProperties(obj,ifcproduct): - +def setProperties(obj, ifcproduct): """sets the IFC properties of a component""" props = obj.IfcProperties @@ -215,14 +211,13 @@ def setProperties(obj,ifcproduct): pset = prel.RelatingPropertyDefinition if pset.is_a("IfcPropertySet"): for prop in pset.HasProperties: - if hasattr(prop,"NominalValue"): - propname = prop.Name+";;"+pset.Name + if hasattr(prop, "NominalValue"): + propname = prop.Name + ";;" + pset.Name v = [p.strip("'") for p in str(prop.NominalValue).strip(")").split(")")] propvalue = ";;".join(v) -def setColor(obj,ifcproduct): - +def setColor(obj, ifcproduct): """sets the color of an object""" global colors @@ -233,8 +228,7 @@ def setColor(obj,ifcproduct): obj.ViewObject.ShapeColor = color[:3] -def createLayer(obj,ifcproduct): - +def createLayer(obj, ifcproduct): """sets the layer of a component""" global layers @@ -244,11 +238,10 @@ def createLayer(obj,ifcproduct): for layer in rep.LayerAssignments: if not layer.id() in layers: layers[layer.id()] = Draft.make_layer(layer.Name) - layers[layer.id()].Proxy.addObject(layers[layer.id()],obj) + layers[layer.id()].Proxy.addObject(layers[layer.id()], obj) -def createMaterial(obj,ifcproduct): - +def createMaterial(obj, ifcproduct): """sets the material of a component""" global materials @@ -257,16 +250,15 @@ def createMaterial(obj,ifcproduct): if association.is_a("IfcRelAssociatesMaterial"): material = association.RelatingMaterial if material.is_a("IfcMaterialList"): - material = material.Materials[0] # take the first one for now... + material = material.Materials[0] # take the first one for now... if material.is_a("IfcMaterial"): if not material.id() in materials: color = importIFCHelper.getColorFromMaterial(material) - materials[material.id()] = Arch.makeMaterial(material.Name,color=color) + materials[material.id()] = Arch.makeMaterial(material.Name, color=color) obj.Material = materials[material.id()] -def createModelStructure(obj,ifcobj): - +def createModelStructure(obj, ifcobj): """sets the parent containers of an IFC object""" global objects @@ -279,48 +271,47 @@ def createModelStructure(obj,ifcobj): parentobj = Arch.makeSite() else: parentobj = Arch.makeBuildingPart() - setAttributes(parentobj,parent) - setProperties(parentobj,parent) - createModelStructure(parentobj,parent) + setAttributes(parentobj, parent) + setProperties(parentobj, parent) + createModelStructure(parentobj, parent) objects[parent.id()] = parentobj - if hasattr(objects[parent.id()].Proxy,"addObject"): - objects[parent.id()].Proxy.addObject(objects[parent.id()],obj) + if hasattr(objects[parent.id()].Proxy, "addObject"): + objects[parent.id()].Proxy.addObject(objects[parent.id()], obj) -def setRelationships(obj,ifcobj): - +def setRelationships(obj, ifcobj): """sets additions/subtractions""" global adds global subs - if hasattr(ifcobj,"HasOpenings") and ifcobj.HasOpenings: + if hasattr(ifcobj, "HasOpenings") and ifcobj.HasOpenings: for rel in ifcobj.HasOpenings: - subs.setdefault(ifcobj.id(),[]).append(rel.RelatedOpeningElement) + subs.setdefault(ifcobj.id(), []).append(rel.RelatedOpeningElement) # TODO: assemblies & booleans def processRelationships(): - """process all stored relationships""" - for dom in ((subs,"Subtractions"),(adds,"Additions")): - for key,vals in dom[0].items(): + for dom in ((subs, "Subtractions"), (adds, "Additions")): + for key, vals in dom[0].items(): if key in objects: for val in vals: if val in objects: - if hasattr(objects[key],dom[1]): - g = getattr(objects[key],dom[1]) + if hasattr(objects[key], dom[1]): + g = getattr(objects[key], dom[1]) g.append(val) - setattr(objects[key],dom[1],g) + setattr(objects[key], dom[1], g) + def storeColorDict(): - """stores the color dictionary in the document Meta if non-GUI mode""" if colors and not FreeCAD.GuiUp: import json + d = FreeCAD.ActiveDocument.Meta d["colordict"] = json.dumps(colors) FreeCAD.ActiveDocument.Meta = d diff --git a/src/Mod/BIM/importers/importJSON.py b/src/Mod/BIM/importers/importJSON.py index d371953917..792e5c9c2a 100644 --- a/src/Mod/BIM/importers/importJSON.py +++ b/src/Mod/BIM/importers/importJSON.py @@ -38,7 +38,9 @@ if FreeCAD.GuiUp: else: FreeCADGui = None - def translate(ctxt, txt): return txt + + def translate(ctxt, txt): + return txt def export(exportList, filename): @@ -46,28 +48,27 @@ def export(exportList, filename): # Convert objects data = { - 'version': '0.0.1', - 'description': 'Mesh data exported from FreeCAD', - 'objects': [getObjectData(obj) for obj in exportList] - } + "version": "0.0.1", + "description": "Mesh data exported from FreeCAD", + "objects": [getObjectData(obj) for obj in exportList], + } # Write file outfile = pyopen(filename, "w") - json.dump(data, outfile, separators = (',', ':')) + json.dump(data, outfile, separators=(",", ":")) outfile.close() # Success - FreeCAD.Console.PrintMessage( - translate("Arch", "Successfully written") + ' ' + filename + "\n") + FreeCAD.Console.PrintMessage(translate("Arch", "Successfully written") + " " + filename + "\n") def getObjectData(obj): - result = {'name': str(obj.Label.encode("utf8"))} - if hasattr(obj, "Description"): result['description'] = str(obj.Description) + result = {"name": str(obj.Label.encode("utf8"))} + if hasattr(obj, "Description"): + result["description"] = str(obj.Description) if FreeCADGui: - result['color'] = \ - Draft.getrgb(obj.ViewObject.ShapeColor, testbw = False) + result["color"] = Draft.getrgb(obj.ViewObject.ShapeColor, testbw=False) if obj.isDerivedFrom("Part::Feature"): mesh = Mesh.Mesh(obj.Shape.tessellate(0.1)) @@ -77,12 +78,12 @@ def getObjectData(obj): for f in obj.Shape.Faces: for w in f.Wires: wo = Part.Wire(Part.__sortEdges__(w.Edges)) - wires.append([[v.x, v.y, v.z] - for v in wo.discretize(QuasiDeflection = 0.1)]) + wires.append([[v.x, v.y, v.z] for v in wo.discretize(QuasiDeflection=0.1)]) - result['wires'] = wires + result["wires"] = wires - elif obj.isDerivedFrom("Mesh::Feature"): mesh = obj.Mesh + elif obj.isDerivedFrom("Mesh::Feature"): + mesh = obj.Mesh # Add vertices count = 0 @@ -95,13 +96,13 @@ def getObjectData(obj): count += 1 vertices.append([v.x, v.y, v.z]) - result['vertices'] = vertices + result["vertices"] = vertices # Add facets & normals facets = [[vIndex[i] for i in f.PointIndices] for f in mesh.Facets] normals = [[f.Normal.x, f.Normal.y, f.Normal.z] for f in mesh.Facets] - result['normals'] = normals - result['facets'] = facets + result["normals"] = normals + result["facets"] = facets return result diff --git a/src/Mod/BIM/importers/importOBJ.py b/src/Mod/BIM/importers/importOBJ.py index 3cf9397046..e4d36336cd 100644 --- a/src/Mod/BIM/importers/importOBJ.py +++ b/src/Mod/BIM/importers/importOBJ.py @@ -50,22 +50,24 @@ if FreeCAD.GuiUp: from draftutils.translate import translate else: # \cond - def translate(context,text): + def translate(context, text): return text + # \endcond -def findVert(aVertex,aList): +def findVert(aVertex, aList): "finds aVertex in aList, returns index" p = Draft.precision() for i in range(len(aList)): - if round(aVertex.X,p) == round(aList[i].X,p): - if round(aVertex.Y,p) == round(aList[i].Y,p): - if round(aVertex.Z,p) == round(aList[i].Z,p): + if round(aVertex.X, p) == round(aList[i].X, p): + if round(aVertex.Y, p) == round(aList[i].Y, p): + if round(aVertex.Z, p) == round(aList[i].Z, p): return i return None -def getIndices(obj,shape,offsetv,offsetvn): + +def getIndices(obj, shape, offsetv, offsetvn): "returns a list with 2 lists: vertices and face indexes, offset with the given amount" p = Draft.precision() vlist = [] @@ -75,39 +77,66 @@ def getIndices(obj,shape,offsetv,offsetvn): hascurve = False mesh = None - if isinstance(shape,Part.Shape): + if isinstance(shape, Part.Shape): for e in shape.Edges: try: - if not isinstance(e.Curve,Part.Line): + if not isinstance(e.Curve, Part.Line): hascurve = True - except Exception: # unimplemented curve type + except Exception: # unimplemented curve type hascurve = True if hascurve: - tol = params.get_param("MaxDeviationExport",path="Mod/Mesh") + tol = params.get_param("MaxDeviationExport", path="Mod/Mesh") mesh = Mesh.Mesh() mesh.addFacets(shape.getFaces(tol)) - FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") + FreeCAD.Console.PrintWarning( + translate("Arch", "Found a shape containing curves, triangulating") + "\n" + ) break - elif isinstance(shape,Mesh.Mesh): + elif isinstance(shape, Mesh.Mesh): mesh = shape if mesh: for v in mesh.Topology[0]: - vlist.append(" "+str(round(v[0],p))+" "+str(round(v[1],p))+" "+str(round(v[2],p))) + vlist.append( + " " + str(round(v[0], p)) + " " + str(round(v[1], p)) + " " + str(round(v[2], p)) + ) for vn in mesh.Facets: - vnlist.append(" "+str(round(vn.Normal[0],p))+" "+str(round(vn.Normal[1],p))+" "+str(round(vn.Normal[2],p))) + vnlist.append( + " " + + str(round(vn.Normal[0], p)) + + " " + + str(round(vn.Normal[1], p)) + + " " + + str(round(vn.Normal[2], p)) + ) for i, vn in enumerate(mesh.Topology[1]): - flist.append(" "+str(vn[0]+offsetv)+"//"+str(i+offsetvn)+" "+str(vn[1]+offsetv)+"//"+str(i+offsetvn)+" "+str(vn[2]+offsetv)+"//"+str(i+offsetvn)+" ") + flist.append( + " " + + str(vn[0] + offsetv) + + "//" + + str(i + offsetvn) + + " " + + str(vn[1] + offsetv) + + "//" + + str(i + offsetvn) + + " " + + str(vn[2] + offsetv) + + "//" + + str(i + offsetvn) + + " " + ) else: for v in shape.Vertexes: - vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) + vlist.append( + " " + str(round(v.X, p)) + " " + str(round(v.Y, p)) + " " + str(round(v.Z, p)) + ) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": - ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offsetv) - ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offsetv) + ei = " " + str(findVert(e.Vertexes[0], shape.Vertexes) + offsetv) + ei += " " + str(findVert(e.Vertexes[-1], shape.Vertexes) + offsetv) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: @@ -117,7 +146,7 @@ def getIndices(obj,shape,offsetv,offsetvn): fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) - fi += " " + str(findVert(vdata,shape.Vertexes) + offsetv) + fi += " " + str(findVert(vdata, shape.Vertexes) + offsetv) flist.append(fi) else: fi = "" @@ -127,17 +156,16 @@ def getIndices(obj,shape,offsetv,offsetvn): edges.reverse() for e in edges: v = e.Vertexes[0 if e.Orientation == "Forward" else 1] - ind = findVert(v,shape.Vertexes) + ind = findVert(v, shape.Vertexes) if ind is None: - return None,None,None,None + return None, None, None, None fi += " " + str(ind + offsetv) flist.append(fi) - return vlist,vnlist,elist,flist + return vlist, vnlist, elist, flist -def export(exportList,filename,colors=None): - +def export(exportList, filename, colors=None): """export(exportList,filename,colors=None): Called when freecad exports a file. exportList is a list of objects, filename is the .obj file to export (a .mtl @@ -145,14 +173,13 @@ def export(exportList,filename,colors=None): optionally colors can be a dict containing ["objectName:colorTuple"] pairs for use in non-GUI mode.""" - outfile = codecs.open(filename,"wb",encoding="utf8") + outfile = codecs.open(filename, "wb", encoding="utf8") ver = FreeCAD.Version() outfile.write("# FreeCAD v" + ver[0] + "." + ver[1] + " build" + ver[2] + " Arch module\n") outfile.write("# https://www.freecad.org\n") offsetv = 1 offsetvn = 1 - objectslist = Draft.get_group_contents(exportList, walls=True, - addgroups=True) + objectslist = Draft.get_group_contents(exportList, walls=True, addgroups=True) objectslist = Arch.pruneIncluded(objectslist, strict=True) filenamemtl = filename[:-4] + ".mtl" materials = [] @@ -166,9 +193,11 @@ def export(exportList,filename,colors=None): if not visible: continue - if not (obj.isDerivedFrom("Part::Feature") - or obj.isDerivedFrom("Mesh::Feature") - or obj.isDerivedFrom("App::Link")): + if not ( + obj.isDerivedFrom("Part::Feature") + or obj.isDerivedFrom("Mesh::Feature") + or obj.isDerivedFrom("App::Link") + ): continue if obj.isDerivedFrom("App::Link"): @@ -177,7 +206,9 @@ def export(exportList,filename,colors=None): parents = obj.Parents if parents: doc = obj.Document - parents = [parents[0][0]] + [doc.getObject(name) for name in parents[0][1].split(".")[:-2]] + parents = [parents[0][0]] + [ + doc.getObject(name) for name in parents[0][1].split(".")[:-2] + ] parents.reverse() # child-parent-grandparent order. for parent in parents: obj_place = parent.Placement.multiply(obj_place) @@ -188,36 +219,40 @@ def export(exportList,filename,colors=None): if FreeCAD.GuiUp: if obj.ViewObject.DisplayMode == "HiRes": # check if high-resolution object is available - if hasattr(obj,"HiRes") and obj.HiRes: + if hasattr(obj, "HiRes") and obj.HiRes: if obj.HiRes.isDerivedFrom("Mesh::Feature"): hires = obj.HiRes.Mesh.copy() else: hires = obj.HiRes.Shape.copy(False, True) hires.Placement = obj_place.multiply(hires.Placement) - if not hires \ - and hasattr(obj,"CloneOf") \ - and obj.CloneOf \ - and hasattr(obj.CloneOf,"HiRes") \ - and obj.CloneOf.HiRes: + if ( + not hires + and hasattr(obj, "CloneOf") + and obj.CloneOf + and hasattr(obj.CloneOf, "HiRes") + and obj.CloneOf.HiRes + ): if obj.CloneOf.HiRes.isDerivedFrom("Mesh::Feature"): hires = obj.CloneOf.HiRes.Mesh.copy() else: hires = obj.CloneOf.HiRes.Shape.copy(False, True) - hires.Placement = obj_place.multiply(obj.CloneOf.Placement).multiply(hires.Placement) + hires.Placement = obj_place.multiply(obj.CloneOf.Placement).multiply( + hires.Placement + ) if hires: - vlist,vnlist,elist,flist = getIndices(obj,hires,offsetv,offsetvn) - elif hasattr(obj,"Shape") and obj.Shape: + vlist, vnlist, elist, flist = getIndices(obj, hires, offsetv, offsetvn) + elif hasattr(obj, "Shape") and obj.Shape: shape = obj.Shape.copy(False, True) shape.Placement = obj_place - vlist,vnlist,elist,flist = getIndices(obj,shape,offsetv,offsetvn) - elif hasattr(obj,"Mesh") and obj.Mesh: + vlist, vnlist, elist, flist = getIndices(obj, shape, offsetv, offsetvn) + elif hasattr(obj, "Mesh") and obj.Mesh: mesh = obj.Mesh.copy() mesh.Placement = obj_place - vlist,vnlist, elist,flist = getIndices(obj,mesh,offsetv,offsetvn) + vlist, vnlist, elist, flist = getIndices(obj, mesh, offsetv, offsetvn) if vlist is None: - FreeCAD.Console.PrintError("Unable to export object "+obj.Label+". Skipping.\n") + FreeCAD.Console.PrintError("Unable to export object " + obj.Label + ". Skipping.\n") else: offsetv += len(vlist) offsetvn += len(vnlist) @@ -225,9 +260,9 @@ def export(exportList,filename,colors=None): # write material m = False - if hasattr(obj,"Material"): + if hasattr(obj, "Material"): if obj.Material: - if hasattr(obj.Material,"Material"): + if hasattr(obj.Material, "Material"): outfile.write("usemtl " + obj.Material.Name + "\n") materials.append(obj.Material) m = True @@ -236,18 +271,22 @@ def export(exportList,filename,colors=None): if obj.Name in colors: color = colors[obj.Name] if color: - if isinstance(color[0],tuple): + if isinstance(color[0], tuple): # this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors color = color[0] - #print("found color for obj",obj.Name,":",color) - mn = Draft.getrgb(color,testbw=False)[1:] + # print("found color for obj",obj.Name,":",color) + mn = Draft.getrgb(color, testbw=False)[1:] outfile.write("usemtl color_" + mn + "\n") - materials.append(("color_" + mn,color,0)) + materials.append(("color_" + mn, color, 0)) elif FreeCAD.GuiUp: - if hasattr(obj.ViewObject,"ShapeAppearance") and hasattr(obj.ViewObject,"Transparency"): - mn = Draft.getrgb(obj.ViewObject.ShapeColor,testbw=False)[1:] + if hasattr(obj.ViewObject, "ShapeAppearance") and hasattr( + obj.ViewObject, "Transparency" + ): + mn = Draft.getrgb(obj.ViewObject.ShapeColor, testbw=False)[1:] outfile.write("usemtl color_" + mn + "\n") - materials.append(("color_" + mn,obj.ViewObject.ShapeColor,obj.ViewObject.Transparency)) + materials.append( + ("color_" + mn, obj.ViewObject.ShapeColor, obj.ViewObject.Transparency) + ) # write geometry for v in vlist: @@ -260,48 +299,65 @@ def export(exportList,filename,colors=None): outfile.write("f" + f + "\n") outfile.close() - FreeCAD.Console.PrintMessage(translate("Arch","Successfully written") + " " + filename + "\n") + FreeCAD.Console.PrintMessage(translate("Arch", "Successfully written") + " " + filename + "\n") if materials: - outfile = pyopen(filenamemtl,"w") + outfile = pyopen(filenamemtl, "w") outfile.write("# FreeCAD v" + ver[0] + "." + ver[1] + " build" + ver[2] + " Arch module\n") outfile.write("# https://www.freecad.org\n") - kinds = {"AmbientColor":"Ka ","DiffuseColor":"Kd ","SpecularColor":"Ks ","EmissiveColor":"Ke ","Transparency":"Tr ","Dissolve":"d "} - done = [] # store names to avoid duplicates + kinds = { + "AmbientColor": "Ka ", + "DiffuseColor": "Kd ", + "SpecularColor": "Ks ", + "EmissiveColor": "Ke ", + "Transparency": "Tr ", + "Dissolve": "d ", + } + done = [] # store names to avoid duplicates for mat in materials: - if isinstance(mat,tuple): + if isinstance(mat, tuple): if not mat[0] in done: outfile.write("newmtl " + mat[0] + "\n") - outfile.write("Kd " + str(mat[1][0]) + " " + str(mat[1][1]) + " " + str(mat[1][2]) + "\n") - outfile.write("Tr " + str(mat[2]/100) + "\n") - outfile.write("d " + str(1-mat[2]/100) + "\n") + outfile.write( + "Kd " + str(mat[1][0]) + " " + str(mat[1][1]) + " " + str(mat[1][2]) + "\n" + ) + outfile.write("Tr " + str(mat[2] / 100) + "\n") + outfile.write("d " + str(1 - mat[2] / 100) + "\n") done.append(mat[0]) else: if not mat.Name in done: outfile.write("newmtl " + mat.Name + "\n") for prop in kinds: if prop in mat.Material: - outfile.write(kinds[prop] + mat.Material[prop].strip("()").replace(',',' ') + "\n") + outfile.write( + kinds[prop] + + mat.Material[prop].strip("()").replace(",", " ") + + "\n" + ) done.append(mat.Name) outfile.write("# Material Count: " + str(len(materials))) outfile.close() - FreeCAD.Console.PrintMessage(translate("Arch","Successfully written") + ' ' + filenamemtl + "\n") + FreeCAD.Console.PrintMessage( + translate("Arch", "Successfully written") + " " + filenamemtl + "\n" + ) # return entry after given index from an array or None on array end def peek(index, array): if index < len(array) - 1: - return array[index+1].strip() + return array[index + 1].strip() else: return None + def open(filename): "called when freecad wants to open a file" docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) doc.Label = docname - return insert(filename,doc.Name) + return insert(filename, doc.Name) -def insert(filename,docname): + +def insert(filename, docname): meshName = ntpath.basename(filename) for i in meshName.split(): @@ -317,7 +373,7 @@ def insert(filename,docname): doc = FreeCAD.newDocument(docname) FreeCAD.ActiveDocument = doc - with pyopen(filename,"r",encoding="utf8") as infile: + with pyopen(filename, "r", encoding="utf8") as infile: verts = [] medges = [] edges = [] @@ -328,9 +384,9 @@ def insert(filename,docname): content_array = [] for line in infile: line = line.strip() - while line.endswith('\\'): + while line.endswith("\\"): next_line = next(infile).strip() - line = line.rstrip()[:-1] + ' ' + next_line + line = line.rstrip()[:-1] + " " + next_line content_array.append(line) activeobjectExists = False for line in content_array: @@ -340,9 +396,9 @@ def insert(filename,docname): activeobject = meshName for index, line in enumerate(content_array): if line[:7] == "mtllib ": - matlib = os.path.join(os.path.dirname(filename),line[7:]) + matlib = os.path.join(os.path.dirname(filename), line[7:]) if os.path.exists(matlib): - with pyopen(matlib,"r") as matfile: + with pyopen(matlib, "r") as matfile: mname = None color = None trans = None @@ -350,19 +406,19 @@ def insert(filename,docname): mline = mline.strip() if mline[:7] == "newmtl ": if mname and color: - colortable[mname] = [color,trans] + colortable[mname] = [color, trans] color = None trans = None mname = mline[7:] elif mline[:3] == "Kd ": color = tuple([float(i) for i in mline[3:].split()]) elif mline[:2] == "d ": - trans = int((1-float(mline[2:]))*100) + trans = int((1 - float(mline[2:])) * 100) if mname and color: - colortable[mname] = [color,trans] + colortable[mname] = [color, trans] elif line[:2] == "o ": if activeobject: - makeMesh(doc,group,activeobject,verts,medges,facets,material,colortable) + makeMesh(doc, group, activeobject, verts, medges, facets, material, colortable) material = None medges = [] facets = [] @@ -391,30 +447,31 @@ def insert(filename,docname): elif line[:7] == "usemtl ": material = line[7:] if activeobject: - makeMesh(doc,group,activeobject,verts,medges,facets,material,colortable) - FreeCAD.Console.PrintMessage(translate("Arch","Successfully imported") + ' ' + filename + "\n") + makeMesh(doc, group, activeobject, verts, medges, facets, material, colortable) + FreeCAD.Console.PrintMessage(translate("Arch", "Successfully imported") + " " + filename + "\n") return doc -def makeMesh(doc,group,activeobject,verts,edges,facets,material,colortable): + +def makeMesh(doc, group, activeobject, verts, edges, facets, material, colortable): mfacets = [] if facets: for facet in facets: if len(facet) > 3: - vecs = [FreeCAD.Vector(*verts[i-1]) for i in facet] + vecs = [FreeCAD.Vector(*verts[i - 1]) for i in facet] vecs.append(vecs[0]) pol = Part.makePolygon(vecs) try: face = Part.Face(pol) except Part.OCCError: - print("Skipping non-planar polygon:",vecs) + print("Skipping non-planar polygon:", vecs) else: tris = face.tessellate(1) for tri in tris[1]: mfacets.append([tris[0][i] for i in tri]) else: - mfacets.append([verts[i-1] for i in facet]) + mfacets.append([verts[i - 1] for i in facet]) if mfacets: - mobj = doc.addObject("Mesh::Feature",activeobject) + mobj = doc.addObject("Mesh::Feature", activeobject) mobj.Label = activeobject mobj.Mesh = Mesh.Mesh(mfacets) if FreeCAD.GuiUp and material and material in colortable: @@ -455,7 +512,7 @@ def makeMesh(doc,group,activeobject,verts,edges,facets,material,colortable): part.Label = activeobject features = [] for strip in medges: - points = [FreeCAD.Vector(*verts[i-1]) for i in strip] + points = [FreeCAD.Vector(*verts[i - 1]) for i in strip] wire = Draft.make_wire(points) if FreeCAD.GuiUp and material and material in colortable: wire.ViewObject.ShapeColor = colortable[material][0] diff --git a/src/Mod/BIM/importers/importSH3D.py b/src/Mod/BIM/importers/importSH3D.py index 9e67cbf94d..226b6f7b1e 100644 --- a/src/Mod/BIM/importers/importSH3D.py +++ b/src/Mod/BIM/importers/importSH3D.py @@ -22,9 +22,9 @@ # * * # *************************************************************************** -__title__ = "FreeCAD SweetHome3D Importer" +__title__ = "FreeCAD SweetHome3D Importer" __author__ = "Yorik van Havre" -__url__ = "https://www.freecad.org" +__url__ = "https://www.freecad.org" ## @package importSH3D # \ingroup ARCH @@ -52,7 +52,7 @@ def open(filename): return doc -def insert(filename,docname): +def insert(filename, docname): "called when freecad wants to import a file" try: doc = FreeCAD.getDocument(docname) @@ -67,8 +67,10 @@ def read(filename): "reads the file and creates objects in the active document" import BIM.importers.importSH3DHelper + if DEBUG: from importlib import reload + reload(BIM.importers.importSH3DHelper) pi = Base.ProgressIndicator() diff --git a/src/Mod/BIM/importers/importSH3DHelper.py b/src/Mod/BIM/importers/importSH3DHelper.py index 757a2ebbe7..8f5bd2cf49 100644 --- a/src/Mod/BIM/importers/importSH3DHelper.py +++ b/src/Mod/BIM/importers/importSH3DHelper.py @@ -57,38 +57,40 @@ else: # \cond def translate(_, text): return text + # \endcond # Used to make section edges more visible (https://coolors.co/5bc0eb-fde74c-9bc53d-e55934-fa7921) DEBUG_EDGES_COLORS = ["5bc0eb", "fde74c", "9bc53d", "e55934", "fa7921"] DEBUG_POINT_COLORS = ["011627", "ff0022", "41ead4", "fdfffc", "b91372"] -RED = (255,0,0,1) -GREEN = (0,255,0,1) -BLUE = (0,0,255,1) -MAGENTA = (255,85,255,1) -MAGENTA_LIGHT = (255,85,127,1) -ORANGE = (255,85,0,1) +RED = (255, 0, 0, 1) +GREEN = (0, 255, 0, 1) +BLUE = (0, 0, 255, 1) +MAGENTA = (255, 85, 255, 1) +MAGENTA_LIGHT = (255, 85, 127, 1) +ORANGE = (255, 85, 0, 1) try: from Render import Camera, PointLight from Render.project import Project + RENDER_IS_AVAILABLE = True -except : +except: RENDER_IS_AVAILABLE = False # SweetHome3D is in cm while FreeCAD is in mm FACTOR = 10 TOLERANCE = 1 DEFAULT_WALL_WIDTH = 100 -TWO_PI = 2* math.pi +TWO_PI = 2 * math.pi DEFAULT_MATERIAL = App.Material( - DiffuseColor=(1.00,0.00,0.00), - AmbientColor=(0.33,0.33,0.33), - SpecularColor=(0.53,0.53,0.53), - EmissiveColor=(0.00,0.00,0.00), + DiffuseColor=(1.00, 0.00, 0.00), + AmbientColor=(0.33, 0.33, 0.33), + SpecularColor=(0.53, 0.53, 0.53), + EmissiveColor=(0.00, 0.00, 0.00), Shininess=(0.90), - Transparency=(0.00) - ) + Transparency=(0.00), +) ORIGIN = App.Vector(0, 0, 0) X_NORM = App.Vector(1, 0, 0) @@ -107,89 +109,89 @@ NO_ROT = App.Rotation() # grep 'catalogId=' | \ # sed -e 's/.*catalogId=//;s/ name=.*/: ("Simple door","Door")/' | sort -u DOOR_MODELS = { - 'eTeks#doorFrame': ("Opening only", "Opening Element"), - 'eTeks#door': ("Simple door","Door"), - 'eTeks#frontDoor': ("Simple door","Door"), - 'eTeks#garageDoor': ("Simple door","Door"), - 'eTeks#openDoor': ("Simple door","Door"), - 'eTeks#roundDoorFrame': ("Opening only", "Opening Element"), - 'eTeks#roundedDoor': ("Simple door","Door"), - 'Kator Legaz#exterior-door-01': ("Simple door","Door"), - 'Kator Legaz#exterior-door-02': ("Simple door","Door"), - 'Kator Legaz#exterior-door-03': ("Glass door","Door"), - 'Kator Legaz#exterior-door-05': ("Simple door","Door"), - 'Kator Legaz#exterior-door-07': ("Glass door","Door"), - 'Kator Legaz#screen-door': ("Simple door","Door"), - 'Scopia#door': ("Simple door","Door"), - 'Scopia#double_door_2': ("Simple door","Door"), - 'Scopia#double_door': ("Glass door","Door"), - 'Scopia#double_door_with_little_part': ("Glass door","Door"), - 'Scopia#elevator-door': ("Simple door","Door"), - 'Scopia#garage-door2': ("Simple door","Door"), - 'Scopia#garage-door': ("Simple door","Door"), - 'Scopia#glassDoor2': ("Glass door","Door"), - 'Scopia#glass_door': ("Glass door","Door"), - 'Scopia#puerta': ("Simple door","Door"), - "PeterSmolik#door1": ("Simple door","Door"), - "PeterSmolik#doorGlassPanels": ("Simple door","Door"), - "PeterSmolik#door1": ("Simple door","Door"), - 'Siath#emergencyGlassDoubleDoor': ("Simple door","Door"), - 'OlaKristianHoff#door_window_thick_double_2x4': ("Simple door","Door"), - 'Mchnz#craftsmanDoorClosed': ("Simple door","Door"), - - 'eTeks#doubleFrenchWindow126x200': ("Open 1-pane","Window"), - 'eTeks#doubleHungWindow80x122': ("Open 1-pane","Window"), - 'eTeks#doubleOutwardOpeningWindow': ("Open 1-pane","Window"), - 'eTeks#doubleWindow126x123': ("Open 1-pane","Window"), - 'eTeks#doubleWindow126x163': ("Open 1-pane","Window"), - 'eTeks#fixedTriangleWindow85x85': ("Open 1-pane","Window"), - 'eTeks#fixedWindow85x123': ("Fixed","Window"), - 'eTeks#frenchWindow85x200': ("Open 1-pane","Window"), - 'eTeks#halfRoundWindow': ("Open 1-pane","Window"), - 'eTeks#roundWindow': ("Open 1-pane","Window"), - 'eTeks#sliderWindow126x200': ("Open 1-pane","Window"), - 'eTeks#window85x123': ("Open 1-pane","Window"), - 'eTeks#window85x163': ("Open 1-pane","Window"), - 'eTeks#serviceHatch': ("Fixed","Window"), - 'Kator Legaz#window-01': ("Open 1-pane","Window"), - 'Kator Legaz#window-08-02': ("Open 1-pane","Window"), - 'Kator Legaz#window-08': ("Open 1-pane","Window"), - 'Scopia#turn-window': ("Open 1-pane","Window"), - 'Scopia#window_2x1_medium_with_large_pane': ("Open 1-pane","Window"), - 'Scopia#window_2x1_with_sliders': ("Open 1-pane","Window"), - 'Scopia#window_2x3_arched': ("Open 1-pane","Window"), - 'Scopia#window_2x3': ("Open 1-pane","Window"), - 'Scopia#window_2x3_regular': ("Open 1-pane","Window"), - 'Scopia#window_2x4_arched': ("Open 1-pane","Window"), - 'Scopia#window_2x4': ("Open 1-pane","Window"), - 'Scopia#window_2x6': ("Open 1-pane","Window"), - 'Scopia#window_3x1': ("Open 1-pane","Window"), - 'Scopia#window_4x1': ("Open 1-pane","Window"), - 'Scopia#window_4x3_arched': ("Open 1-pane","Window"), - 'Scopia#window_4x3': ("Open 1-pane","Window"), - 'Scopia#window_4x5': ("Open 1-pane","Window"), - 'Artist373#rectangularFivePanesWindow': ("Open 1-pane","Window"), - 'OlaKristianHoff#window_shop': ("Open 1-pane","Window"), - 'OlaKristianHoff#window_double_2x3_frame_sill': ("Open 1-pane","Window"), - 'OlaKristianHoff#window_deep': ("Open 1-pane","Window"), - 'OlaKristianHoff#fixed_window_2x2': ("Fixed","Window"), - 'OlaKristianHoff#window_double_3x3': ("Open 1-pane","Window"), + "eTeks#doorFrame": ("Opening only", "Opening Element"), + "eTeks#door": ("Simple door", "Door"), + "eTeks#frontDoor": ("Simple door", "Door"), + "eTeks#garageDoor": ("Simple door", "Door"), + "eTeks#openDoor": ("Simple door", "Door"), + "eTeks#roundDoorFrame": ("Opening only", "Opening Element"), + "eTeks#roundedDoor": ("Simple door", "Door"), + "Kator Legaz#exterior-door-01": ("Simple door", "Door"), + "Kator Legaz#exterior-door-02": ("Simple door", "Door"), + "Kator Legaz#exterior-door-03": ("Glass door", "Door"), + "Kator Legaz#exterior-door-05": ("Simple door", "Door"), + "Kator Legaz#exterior-door-07": ("Glass door", "Door"), + "Kator Legaz#screen-door": ("Simple door", "Door"), + "Scopia#door": ("Simple door", "Door"), + "Scopia#double_door_2": ("Simple door", "Door"), + "Scopia#double_door": ("Glass door", "Door"), + "Scopia#double_door_with_little_part": ("Glass door", "Door"), + "Scopia#elevator-door": ("Simple door", "Door"), + "Scopia#garage-door2": ("Simple door", "Door"), + "Scopia#garage-door": ("Simple door", "Door"), + "Scopia#glassDoor2": ("Glass door", "Door"), + "Scopia#glass_door": ("Glass door", "Door"), + "Scopia#puerta": ("Simple door", "Door"), + "PeterSmolik#door1": ("Simple door", "Door"), + "PeterSmolik#doorGlassPanels": ("Simple door", "Door"), + "PeterSmolik#door1": ("Simple door", "Door"), + "Siath#emergencyGlassDoubleDoor": ("Simple door", "Door"), + "OlaKristianHoff#door_window_thick_double_2x4": ("Simple door", "Door"), + "Mchnz#craftsmanDoorClosed": ("Simple door", "Door"), + "eTeks#doubleFrenchWindow126x200": ("Open 1-pane", "Window"), + "eTeks#doubleHungWindow80x122": ("Open 1-pane", "Window"), + "eTeks#doubleOutwardOpeningWindow": ("Open 1-pane", "Window"), + "eTeks#doubleWindow126x123": ("Open 1-pane", "Window"), + "eTeks#doubleWindow126x163": ("Open 1-pane", "Window"), + "eTeks#fixedTriangleWindow85x85": ("Open 1-pane", "Window"), + "eTeks#fixedWindow85x123": ("Fixed", "Window"), + "eTeks#frenchWindow85x200": ("Open 1-pane", "Window"), + "eTeks#halfRoundWindow": ("Open 1-pane", "Window"), + "eTeks#roundWindow": ("Open 1-pane", "Window"), + "eTeks#sliderWindow126x200": ("Open 1-pane", "Window"), + "eTeks#window85x123": ("Open 1-pane", "Window"), + "eTeks#window85x163": ("Open 1-pane", "Window"), + "eTeks#serviceHatch": ("Fixed", "Window"), + "Kator Legaz#window-01": ("Open 1-pane", "Window"), + "Kator Legaz#window-08-02": ("Open 1-pane", "Window"), + "Kator Legaz#window-08": ("Open 1-pane", "Window"), + "Scopia#turn-window": ("Open 1-pane", "Window"), + "Scopia#window_2x1_medium_with_large_pane": ("Open 1-pane", "Window"), + "Scopia#window_2x1_with_sliders": ("Open 1-pane", "Window"), + "Scopia#window_2x3_arched": ("Open 1-pane", "Window"), + "Scopia#window_2x3": ("Open 1-pane", "Window"), + "Scopia#window_2x3_regular": ("Open 1-pane", "Window"), + "Scopia#window_2x4_arched": ("Open 1-pane", "Window"), + "Scopia#window_2x4": ("Open 1-pane", "Window"), + "Scopia#window_2x6": ("Open 1-pane", "Window"), + "Scopia#window_3x1": ("Open 1-pane", "Window"), + "Scopia#window_4x1": ("Open 1-pane", "Window"), + "Scopia#window_4x3_arched": ("Open 1-pane", "Window"), + "Scopia#window_4x3": ("Open 1-pane", "Window"), + "Scopia#window_4x5": ("Open 1-pane", "Window"), + "Artist373#rectangularFivePanesWindow": ("Open 1-pane", "Window"), + "OlaKristianHoff#window_shop": ("Open 1-pane", "Window"), + "OlaKristianHoff#window_double_2x3_frame_sill": ("Open 1-pane", "Window"), + "OlaKristianHoff#window_deep": ("Open 1-pane", "Window"), + "OlaKristianHoff#fixed_window_2x2": ("Fixed", "Window"), + "OlaKristianHoff#window_double_3x3": ("Open 1-pane", "Window"), } -ET_XPATH_LEVEL = 'level' -ET_XPATH_ROOM = 'room' -ET_XPATH_WALL = 'wall' -ET_XPATH_DOOR_OR_WINDOWS = './/doorOrWindow' -ET_XPATH_PIECE_OF_FURNITURE = './/pieceOfFurniture' -ET_XPATH_LIGHT = 'light' -ET_XPATH_OBSERVER_CAMERA = 'observerCamera' -ET_XPATH_CAMERA = 'camera' -ET_XPATH_DUMMY_SLAB = 'DummySlab' -ET_XPATH_DUMMY_DECORATE = 'DummyDecorate' +ET_XPATH_LEVEL = "level" +ET_XPATH_ROOM = "room" +ET_XPATH_WALL = "wall" +ET_XPATH_DOOR_OR_WINDOWS = ".//doorOrWindow" +ET_XPATH_PIECE_OF_FURNITURE = ".//pieceOfFurniture" +ET_XPATH_LIGHT = "light" +ET_XPATH_OBSERVER_CAMERA = "observerCamera" +ET_XPATH_CAMERA = "camera" +ET_XPATH_DUMMY_SLAB = "DummySlab" +ET_XPATH_DUMMY_DECORATE = "DummyDecorate" + class Transaction(object): - def __init__(self, title, doc= None): + def __init__(self, title, doc=None): if doc is None: doc = App.ActiveDocument self.title = title @@ -242,7 +244,7 @@ class SH3DImporter: self.walls = {} self.spaces = {} - def import_sh3d_from_string(self, home:str): + def import_sh3d_from_string(self, home: str): """Import the SH3D Home from a String. Args: @@ -259,7 +261,7 @@ class SH3DImporter: self.progress_bar.start(f"Importing SweetHome 3D Home. Standby…", -1) self._import_home(ET.fromstring(home)) - def import_sh3d_from_filename(self, filename:str): + def import_sh3d_from_filename(self, filename: str): """Import the SH3D file. Args: @@ -277,7 +279,7 @@ class SH3DImporter: if self.progress_bar: self.progress_bar.start(f"Importing SweetHome 3D file '{self.filename}'. Standby…", -1) - with zipfile.ZipFile(self.filename, 'r') as zip: + with zipfile.ZipFile(self.filename, "r") as zip: self.zip = zip entries = zip.namelist() if "Home.xml" not in entries: @@ -294,7 +296,12 @@ class SH3DImporter: # Get all the FreeCAD object in the active doc, in order to allow # for merge of existing object if self.preferences["MERGE"]: - list(map(lambda o2: self.add_fc_objects(o2), list(filter(lambda o1: hasattr(o1, 'id'), doc.Objects)))) + list( + map( + lambda o2: self.add_fc_objects(o2), + list(filter(lambda o1: hasattr(o1, "id"), doc.Objects)), + ) + ) # Let's create the project and site for this import self._setup_project(home) @@ -306,8 +313,13 @@ class SH3DImporter: else: # Has the default floor already been created from a # previous import? - if self.preferences["DEBUG_GEOMETRY"]: _log("No level defined. Using default level…") - self.default_floor = self.fc_objects.get('Level') if 'Level' in self.fc_objects else self._create_default_floor() + if self.preferences["DEBUG_GEOMETRY"]: + _log("No level defined. Using default level…") + self.default_floor = ( + self.fc_objects.get("Level") + if "Level" in self.fc_objects + else self._create_default_floor() + ) # Importing elements ... self._import_elements(home, ET_XPATH_ROOM) @@ -357,14 +369,13 @@ class SH3DImporter: Project.create(doc, renderer="Povray", template="povray_standard.pov") Gui.Selection.clearSelection() Gui.Selection.addSelection(self.site) - Gui.runCommand('Render_View', 0) + Gui.runCommand("Render_View", 0) self._refresh() _msg(f"Successfully imported home '{home.get('name')}' …") def _get_object_count(self, home): - """Get an approximate count of object to be imported - """ + """Get an approximate count of object to be imported""" count = 0 for tag in self.handlers.keys(): count = count + len(list(home.findall(tag))) @@ -373,24 +384,25 @@ class SH3DImporter: def _get_preferences(self): """Retrieve the SH3D preferences available in Mod/Arch.""" self.preferences = { - 'IMPORT_DOORS_AND_WINDOWS': get_param_arch("sh3dImportDoorsAndWindows"), - 'IMPORT_FURNITURES': get_param_arch("sh3dImportFurnitures"), - 'IMPORT_LIGHTS': get_param_arch("sh3dImportLights") and RENDER_IS_AVAILABLE, - 'IMPORT_CAMERAS': get_param_arch("sh3dImportCameras") and RENDER_IS_AVAILABLE, - 'MERGE': get_param_arch("sh3dMerge"), - 'CREATE_ARCH_EQUIPMENT': get_param_arch("sh3dCreateArchEquipment"), - 'JOIN_ARCH_WALL': get_param_arch("sh3dJoinArchWall"), - 'CREATE_RENDER_PROJECT': get_param_arch("sh3dCreateRenderProject") and RENDER_IS_AVAILABLE, - 'FIT_VIEW': get_param_arch("sh3dFitView"), - 'CREATE_IFC_PROJECT': get_param_arch("sh3dCreateIFCProject"), - 'DEFAULT_FLOOR_COLOR': color_fc2sh(get_param_arch("sh3dDefaultFloorColor")), - 'DEFAULT_CEILING_COLOR': color_fc2sh(get_param_arch("sh3dDefaultCeilingColor")), - 'CREATE_GROUND_MESH': get_param_arch("sh3dCreateGroundMesh"), - 'DEFAULT_GROUND_COLOR': color_fc2sh(get_param_arch("sh3dDefaultGroundColor")), - 'DEFAULT_SKY_COLOR': color_fc2sh(get_param_arch("sh3dDefaultSkyColor")), - 'DECORATE_SURFACES': get_param_arch("sh3dDecorateSurfaces"), - 'DEFAULT_FURNITURE_COLOR': color_fc2sh(get_param_arch("sh3dDefaultFurnitureColor")), - 'DEBUG_GEOMETRY': get_param_arch("sh3dDebugGeometry"), + "IMPORT_DOORS_AND_WINDOWS": get_param_arch("sh3dImportDoorsAndWindows"), + "IMPORT_FURNITURES": get_param_arch("sh3dImportFurnitures"), + "IMPORT_LIGHTS": get_param_arch("sh3dImportLights") and RENDER_IS_AVAILABLE, + "IMPORT_CAMERAS": get_param_arch("sh3dImportCameras") and RENDER_IS_AVAILABLE, + "MERGE": get_param_arch("sh3dMerge"), + "CREATE_ARCH_EQUIPMENT": get_param_arch("sh3dCreateArchEquipment"), + "JOIN_ARCH_WALL": get_param_arch("sh3dJoinArchWall"), + "CREATE_RENDER_PROJECT": get_param_arch("sh3dCreateRenderProject") + and RENDER_IS_AVAILABLE, + "FIT_VIEW": get_param_arch("sh3dFitView"), + "CREATE_IFC_PROJECT": get_param_arch("sh3dCreateIFCProject"), + "DEFAULT_FLOOR_COLOR": color_fc2sh(get_param_arch("sh3dDefaultFloorColor")), + "DEFAULT_CEILING_COLOR": color_fc2sh(get_param_arch("sh3dDefaultCeilingColor")), + "CREATE_GROUND_MESH": get_param_arch("sh3dCreateGroundMesh"), + "DEFAULT_GROUND_COLOR": color_fc2sh(get_param_arch("sh3dDefaultGroundColor")), + "DEFAULT_SKY_COLOR": color_fc2sh(get_param_arch("sh3dDefaultSkyColor")), + "DECORATE_SURFACES": get_param_arch("sh3dDecorateSurfaces"), + "DEFAULT_FURNITURE_COLOR": color_fc2sh(get_param_arch("sh3dDefaultFurnitureColor")), + "DEBUG_GEOMETRY": get_param_arch("sh3dDebugGeometry"), } def _setup_handlers(self): @@ -398,13 +410,13 @@ class SH3DImporter: ET_XPATH_LEVEL: LevelHandler(self), ET_XPATH_ROOM: RoomHandler(self), ET_XPATH_WALL: WallHandler(self), - ET_XPATH_DUMMY_SLAB : None, + ET_XPATH_DUMMY_SLAB: None, } if self.preferences["IMPORT_DOORS_AND_WINDOWS"]: self.handlers[ET_XPATH_DOOR_OR_WINDOWS] = DoorOrWindowHandler(self) if self.preferences["DECORATE_SURFACES"]: - self.handlers[ET_XPATH_DUMMY_DECORATE] = None, + self.handlers[ET_XPATH_DUMMY_DECORATE] = (None,) if self.preferences["IMPORT_FURNITURES"]: self.handlers[ET_XPATH_PIECE_OF_FURNITURE] = FurnitureHandler(self) @@ -422,7 +434,9 @@ class SH3DImporter: if App.GuiUp: Gui.updateGui() - def set_property(self, obj, type_, name, description, value, valid_values=None, group="SweetHome3D"): + def set_property( + self, obj, type_, name, description, value, valid_values=None, group="SweetHome3D" + ): """Set the attribute of the given object as an FC property Note that the method has a default behavior when the value is not specified. @@ -440,7 +454,8 @@ class SH3DImporter: if valid_values: setattr(obj, name, valid_values) if value is None: - if self.preferences["DEBUG_GEOMETRY"]:_log(f"Setting obj.{name}=None") + if self.preferences["DEBUG_GEOMETRY"]: + _log(f"Setting obj.{name}=None") return if type(value) is ET.Element or type(value) is type(dict()): if type_ == "App::PropertyString": @@ -497,9 +512,16 @@ class SH3DImporter: if self.preferences["MERGE"] and id in self.fc_objects: fc_object = self.fc_objects[id] if sh_type: - assert fc_object.shType == sh_type, f"Invalid shType: expected {sh_type}, got {fc_object.shType}" + assert ( + fc_object.shType == sh_type + ), f"Invalid shType: expected {sh_type}, got {fc_object.shType}" if self.preferences["DEBUG_GEOMETRY"]: - _log(translate("BIM", f"Merging imported element '{id}' with existing element of type '{type(fc_object)}'")) + _log( + translate( + "BIM", + f"Merging imported element '{id}' with existing element of type '{type(fc_object)}'", + ) + ) return fc_object if self.preferences["DEBUG_GEOMETRY"]: _log(translate("BIM", f"No element found with id '{id}' and type '{sh_type}'")) @@ -580,19 +602,18 @@ class SH3DImporter: return list(itertools.chain(*self.walls.values())) def get_walls(self, floor): - """Returns the wall belonging to the specified level + """Returns the wall belonging to the specified level - Args: - floor (Arch.Level): the level for which to return the list of wall + Args: + floor (Arch.Level): the level for which to return the list of wall - Returns: - list: the list of Arch.Wall - """ - return self.walls.get(floor.id, []) + Returns: + list: the list of Arch.Wall + """ + return self.walls.get(floor.id, []) def _create_groups(self): - """Create FreeCAD Group for the different imported elements - """ + """Create FreeCAD Group for the different imported elements""" doc = App.ActiveDocument if self.preferences["IMPORT_CAMERAS"] and not doc.getObject("Cameras"): _log(f"Creating Cameras group…") @@ -608,36 +629,34 @@ class SH3DImporter: elm (str): the element """ - if 'Site' in self.fc_objects: - self.site = self.fc_objects.get('Site') + if "Site" in self.fc_objects: + self.site = self.fc_objects.get("Site") else: self.site = self._create_site() self._set_site_properties(elm) - if elm.get('name') in self.fc_objects: - self.building = self.fc_objects.get(elm.get('name')) + if elm.get("name") in self.fc_objects: + self.building = self.fc_objects.get(elm.get("name")) else: self.building = self._create_building(elm) self.site.addObject(self.building) - if 'Project' in self.fc_objects: - self.project = self.fc_objects.get('Project') + if "Project" in self.fc_objects: + self.project = self.fc_objects.get("Project") elif self.preferences["CREATE_IFC_PROJECT"]: self.project = self._create_project() if self.project: self.project.addObject(self.site) def _create_project(self): - """Create a default Arch::Project object - """ + """Create a default Arch::Project object""" project = Arch.makeProject([]) self.set_property(project, "App::PropertyString", "id", "The element's id", "Project") return project def _create_site(self): - """Create a default Arch::Site object - """ + """Create a default Arch::Site object""" site = Arch.makeSite([]) self.set_property(site, "App::PropertyString", "id", "The element's id", "Site") return site @@ -652,46 +671,48 @@ class SH3DImporter: the Arch::Building """ building = Arch.makeBuilding([]) - self.set_property(building, "App::PropertyString", "shType", "The element type", 'building') - self.set_property(building, "App::PropertyString", "id", "The element's id", elm.get('name')) - for property in elm.findall('property'): - name = re.sub('[^A-Za-z0-9]+', '', property.get('name')) - value = property.get('value') + self.set_property(building, "App::PropertyString", "shType", "The element type", "building") + self.set_property( + building, "App::PropertyString", "id", "The element's id", elm.get("name") + ) + for property in elm.findall("property"): + name = re.sub("[^A-Za-z0-9]+", "", property.get("name")) + value = property.get("value") self.set_property(building, "App::PropertyString", name, "", value) return building def _create_default_floor(self): - """Create a default Arch::Floor object - """ - return self.handlers['level'].create_default_floor() + """Create a default Arch::Floor object""" + return self.handlers["level"].create_default_floor() def _create_ground_mesh(self, elm): self.building.recompute(True) ground = None if self.preferences["MERGE"]: - ground = self.get_fc_object('ground', 'ground') + ground = self.get_fc_object("ground", "ground") if not ground: bb = self.building.Shape.BoundBox - dx = bb.XLength/2 - dy = bb.YLength/2 - SO = App.Vector(bb.XMin-dx, bb.YMin-dy, 0) - NO = App.Vector(bb.XMin-dx, bb.YMax+dy, 0) - NE = App.Vector(bb.XMax+dx, bb.YMax+dy, 0) - SE = App.Vector(bb.XMax+dx, bb.YMin-dy, 0) + dx = bb.XLength / 2 + dy = bb.YLength / 2 + SO = App.Vector(bb.XMin - dx, bb.YMin - dy, 0) + NO = App.Vector(bb.XMin - dx, bb.YMax + dy, 0) + NE = App.Vector(bb.XMax + dx, bb.YMax + dy, 0) + SE = App.Vector(bb.XMax + dx, bb.YMin - dy, 0) edge0 = Part.makeLine(SO, NO) edge1 = Part.makeLine(NO, NE) edge2 = Part.makeLine(NE, SE) edge3 = Part.makeLine(SE, SO) - ground_face = Part.makeFace([ Part.Wire([edge0, edge1, edge2, edge3]) ]) + ground_face = Part.makeFace([Part.Wire([edge0, edge1, edge2, edge3])]) - ground = App.ActiveDocument.addObject("Mesh::Feature", "Ground") - ground.Mesh = MeshPart.meshFromShape(Shape=ground_face, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False) + ground = App.ActiveDocument.addObject("Mesh::Feature", "Ground") + ground.Mesh = MeshPart.meshFromShape( + Shape=ground_face, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False + ) ground.Label = "Ground" - self.set_property(ground, "App::PropertyString", "shType", "The element type", 'ground') - self.set_property(ground, "App::PropertyString", "id", "The ground's id", 'ground') - + self.set_property(ground, "App::PropertyString", "shType", "The element type", "ground") + self.set_property(ground, "App::PropertyString", "id", "The ground's id", "ground") set_color_and_transparency(ground, self.site.groundColor) ground.ViewObject.Transparency = 50 @@ -717,62 +738,110 @@ class SH3DImporter: """ elements = parent.findall(xpath) # Is it a real tag name or an xpath expression? - tag_name = xpath[3:] if xpath.startswith('.') else xpath + tag_name = xpath[3:] if xpath.startswith(".") else xpath total_steps, current_step = self._get_progress_info(xpath) total_elements = len(elements) if self.progress_bar: self.progress_bar.stop() - self.progress_bar.start(f"Step {current_step}/{total_steps}: importing {total_elements} '{tag_name}' elements. Standby…", total_elements) + self.progress_bar.start( + f"Step {current_step}/{total_steps}: importing {total_elements} '{tag_name}' elements. Standby…", + total_elements, + ) _msg(f"Importing {total_elements} '{tag_name}' elements…") handler = self.handlers[xpath] + def _process(tuple): (i, elm) = tuple - _msg(f"Importing {tag_name}#{i} ({self.current_object_count + 1}/{self.total_object_count})…") + _msg( + f"Importing {tag_name}#{i} ({self.current_object_count + 1}/{self.total_object_count})…" + ) try: # with Transaction(f"Importing {tag_name}#{i}"): - handler.process(parent, i, elm) + handler.process(parent, i, elm) except Exception as e: _err(f"Importing {tag_name}#{i} failed") _err(str(e)) _err(traceback.format_exc()) - if self.progress_bar: self.progress_bar.next() + if self.progress_bar: + self.progress_bar.next() self.current_object_count = self.current_object_count + 1 + list(map(_process, enumerate(elements))) def _get_progress_info(self, xpath): xpaths = list(self.handlers.keys()) total_steps = len(xpaths) - current_step = xpaths.index(xpath)+1 + current_step = xpaths.index(xpath) + 1 return total_steps, current_step def _set_site_properties(self, elm): # All information in environment?, backgroundImage?, print?, compass # are added to the site object. Some are furthermore added to the ground - environments = elm.findall('environment') + environments = elm.findall("environment") if len(environments) > 0: environment = environments[0] - ground_color = environment.get('groundColor',self.preferences["DEFAULT_GROUND_COLOR"]) - sky_color = environment.get('ceilingColor', self.preferences["DEFAULT_SKY_COLOR"]) - lightColor = environment.get('lightColor', self.preferences["DEFAULT_SKY_COLOR"]) - ceillingLightColor = environment.get('ceillingLightColor', self.preferences["DEFAULT_SKY_COLOR"]) + ground_color = environment.get("groundColor", self.preferences["DEFAULT_GROUND_COLOR"]) + sky_color = environment.get("ceilingColor", self.preferences["DEFAULT_SKY_COLOR"]) + lightColor = environment.get("lightColor", self.preferences["DEFAULT_SKY_COLOR"]) + ceillingLightColor = environment.get( + "ceillingLightColor", self.preferences["DEFAULT_SKY_COLOR"] + ) self.set_property(self.site, "App::PropertyString", "groundColor", "", ground_color) - self.set_property(self.site, "App::PropertyBool", "backgroundImageVisibleOnGround3D", "", environment) + self.set_property( + self.site, "App::PropertyBool", "backgroundImageVisibleOnGround3D", "", environment + ) self.set_property(self.site, "App::PropertyString", "skyColor", "", sky_color) self.set_property(self.site, "App::PropertyString", "lightColor", "", lightColor) self.set_property(self.site, "App::PropertyFloat", "wallsAlpha", "", environment) self.set_property(self.site, "App::PropertyBool", "allLevelsVisible", "", environment) - self.set_property(self.site, "App::PropertyBool", "observerCameraElevationAdjusted", "", environment) - self.set_property(self.site, "App::PropertyString", "ceillingLightColor", "", ceillingLightColor) - self.set_property(self.site, "App::PropertyEnumeration", "drawingMode", "", str(environment.get('drawingMode', 'FILL')), valid_values=["FILL", "OUTLINE", "FILL_AND_OUTLINE"]) - self.set_property(self.site, "App::PropertyFloat", "subpartSizeUnderLight", "", environment) + self.set_property( + self.site, "App::PropertyBool", "observerCameraElevationAdjusted", "", environment + ) + self.set_property( + self.site, "App::PropertyString", "ceillingLightColor", "", ceillingLightColor + ) + self.set_property( + self.site, + "App::PropertyEnumeration", + "drawingMode", + "", + str(environment.get("drawingMode", "FILL")), + valid_values=["FILL", "OUTLINE", "FILL_AND_OUTLINE"], + ) + self.set_property( + self.site, "App::PropertyFloat", "subpartSizeUnderLight", "", environment + ) self.set_property(self.site, "App::PropertyInteger", "photoWidth", "", environment) self.set_property(self.site, "App::PropertyInteger", "photoHeight", "", environment) - self.set_property(self.site, "App::PropertyEnumeration", "photoAspectRatio", "", str(environment.get('photoAspectRatio', 'VIEW_3D_RATIO')), valid_values=["FREE_RATIO", "VIEW_3D_RATIO", "RATIO_4_3", "RATIO_3_2", "RATIO_16_9", "RATIO_2_1", "RATIO_24_10", "SQUARE_RATIO"]) + self.set_property( + self.site, + "App::PropertyEnumeration", + "photoAspectRatio", + "", + str(environment.get("photoAspectRatio", "VIEW_3D_RATIO")), + valid_values=[ + "FREE_RATIO", + "VIEW_3D_RATIO", + "RATIO_4_3", + "RATIO_3_2", + "RATIO_16_9", + "RATIO_2_1", + "RATIO_24_10", + "SQUARE_RATIO", + ], + ) self.set_property(self.site, "App::PropertyInteger", "photoQuality", "", environment) self.set_property(self.site, "App::PropertyInteger", "videoWidth", "", environment) - self.set_property(self.site, "App::PropertyEnumeration", "videoAspectRatio", "", str(environment.get('videoAspectRatio', 'RATIO_4_3')), valid_values=["RATIO_4_3", "RATIO_16_9", "RATIO_24_10"]) + self.set_property( + self.site, + "App::PropertyEnumeration", + "videoAspectRatio", + "", + str(environment.get("videoAspectRatio", "RATIO_4_3")), + valid_values=["RATIO_4_3", "RATIO_16_9", "RATIO_24_10"], + ) self.set_property(self.site, "App::PropertyInteger", "photoQuality", "", environment) self.set_property(self.site, "App::PropertyInteger", "videoQuality", "", environment) self.set_property(self.site, "App::PropertyString", "videoSpeed", "", environment) @@ -780,7 +849,7 @@ class SH3DImporter: else: _msg(f"No tag found in <{elm.tag}>") - bg_imgs = elm.findall('backgroundImage') + bg_imgs = elm.findall("backgroundImage") if len(bg_imgs) > 0: bg_img = bg_imgs[0] self.set_property(self.site, "App::PropertyString", "image", "", bg_img) @@ -791,25 +860,49 @@ class SH3DImporter: self.set_property(self.site, "App::PropertyFloat", "scaleDistanceYEnd", "", bg_img) self.set_property(self.site, "App::PropertyFloat", "xOrigin", "", bg_img) self.set_property(self.site, "App::PropertyFloat", "yOrigin", "", bg_img) - self.set_property(self.site, "App::PropertyBool", "visible", "Whether the background image is visible", bg_img) + self.set_property( + self.site, + "App::PropertyBool", + "visible", + "Whether the background image is visible", + bg_img, + ) else: _msg(f"No tag found in <{elm.tag}>") - compasses = elm.findall('compass') + compasses = elm.findall("compass") if len(compasses) > 0: compass = compasses[0] self.set_property(self.site, "App::PropertyFloat", "x", "The compass's x", compass) self.set_property(self.site, "App::PropertyFloat", "y", "The compass's y", compass) - self.set_property(self.site, "App::PropertyFloat", "diameter", "The compass's diameter in cm", compass) - self.set_property(self.site, "App::PropertyFloat", "northDirection", "The compass's angle to the north in degree", compass) - self.set_property(self.site, "App::PropertyFloat", "longitude", "The compass's longitude", compass) - self.set_property(self.site, "App::PropertyFloat", "latitude", "The compass's latitude", compass) - self.set_property(self.site, "App::PropertyString", "timeZone", "The compass's TimeZone", compass) - self.set_property(self.site, "App::PropertyBool", "visible", "Whether the compass is visible", compass) + self.set_property( + self.site, "App::PropertyFloat", "diameter", "The compass's diameter in cm", compass + ) + self.set_property( + self.site, + "App::PropertyFloat", + "northDirection", + "The compass's angle to the north in degree", + compass, + ) + self.set_property( + self.site, "App::PropertyFloat", "longitude", "The compass's longitude", compass + ) + self.set_property( + self.site, "App::PropertyFloat", "latitude", "The compass's latitude", compass + ) + self.set_property( + self.site, "App::PropertyString", "timeZone", "The compass's TimeZone", compass + ) + self.set_property( + self.site, "App::PropertyBool", "visible", "Whether the compass is visible", compass + ) self.site.Declination = math.degrees(ang_sh2fc(float(self.site.northDirection))) self.site.Longitude = math.degrees(float(self.site.longitude)) self.site.Latitude = math.degrees(float(self.site.latitude)) - self.site.EPWFile = '' # https://www.ladybug.tools/epwmap/ or https://climate.onebuilding.org + self.site.EPWFile = ( + "" # https://www.ladybug.tools/epwmap/ or https://climate.onebuilding.org + ) else: _msg(f"No tag found in <{elm.tag}>") @@ -821,20 +914,25 @@ class SH3DImporter: total_elements = len(floors) if self.progress_bar: self.progress_bar.stop() - self.progress_bar.start(f"Step {current_step}/{total_steps}: Creating {total_elements} 'slab' elements. Standby…", len(all_walls) + len(all_spaces)) + self.progress_bar.start( + f"Step {current_step}/{total_steps}: Creating {total_elements} 'slab' elements. Standby…", + len(all_walls) + len(all_spaces), + ) _msg(f"Creating {total_elements} 'slab' elements…") handler = self.handlers[ET_XPATH_LEVEL] + def _create_slab(tuple): (i, floor) = tuple _msg(f"Creating slab#{i} for floor '{floor.Label}'…") try: # with Transaction(f"Creating slab#{i} for floor '{floor.Label}'"): - handler.create_slabs(floor, self.progress_bar) + handler.create_slabs(floor, self.progress_bar) except Exception as e: _err(f"Creating slab#{i} for floor '{floor.Label}' failed") _err(str(e)) _err(traceback.format_exc()) + list(map(_create_slab, enumerate(floors))) def _decorate_surfaces(self): @@ -842,23 +940,28 @@ class SH3DImporter: all_walls = self.get_all_walls() all_spaces = self.get_all_spaces() - total_elements = len(all_spaces)+len(all_walls) + total_elements = len(all_spaces) + len(all_walls) if self.progress_bar: self.progress_bar.stop() - self.progress_bar.start(f"Decorating {total_elements} elements. Standby…", total_elements) + self.progress_bar.start( + f"Decorating {total_elements} elements. Standby…", total_elements + ) _msg(f"Decorating {total_elements} elements…") handler = self.handlers[ET_XPATH_ROOM] for i, space in enumerate(all_spaces): handler.post_process(space) - if self.progress_bar: self.progress_bar.next() + if self.progress_bar: + self.progress_bar.next() handler = self.handlers[ET_XPATH_WALL] for i, wall in enumerate(all_walls): handler.post_process(wall) - if self.progress_bar: self.progress_bar.next() - if self.progress_bar: self.progress_bar.stop() + if self.progress_bar: + self.progress_bar.next() + if self.progress_bar: + self.progress_bar.stop() class BaseHandler: @@ -931,7 +1034,7 @@ class BaseHandler: return self.importer.get_walls(floor) def get_wall_spine(self, wall): - if not hasattr(wall, 'BaseObjects'): + if not hasattr(wall, "BaseObjects"): _err(f"Wall {wall.Label} has no BaseObjects to get the Spine from…") return wall.BaseObjects[2] @@ -964,7 +1067,7 @@ class BaseHandler: left_face_name = right_face_name = None left_face = right_face = None - for (i, face) in enumerate(wall.Shape.Faces): + for i, face in enumerate(wall.Shape.Faces): face_cog = face.CenterOfGravity # The face COG is not on the same z as the wall COG @@ -985,7 +1088,7 @@ class BaseHandler: break return (left_face_name, left_face, right_face_name, right_face) - def _get_face_side(self, start:App.Vector, end:App.Vector, cog:App.Vector): + def _get_face_side(self, start: App.Vector, end: App.Vector, cog: App.Vector): # Compute vectors ab = end - start # Vector from start to end ac = cog - start # Vector from start to CenterOfGravity @@ -1003,7 +1106,7 @@ class BaseHandler: def _ps(self, section, print_z: bool = False): # Pretty print a Section in a condensed way - if hasattr(section, 'Shape'): + if hasattr(section, "Shape"): v = section.Shape.Vertexes else: # a Part.Face @@ -1016,15 +1119,15 @@ class BaseHandler: return f"[{self._pv(v[0].Point, print_z)}, {self._pv(v[1].Point, print_z)}]" def _pes(self, edges, print_z: bool = False): - return '->'.join(list(map(lambda e: self._pe(e, print_z), edges))) + return "->".join(list(map(lambda e: self._pe(e, print_z), edges))) def _pv(self, v, print_z: bool = False, ndigits: None = None): # Print an Vector in a condensed way if not v: return "NaN" - if hasattr(v,'X'): + if hasattr(v, "X"): return f"({round(getattr(v, 'X'), ndigits)},{round(getattr(v, 'Y'), ndigits)}{',' + str(round(getattr(v, 'Z'), ndigits) if print_z else '')})" - elif hasattr(v,'x'): + elif hasattr(v, "x"): return f"({round(getattr(v, 'x'), ndigits)},{round(getattr(v, 'y'), ndigits)}{',' + str(round(getattr(v, 'z'), ndigits) if print_z else '')})" raise ValueError(f"Expected a Point or Vector, got {type(v)}") @@ -1046,7 +1149,7 @@ class BaseHandler: App.ActiveDocument.DEBUG_GEOMETRY.addObject(part) return part - def _debug_shape(self, shape, label, color=GREEN, transparency=.75, placement=None): + def _debug_shape(self, shape, label, color=GREEN, transparency=0.75, placement=None): part = Part.show(shape) if placement: part.Placement = placement @@ -1056,11 +1159,13 @@ class BaseHandler: material = part.ViewObject.ShapeAppearance[0] material.DiffuseColor = color material.Transparency = transparency - part.ViewObject.ShapeAppearance = (material) + part.ViewObject.ShapeAppearance = material App.ActiveDocument.DEBUG_GEOMETRY.addObject(part) return part - def _debug_mesh(self, mesh, label, transform=None, color=GREEN, transparency=.75, placement=None): + def _debug_mesh( + self, mesh, label, transform=None, color=GREEN, transparency=0.75, placement=None + ): shape = Part.Shape() new_mesh = mesh.copy() if transform: @@ -1084,35 +1189,52 @@ class LevelHandler(BaseHandler): """ floor = None if self.importer.preferences["MERGE"]: - floor = self.get_fc_object(elm.get("id"), 'level') + floor = self.get_fc_object(elm.get("id"), "level") if not floor: floor = Arch.makeFloor() - floor.Label = elm.get('name') - floor.Placement.Base.z = dim_sh2fc(float(elm.get('elevation'))) - floor.Height = dim_sh2fc(float(elm.get('height'))) + floor.Label = elm.get("name") + floor.Placement.Base.z = dim_sh2fc(float(elm.get("elevation"))) + floor.Height = dim_sh2fc(float(elm.get("height"))) self._set_properties(floor, elm) - floor.Visibility = elm.get('visible', 'true') == 'true' + floor.Visibility = elm.get("visible", "true") == "true" self._create_groups(floor) self.importer.add_floor(floor) def create_default_floor(self): floor = Arch.makeFloor() - floor.Label = 'Level' + floor.Label = "Level" floor.Placement.Base.z = 0 floor.Height = 2500 - self._set_properties(floor, dict({'shType': 'level', 'id':'Level', 'floorThickness':25, 'elevationIndex': 0, 'viewable': True})) + self._set_properties( + floor, + dict( + { + "shType": "level", + "id": "Level", + "floorThickness": 25, + "elevationIndex": 0, + "viewable": True, + } + ), + ) self._create_groups(floor) self.importer.add_floor(floor) return floor def _set_properties(self, obj, elm): - self.setp(obj, "App::PropertyString", "shType", "The element type", 'level') + self.setp(obj, "App::PropertyString", "shType", "The element type", "level") self.setp(obj, "App::PropertyString", "id", "The floor's id", elm) - self.setp(obj, "App::PropertyQuantity", "floorThickness", "The floor's slab thickness", dim_sh2fc(float(elm.get('floorThickness')))) + self.setp( + obj, + "App::PropertyQuantity", + "floorThickness", + "The floor's slab thickness", + dim_sh2fc(float(elm.get("floorThickness"))), + ) self.setp(obj, "App::PropertyInteger", "elevationIndex", "The floor number", elm) self.setp(obj, "App::PropertyBool", "viewable", "Whether the floor is viewable", elm) @@ -1130,9 +1252,15 @@ class LevelHandler(BaseHandler): if self.importer.preferences["DECORATE_SURFACES"]: self._create_group(floor, "DecorationWallsGroupName", f"Decoration-{floor.Label}-Walls") - self._create_group(floor, "DecorationCeilingsGroupName", f"Decoration-{floor.Label}-Ceilings") - self._create_group(floor, "DecorationFloorsGroupName", f"Decoration-{floor.Label}-Floors") - self._create_group(floor, "DecorationBaseboardsGroupName", f"Decoration-{floor.Label}-Baseboards") + self._create_group( + floor, "DecorationCeilingsGroupName", f"Decoration-{floor.Label}-Ceilings" + ) + self._create_group( + floor, "DecorationFloorsGroupName", f"Decoration-{floor.Label}-Floors" + ) + self._create_group( + floor, "DecorationBaseboardsGroupName", f"Decoration-{floor.Label}-Baseboards" + ) if self.importer.preferences["IMPORT_FURNITURES"]: self._create_group(floor, "FurnitureGroupName", f"Furnitures-{floor.Label}") @@ -1150,7 +1278,13 @@ class LevelHandler(BaseHandler): if not group: group = floor.newObject("App::DocumentObjectGroup") group.Label = group_label - self.setp(floor, "App::PropertyString", prop_group_name, "The DocumentObjectGroup name for the group on this floor", group.Name) + self.setp( + floor, + "App::PropertyString", + prop_group_name, + "The DocumentObjectGroup name for the group on this floor", + group.Name, + ) return group @@ -1166,7 +1300,7 @@ class LevelHandler(BaseHandler): """ slab = None if self.importer.preferences["MERGE"]: - slab = self.get_fc_object(f"{floor.id}-slab", 'slab') + slab = self.get_fc_object(f"{floor.id}-slab", "slab") def _extrude(obj_to_extrude): """Return the Part.Extrude suitable for fusion by the make_multi_fuse tool. @@ -1184,7 +1318,7 @@ class LevelHandler(BaseHandler): obj_to_extrude.recompute(True) projection = TechDraw.project(obj_to_extrude.Shape, Z_NORM)[0] face = Part.Face(Part.Wire(projection.Edges)) - extrude = face.extrude(-Z_NORM*floor.floorThickness.Value) + extrude = face.extrude(-Z_NORM * floor.floorThickness.Value) part = Part.show(extrude, "Extrusion") # part.Placement.Base.z = floor.Placement.Base.z part.Label = f"{floor.Label}-{obj_to_extrude.Label}-extrusion" @@ -1199,14 +1333,19 @@ class LevelHandler(BaseHandler): if not slab: # Take the spaces whose floor is actually visible, and all the walls - projections = list(map(lambda s: s.ReferenceFace, filter(lambda s: s.floorVisible, self.get_spaces(floor)))) + projections = list( + map( + lambda s: s.ReferenceFace, + filter(lambda s: s.floorVisible, self.get_spaces(floor)), + ) + ) projections.extend(list(map(lambda w: w.ReferenceFace, self.get_walls(floor)))) extrusions = list(map(_extrude, projections)) extrusions = list(filter(lambda o: o is not None, extrusions)) if len(extrusions) > 0: if len(extrusions) > 1: bf = BOPTools.BOPFeatures.BOPFeatures(App.ActiveDocument) - slab_base = bf.make_multi_fuse([ o.Name for o in extrusions]) + slab_base = bf.make_multi_fuse([o.Name for o in extrusions]) slab_base.Label = f"{floor.Label}-footprint" slab_base.recompute() else: @@ -1216,24 +1355,42 @@ class LevelHandler(BaseHandler): slab = Arch.makeStructure(slab_base) slab.Placement.Base.z = floor.Placement.Base.z slab.Normal = -Z_NORM - slab.setExpression('Height', f"{slab_base.Name}.Shape.BoundBox.ZLength") + slab.setExpression("Height", f"{slab_base.Name}.Shape.BoundBox.ZLength") else: _wrn(f"No object found for floor {floor.Label}.") - self.setp(floor, "App::PropertyString", "ReferenceSlabName", "The name of the Slab used on this floor", None) + self.setp( + floor, + "App::PropertyString", + "ReferenceSlabName", + "The name of the Slab used on this floor", + None, + ) return slab.Label = f"{floor.Label}-slab" if self.importer.preferences["DEBUG_GEOMETRY"]: - slab.ViewObject.DisplayMode = 'Wireframe' - slab.ViewObject.DrawStyle = 'Dotted' + slab.ViewObject.DisplayMode = "Wireframe" + slab.ViewObject.DrawStyle = "Dotted" slab.ViewObject.LineColor = ORANGE slab.ViewObject.LineWidth = 2 - self.setp(slab, "App::PropertyString", "shType", "The element type", 'slab') + self.setp(slab, "App::PropertyString", "shType", "The element type", "slab") self.setp(slab, "App::PropertyString", "id", "The slab's id", f"{floor.id}-slab") - self.setp(slab, "App::PropertyString", "ReferenceFloorName", "The name of the Arch.Floor this slab belongs to", floor.Name) - self.setp(floor, "App::PropertyString", "ReferenceSlabName", "The name of the Slab used on this floor", slab.Name) + self.setp( + slab, + "App::PropertyString", + "ReferenceFloorName", + "The name of the Arch.Floor this slab belongs to", + floor.Name, + ) + self.setp( + floor, + "App::PropertyString", + "ReferenceSlabName", + "The name of the Slab used on this floor", + slab.Name, + ) floor.addObject(slab) @@ -1256,29 +1413,34 @@ class RoomHandler(BaseHandler): """ debug_geometry = self.importer.preferences["DEBUG_GEOMETRY"] - level_id = elm.get('level', None) + level_id = elm.get("level", None) floor = self.get_floor(level_id) assert floor != None, f"Missing floor '{level_id}' for '{elm.get('id')}'…" space = face = None if self.importer.preferences["MERGE"]: - space = self.get_fc_object(elm.get("id"), 'room') + space = self.get_fc_object(elm.get("id"), "room") # A Room is composed of a space with a Face as the base object if not space: - name = elm.get('name', 'Room') + name = elm.get("name", "Room") floor_z = dim_fc2sh(floor.Placement.Base.z) - points = [ coord_sh2fc(App.Vector(float(p.get('x')), float(p.get('y')), floor_z)) for p in elm.findall('point') ] + points = [ + coord_sh2fc(App.Vector(float(p.get("x")), float(p.get("y")), floor_z)) + for p in elm.findall("point") + ] # remove consecutive identical points points = [points[i] for i in range(len(points)) if i == 0 or points[i] != points[i - 1]] # and close the wire points.append(points[0]) # Offset to avoid self-intersecting wires reference_wire = Part.makePolygon(points) - if debug_geometry: self._debug_shape(reference_wire, f"{name}-reference-wire", RED) + if debug_geometry: + self._debug_shape(reference_wire, f"{name}-reference-wire", RED) reference_wire = self._get_offset_wire(reference_wire) - if debug_geometry: self._debug_shape(reference_wire, f"{name}-reference-wire-offset", RED) + if debug_geometry: + self._debug_shape(reference_wire, f"{name}-reference-wire-offset", RED) points = [v.Point for v in reference_wire.Vertexes] reference_face = Draft.make_wire(points, closed=True, face=True, support=None) reference_face.Label = f"{name}-reference" @@ -1292,16 +1454,34 @@ class RoomHandler(BaseHandler): footprint = App.ActiveDocument.addObject("Part::Feature", "Footprint") footprint.Shape = reference_face.Shape.extrude(Z_NORM) footprint.Label = f"{name}-footprint" - self.setp(footprint, "App::PropertyLink", "ReferenceFace", "The Reference Part.Wire", reference_face) + self.setp( + footprint, + "App::PropertyLink", + "ReferenceFace", + "The Reference Part.Wire", + reference_face, + ) space = Arch.makeSpace(footprint) space.IfcType = "Space" space.Label = name self._set_properties(space, elm) - space.setExpression('ElevationWithFlooring', f"{footprint.Name}.Shape.BoundBox.ZMin") - self.setp(space, "App::PropertyString", "ReferenceFloorName", "The name of the Arch.Floor this room belongs to", floor.Name) - self.setp(space, "App::PropertyLink", "ReferenceFace", "The Reference Part.Wire", reference_face) + space.setExpression("ElevationWithFlooring", f"{footprint.Name}.Shape.BoundBox.ZMin") + self.setp( + space, + "App::PropertyString", + "ReferenceFloorName", + "The name of the Arch.Floor this room belongs to", + floor.Name, + ) + self.setp( + space, + "App::PropertyLink", + "ReferenceFace", + "The Reference Part.Wire", + reference_face, + ) self.importer.add_space(floor, space) @@ -1310,24 +1490,62 @@ class RoomHandler(BaseHandler): floor.addObject(space) def _set_properties(self, obj, elm): - floor_color = elm.get('floorColor',self.importer.preferences["DEFAULT_FLOOR_COLOR"]) - ceiling_color = elm.get('ceilingColor', self.importer.preferences["DEFAULT_CEILING_COLOR"]) + floor_color = elm.get("floorColor", self.importer.preferences["DEFAULT_FLOOR_COLOR"]) + ceiling_color = elm.get("ceilingColor", self.importer.preferences["DEFAULT_CEILING_COLOR"]) - self.setp(obj, "App::PropertyString", "shType", "The element type", 'room') - self.setp(obj, "App::PropertyString", "id", "The slab's id", elm.get('id', str(uuid.uuid4()))) + self.setp(obj, "App::PropertyString", "shType", "The element type", "room") + self.setp( + obj, "App::PropertyString", "id", "The slab's id", elm.get("id", str(uuid.uuid4())) + ) self.setp(obj, "App::PropertyFloat", "nameAngle", "The room's name angle", elm) self.setp(obj, "App::PropertyFloat", "nameXOffset", "The room's name x offset", elm) self.setp(obj, "App::PropertyFloat", "nameYOffset", "The room's name y offset", elm) - self.setp(obj, "App::PropertyBool", "areaVisible", "Whether the area of the room is displayed in the plan view", elm) + self.setp( + obj, + "App::PropertyBool", + "areaVisible", + "Whether the area of the room is displayed in the plan view", + elm, + ) self.setp(obj, "App::PropertyFloat", "areaAngle", "The room's area annotation angle", elm) - self.setp(obj, "App::PropertyFloat", "areaXOffset", "The room's area annotation x offset", elm) - self.setp(obj, "App::PropertyFloat", "areaYOffset", "The room's area annotation y offset", elm) - self.setp(obj, "App::PropertyBool", "floorVisible", "Whether the floor of the room is displayed", elm) + self.setp( + obj, "App::PropertyFloat", "areaXOffset", "The room's area annotation x offset", elm + ) + self.setp( + obj, "App::PropertyFloat", "areaYOffset", "The room's area annotation y offset", elm + ) + self.setp( + obj, + "App::PropertyBool", + "floorVisible", + "Whether the floor of the room is displayed", + elm, + ) self.setp(obj, "App::PropertyString", "floorColor", "The room's floor color", floor_color) - self.setp(obj, "App::PropertyPercent", "floorShininess", "The room's floor shininess", percent_sh2fc(elm.get('floorShininess', 0))) - self.setp(obj, "App::PropertyBool", "ceilingVisible", "Whether the ceiling of the room is displayed", elm) - self.setp(obj, "App::PropertyString", "ceilingColor", "The room's ceiling color", ceiling_color) - self.setp(obj, "App::PropertyPercent", "ceilingShininess", "The room's ceiling shininess", percent_sh2fc(elm.get('ceilingShininess', 0))) + self.setp( + obj, + "App::PropertyPercent", + "floorShininess", + "The room's floor shininess", + percent_sh2fc(elm.get("floorShininess", 0)), + ) + self.setp( + obj, + "App::PropertyBool", + "ceilingVisible", + "Whether the ceiling of the room is displayed", + elm, + ) + self.setp( + obj, "App::PropertyString", "ceilingColor", "The room's ceiling color", ceiling_color + ) + self.setp( + obj, + "App::PropertyPercent", + "ceilingShininess", + "The room's ceiling shininess", + percent_sh2fc(elm.get("ceilingShininess", 0)), + ) self.setp(obj, "App::PropertyBool", "ceilingFlat", "", elm) def _get_offset_wire(self, wire, inward=True): @@ -1354,8 +1572,8 @@ class RoomHandler(BaseHandler): # Self intersecting wire can not be properly extruded to # create rooms. We offset the wire inward until it stop # self intersecting. - offset_wire = DraftGeomUtils.offsetWire(wire, offset_vector*multiplier) - self_intersect = self._self_intersect(offset_wire.Edges) + offset_wire = DraftGeomUtils.offsetWire(wire, offset_vector * multiplier) + self_intersect = self._self_intersect(offset_wire.Edges) multiplier += 1 else: if self_intersect: @@ -1383,12 +1601,14 @@ class RoomHandler(BaseHandler): (dist, vectors, _) = e1.distToShape(e2) if dist > 0: continue - for (v1, v2) in vectors: + for v1, v2 in vectors: # Check that the intersections are not extremities # If both v1 and v2 are extremities then the edges # are connected which is not really a self-intersecting # situation. - if v1 not in [v.Point for v in e1.Vertexes] or v2 not in [v.Point for v in e2.Vertexes]: + if v1 not in [v.Point for v in e1.Vertexes] or v2 not in [ + v.Point for v in e2.Vertexes + ]: return True return False @@ -1415,24 +1635,34 @@ class RoomHandler(BaseHandler): facebinder_id = f"{floor.id}-{space.id}-{side}-facebinder" facebinder = None if self.importer.preferences["MERGE"]: - facebinder = self.get_fc_object(facebinder_id, 'facebinder') + facebinder = self.get_fc_object(facebinder_id, "facebinder") if not facebinder: # NOTE: always use Face1 as this is a 2D object - facebinder = Draft.make_facebinder(( space.ReferenceFace, ("Face1", ) )) + facebinder = Draft.make_facebinder((space.ReferenceFace, ("Face1",))) facebinder.Extrusion = 1 facebinder.Label = space.Label + f" {side} finish" - facebinder.Placement.Base.z = 1 if (side == "floor") else floor.Height.Value-1 + facebinder.Placement.Base.z = 1 if (side == "floor") else floor.Height.Value - 1 facebinder.Visibility = getattr(space, f"{side}Visible") set_color_and_transparency(facebinder, getattr(space, f"{side}Color")) set_shininess(facebinder, getattr(space, f"{side}Shininess", 0)) - self.setp(facebinder, "App::PropertyString", "shType", "The element type", 'facebinder') + self.setp(facebinder, "App::PropertyString", "shType", "The element type", "facebinder") self.setp(facebinder, "App::PropertyString", "id", "The element's id", facebinder_id) - self.setp(facebinder, "App::PropertyString", "ReferenceRoomName", "The Reference Arch.Space", space.Name) + self.setp( + facebinder, + "App::PropertyString", + "ReferenceRoomName", + "The Reference Arch.Space", + space.Name, + ) - group_name = getattr(floor, "DecorationFloorsGroupName") if (side == "floor") else getattr(floor, "DecorationCeilingsGroupName") + group_name = ( + getattr(floor, "DecorationFloorsGroupName") + if (side == "floor") + else getattr(floor, "DecorationCeilingsGroupName") + ) floor.getObject(group_name).addObject(facebinder) @@ -1451,17 +1681,17 @@ class WallHandler(BaseHandler): i (int): the ordinal of the imported element elm (Element): the xml element """ - level_id = elm.get('level', None) + level_id = elm.get("level", None) floor = self.get_floor(level_id) assert floor != None, f"Missing floor '{level_id}' for '{elm.get('id')}'…" wall = base_object = None if self.importer.preferences["MERGE"]: - wall = self.get_fc_object(elm.get("id"), 'wall') + wall = self.get_fc_object(elm.get("id"), "wall") if not wall: - prev = self._get_sibling_wall(parent, elm, 'wallAtStart') - next = self._get_sibling_wall(parent, elm, 'wallAtEnd') + prev = self._get_sibling_wall(parent, elm, "wallAtStart") + next = self._get_sibling_wall(parent, elm, "wallAtEnd") wall, base_object = self._create_wall(floor, prev, next, elm) if not wall: _log(f"No wall created for {elm.get('id')}. Skipping!") @@ -1475,7 +1705,13 @@ class WallHandler(BaseHandler): wall.BaseObjects[2].Label = f"wall{i}-spine" self._set_properties(wall, elm) self._set_baseboard_properties(wall, elm) - self.setp(wall, "App::PropertyString", "ReferenceFloorName", "The Name of the Arch.Floor this walls belongs to", floor.Name) + self.setp( + wall, + "App::PropertyString", + "ReferenceFloorName", + "The Name of the Arch.Floor this walls belongs to", + floor.Name, + ) wall.recompute(True) @@ -1493,37 +1729,89 @@ class WallHandler(BaseHandler): return None sibling_wall = parent.find(f"./wall[@id='{sibling_wall_id}']") if sibling_wall is None: - wall_id = wall.get('id') - raise ValueError(f"Invalid SweetHome3D file: wall {wall_id} reference an unknown wall {sibling_wall_id}") + wall_id = wall.get("id") + raise ValueError( + f"Invalid SweetHome3D file: wall {wall_id} reference an unknown wall {sibling_wall_id}" + ) return sibling_wall def _set_properties(self, obj, elm): - top_color = elm.get('topColor', self.importer.preferences["DEFAULT_FLOOR_COLOR"]) - left_side_color = elm.get('leftSideColor', self.importer.preferences["DEFAULT_FLOOR_COLOR"]) - right_side_color = elm.get('rightSideColor', self.importer.preferences["DEFAULT_FLOOR_COLOR"]) + top_color = elm.get("topColor", self.importer.preferences["DEFAULT_FLOOR_COLOR"]) + left_side_color = elm.get("leftSideColor", self.importer.preferences["DEFAULT_FLOOR_COLOR"]) + right_side_color = elm.get( + "rightSideColor", self.importer.preferences["DEFAULT_FLOOR_COLOR"] + ) - self.setp(obj, "App::PropertyString", "shType", "The element type", 'wall') + self.setp(obj, "App::PropertyString", "shType", "The element type", "wall") self.setp(obj, "App::PropertyString", "id", "The wall's id", elm) - self.setp(obj, "App::PropertyString", "wallAtStart", "The Id of the contiguous wall at the start of this wall", elm) - self.setp(obj, "App::PropertyString", "wallAtEnd", "The Id of the contiguous wall at the end of this wall", elm) - self.setp(obj, "App::PropertyString", "pattern", "The pattern of this wall in plan view", elm) + self.setp( + obj, + "App::PropertyString", + "wallAtStart", + "The Id of the contiguous wall at the start of this wall", + elm, + ) + self.setp( + obj, + "App::PropertyString", + "wallAtEnd", + "The Id of the contiguous wall at the end of this wall", + elm, + ) + self.setp( + obj, "App::PropertyString", "pattern", "The pattern of this wall in plan view", elm + ) self.setp(obj, "App::PropertyString", "topColor", "The wall inner color", top_color) - self.setp(obj, "App::PropertyString", "leftSideColor", "The wall inner color", left_side_color) - self.setp(obj, "App::PropertyPercent","leftSideShininess", "The room's ceiling shininess", percent_sh2fc(elm.get('leftSideShininess', 0))) - self.setp(obj, "App::PropertyString", "rightSideColor", "The wall inner color", right_side_color) - self.setp(obj, "App::PropertyPercent","rightSideShininess", "The room's ceiling shininess", percent_sh2fc(elm.get('rightSideShininess', 0))) + self.setp( + obj, "App::PropertyString", "leftSideColor", "The wall inner color", left_side_color + ) + self.setp( + obj, + "App::PropertyPercent", + "leftSideShininess", + "The room's ceiling shininess", + percent_sh2fc(elm.get("leftSideShininess", 0)), + ) + self.setp( + obj, "App::PropertyString", "rightSideColor", "The wall inner color", right_side_color + ) + self.setp( + obj, + "App::PropertyPercent", + "rightSideShininess", + "The room's ceiling shininess", + percent_sh2fc(elm.get("rightSideShininess", 0)), + ) def _set_baseboard_properties(self, obj, elm): # Baseboard are a little bit special: # Since their placement and other characteristics are dependent on # the wall elements to be created (such as doorOrWndows), their # creation is delayed until then - for baseboard in elm.findall('baseboard'): - side = baseboard.get('attribute') - self.setp(obj, "App::PropertyQuantity", f"{side}Thickness", f"The thickness of the {side} baseboard", dim_sh2fc(float(baseboard.get("thickness")))) - self.setp(obj, "App::PropertyQuantity", f"{side}Height", f"The height of the {side} baseboard", dim_sh2fc(float(baseboard.get("height")))) - self.setp(obj, "App::PropertyString", f"{side}Color", f"The color of the {side} baseboard", baseboard.get("color")) + for baseboard in elm.findall("baseboard"): + side = baseboard.get("attribute") + self.setp( + obj, + "App::PropertyQuantity", + f"{side}Thickness", + f"The thickness of the {side} baseboard", + dim_sh2fc(float(baseboard.get("thickness"))), + ) + self.setp( + obj, + "App::PropertyQuantity", + f"{side}Height", + f"The height of the {side} baseboard", + dim_sh2fc(float(baseboard.get("height"))), + ) + self.setp( + obj, + "App::PropertyString", + f"{side}Color", + f"The color of the {side} baseboard", + baseboard.get("color"), + ) def _create_wall(self, floor, prev, next, elm): """Create an Arch::Structure from an SH3D Element. @@ -1543,7 +1831,9 @@ class WallHandler(BaseHandler): debug_geometry = self.importer.preferences["DEBUG_GEOMETRY"] wall_details = self._get_wall_details(floor, elm) - assert wall_details is not None, f"Fail to get details of wall {elm.get('id')}. Bailing out! {elm} / {wall_details}" + assert ( + wall_details is not None + ), f"Fail to get details of wall {elm.get('id')}. Bailing out! {elm} / {wall_details}" # Both the wall at start or the wall at end can be None. prev_wall_details = self._get_wall_details(floor, prev) @@ -1554,19 +1844,19 @@ class WallHandler(BaseHandler): # Is the wall curved (i.e. arc_extent != 0) ? if is_wall_straight: section_start, section_end, spine = self._create_straight_segment( - wall_details, - prev_wall_details, - next_wall_details) + wall_details, prev_wall_details, next_wall_details + ) else: section_start, section_end, spine = self._create_curved_segment( - wall_details, - prev_wall_details, - next_wall_details) + wall_details, prev_wall_details, next_wall_details + ) base_object = None App.ActiveDocument.recompute([section_start, section_end, spine]) if debug_geometry: - _log(f"_create_wall(): wall => section_start={self._ps(section_start)}, section_end={self._ps(section_end)}") + _log( + f"_create_wall(): wall => section_start={self._ps(section_start)}, section_end={self._ps(section_end)}" + ) sweep = self._make_sweep(section_start, section_end, spine) # Sometimes the Part::Sweep creates a "twisted" sweep which @@ -1587,26 +1877,42 @@ class WallHandler(BaseHandler): wall = Arch.makeWall(sweep) if debug_geometry: - wall.ViewObject.DisplayMode = 'Wireframe' - wall.ViewObject.DrawStyle = 'Dotted' + wall.ViewObject.DisplayMode = "Wireframe" + wall.ViewObject.DrawStyle = "Dotted" wall.ViewObject.LineColor = ORANGE wall.ViewObject.LineWidth = 2 self._debug_point(spine.Start, f"{wall.Name}-start") reference_face = self._get_reference_face(wall, is_wall_straight) if reference_face: - self.setp(wall, "App::PropertyLink", "ReferenceFace", "The Reference Part.Wire", reference_face) + self.setp( + wall, + "App::PropertyLink", + "ReferenceFace", + "The Reference Part.Wire", + reference_face, + ) floor.getObject(floor.ReferenceFacesGroupName).addObject(reference_face) else: _err(f"Failed to get the reference face for wall {wall.Name}. Slab might fail!") # Keep track of base objects. Used to decorate walls - self.importer.set_property(wall, "App::PropertyLinkList", "BaseObjects", "The different base objects whose sweep failed. Kept for compatibility reasons", [section_start, section_end, spine]) + self.importer.set_property( + wall, + "App::PropertyLinkList", + "BaseObjects", + "The different base objects whose sweep failed. Kept for compatibility reasons", + [section_start, section_end, spine], + ) # TODO: Width is incorrect when joining walls - wall.setExpression('Length', f'{spine.Name}.Length') - wall.setExpression('Width', f'({section_start.Name}.Length + {section_end.Name}.Length) / 2') - wall.setExpression('Height', f'({section_start.Name}.Height + {section_end.Name}.Height) / 2') + wall.setExpression("Length", f"{spine.Name}.Length") + wall.setExpression( + "Width", f"({section_start.Name}.Length + {section_end.Name}.Length) / 2" + ) + wall.setExpression( + "Height", f"({section_start.Name}.Height + {section_end.Name}.Height) / 2" + ) return wall, base_object @@ -1621,7 +1927,7 @@ class WallHandler(BaseHandler): Returns: Part::Sweep: the Part::Sweep """ - sweep = App.ActiveDocument.addObject('Part::Sweep', "WallShape") + sweep = App.ActiveDocument.addObject("Part::Sweep", "WallShape") sweep.Sections = [section_start, section_end] sweep.Spine = spine sweep.Solid = True @@ -1645,7 +1951,7 @@ class WallHandler(BaseHandler): Returns: Compound: the compound """ - ruled_surface = App.ActiveDocument.addObject('Part::RuledSurface') + ruled_surface = App.ActiveDocument.addObject("Part::RuledSurface") ruled_surface.Curve1 = section_start ruled_surface.Curve2 = section_end ruled_surface.recompute() @@ -1675,16 +1981,16 @@ class WallHandler(BaseHandler): """ if elm is None: return None - x_start = float(elm.get('xStart')) - y_start = float(elm.get('yStart')) - x_end = float(elm.get('xEnd')) - y_end = float(elm.get('yEnd')) + x_start = float(elm.get("xStart")) + y_start = float(elm.get("yStart")) + x_end = float(elm.get("xEnd")) + y_end = float(elm.get("yEnd")) z = dim_fc2sh(floor.Placement.Base.z) - thickness = dim_sh2fc(elm.get('thickness')) - arc_extent = ang_sh2fc(elm.get('arcExtent', 0)) - height_start = dim_sh2fc(elm.get('height', dim_fc2sh(floor.Height))) - height_end = dim_sh2fc(elm.get('heightAtEnd', dim_fc2sh(height_start))) + thickness = dim_sh2fc(elm.get("thickness")) + arc_extent = ang_sh2fc(elm.get("arcExtent", 0)) + height_start = dim_sh2fc(elm.get("height", dim_fc2sh(floor.Height))) + height_end = dim_sh2fc(elm.get("heightAtEnd", dim_fc2sh(height_start))) start = coord_sh2fc(App.Vector(x_start, y_start, z)) end = coord_sh2fc(App.Vector(x_end, y_end, z)) @@ -1723,7 +2029,7 @@ class WallHandler(BaseHandler): Returns: Rectangle, Rectangle, spine: both section and the arc for the wall - # """ + #""" (start, end, _, _, _, _) = wall_details a1, a2, (invert_angle, center, radius) = self._get_normal_angles(wall_details) @@ -1732,8 +2038,8 @@ class WallHandler(BaseHandler): section_end = self._get_section(wall_details, False, next_wall_details, a1, a2) if self.importer.preferences["DEBUG_GEOMETRY"]: - self._debug_vector(start-center, "start-center", GREEN, center) - self._debug_vector(end-center, "end-center", BLUE, center) + self._debug_vector(start - center, "start-center", GREEN, center) + self._debug_vector(end - center, "end-center", BLUE, center) placement = App.Placement(center, App.Rotation()) # BEWARE: makeCircle always draws counter-clockwise (i.e. in positive @@ -1748,11 +2054,22 @@ class WallHandler(BaseHandler): # The Length property is used in the Wall to calculate volume, etc... # Since make Circle does not calculate this Length I calculate it here... - self.importer.set_property(spine, "App::PropertyFloat", "Length", "The length of the Arc", length, group="Draft") + self.importer.set_property( + spine, "App::PropertyFloat", "Length", "The length of the Arc", length, group="Draft" + ) # The Start and End property are used in the Wall to determine Facebinders # characteristics... - self.importer.set_property(spine, "App::PropertyVector", "Start", "The start point of the Arc", start, group="Draft") - self.importer.set_property(spine, "App::PropertyVector", "End", "The end point of the Arc", end, group="Draft") + self.importer.set_property( + spine, + "App::PropertyVector", + "Start", + "The start point of the Arc", + start, + group="Draft", + ) + self.importer.set_property( + spine, "App::PropertyVector", "End", "The end point of the Arc", end, group="Draft" + ) return section_start, section_end, spine @@ -1791,7 +2108,9 @@ class WallHandler(BaseHandler): i_end_z = i_end + App.Vector(0, 0, height) if debug_geometry: - _log(f"Joining wall {self._pv(end-start)}@{self._pv(start)} and wall {self._pv(s_end-s_start)}@{self._pv(s_start)}") + _log( + f"Joining wall {self._pv(end-start)}@{self._pv(start)} and wall {self._pv(s_end-s_start)}@{self._pv(s_start)}" + ) _log(f" wall: {self._pe(lside)},{self._pe(rside)}") _log(f" sibling: {self._pe(s_lside)},{self._pe(s_rside)}") _log(f"intersec: {self._pv(i_start)},{self._pv(i_end)}") @@ -1804,7 +2123,7 @@ class WallHandler(BaseHandler): center = start if at_start else end z_rotation = a1 if at_start else a2 section = Draft.makeRectangle(thickness, height, face=True) - Draft.move([section], App.Vector(-thickness/2, 0, 0)) + Draft.move([section], App.Vector(-thickness / 2, 0, 0)) Draft.rotate([section], 90, ORIGIN, X_NORM) Draft.rotate([section], z_rotation, ORIGIN, Z_NORM) Draft.move([section], center) @@ -1861,18 +2180,18 @@ class WallHandler(BaseHandler): center = radius = None if arc_extent == 0: # Straight Wall... - angle_start = angle_end = 90 - norm_deg_ang(DraftVecUtils.angle(end-start, X_NORM)) + angle_start = angle_end = 90 - norm_deg_ang(DraftVecUtils.angle(end - start, X_NORM)) else: # Calculate the circle that pases through the center of both rectangle # and has the correct angle between p1 and p2 chord = DraftVecUtils.dist(start, end) - radius = abs(chord / (2*math.sin(arc_extent/2))) + radius = abs(chord / (2 * math.sin(arc_extent / 2))) circles = DraftGeomUtils.circleFrom2PointsRadius(start, end, radius) # We take the center that preserve the arc_extent orientation (in FC # coordinate). The orientation is calculated from start to end center = circles[0].Center - angle = norm_deg_ang(DraftVecUtils.angle(start-center, end-center, Z_NORM)) + angle = norm_deg_ang(DraftVecUtils.angle(start - center, end - center, Z_NORM)) if self.importer.preferences["DEBUG_GEOMETRY"]: _msg(f"arc_extent={norm_deg_ang(arc_extent)}, angle={angle}") if norm_deg_ang(arc_extent) != angle: @@ -1900,14 +2219,16 @@ class WallHandler(BaseHandler): Edge: the left handside edge of the wall Edge: the right handside edge of the wall """ - normal = self._get_normal(start, end, start+Z_NORM) - loffset = DraftVecUtils.scale(-normal, thickness/2) - roffset = DraftVecUtils.scale(normal, thickness/2) + normal = self._get_normal(start, end, start + Z_NORM) + loffset = DraftVecUtils.scale(-normal, thickness / 2) + roffset = DraftVecUtils.scale(normal, thickness / 2) edge = DraftGeomUtils.edg(start, end) lside = DraftGeomUtils.offset(edge, loffset) rside = DraftGeomUtils.offset(edge, roffset) if self.importer.preferences["DEBUG_GEOMETRY"]: - _log(f"_get_sides(): wall {self._pv(end-start)}@{self._pv(start)} => normal={self._pv(normal)}, lside={self._pe(lside)}, rside={self._pe(rside)}") + _log( + f"_get_sides(): wall {self._pv(end-start)}@{self._pv(start)} => normal={self._pv(normal)}, lside={self._pe(lside)}, rside={self._pe(rside)}" + ) return lside, rside def _get_normal(self, a, b, c): @@ -1942,13 +2263,17 @@ class WallHandler(BaseHandler): Part.Wire: the wire for the reference face """ # Extract the reference face for later use (when creating the slab) - bottom_faces = list(filter(lambda f: Z_NORM.isEqual(-f.normalAt(0,0),1e-6), wall.Base.Shape.Faces)) + bottom_faces = list( + filter(lambda f: Z_NORM.isEqual(-f.normalAt(0, 0), 1e-6), wall.Base.Shape.Faces) + ) if len(bottom_faces) == 0: return None if len(bottom_faces) > 1: - _wrn(f"Base object for wall {wall.Name} has several bottom facing reference faces! Defaulting to 1st one.") + _wrn( + f"Base object for wall {wall.Name} has several bottom facing reference faces! Defaulting to 1st one." + ) face = bottom_faces.pop(0) @@ -1996,18 +2321,18 @@ class WallHandler(BaseHandler): # The top color is the color of the "mass" of the wall top_color = wall.topColor set_color_and_transparency(wall, top_color) - self._create_facebinder(floor, wall,left_face_name, "left") - self._create_facebinder(floor, wall, right_face_name, "right") + self._create_facebinder(floor, wall, left_face_name, "left") + self._create_facebinder(floor, wall, right_face_name, "right") def _create_facebinder(self, floor, wall, face_name, side): if face_name: facebinder_id = f"{wall.id}-{side}-facebinder" facebinder = None if self.importer.preferences["MERGE"]: - facebinder = self.get_fc_object(facebinder_id, 'facebinder') + facebinder = self.get_fc_object(facebinder_id, "facebinder") if not facebinder: - facebinder = Draft.make_facebinder(( wall, (face_name, ) )) + facebinder = Draft.make_facebinder((wall, (face_name,))) facebinder.Extrusion = 1 facebinder.Label = wall.Label + f" {side} side finish" @@ -2015,9 +2340,15 @@ class WallHandler(BaseHandler): set_color_and_transparency(facebinder, color) shininess = getattr(wall, f"{side}SideShininess", 0) set_shininess(facebinder, shininess) - self.setp(facebinder, "App::PropertyString", "shType", "The element type", 'facebinder') + self.setp(facebinder, "App::PropertyString", "shType", "The element type", "facebinder") self.setp(facebinder, "App::PropertyString", "id", "The element's id", facebinder_id) - self.setp(facebinder, "App::PropertyString", "ReferenceWallName", "The element's wall Name", wall.Name) + self.setp( + facebinder, + "App::PropertyString", + "ReferenceWallName", + "The element's wall Name", + wall.Name, + ) floor.getObject(floor.DecorationWallsGroupName).addObject(facebinder) else: @@ -2040,7 +2371,9 @@ class WallHandler(BaseHandler): if hasattr(wall, f"{side}Height"): face = left_face if side == "leftSideBaseboard" else right_face if not face: - _err(f"Weird: Invalid {side} face for wall {wall.Label}. Skipping baseboard creation") + _err( + f"Weird: Invalid {side} face for wall {wall.Label}. Skipping baseboard creation" + ) continue self._create_baseboard(floor, wall, side, face) @@ -2050,7 +2383,7 @@ class WallHandler(BaseHandler): baseboard_height = getattr(wall, f"{side}Height").Value # Once I have the face, I get the lowest edge. - lowest_z = float('inf') + lowest_z = float("inf") bottom_edge = None for edge in face.Edges: @@ -2064,7 +2397,9 @@ class WallHandler(BaseHandler): offset_bottom_edge = bottom_edge.translated(offset_vector) if self.importer.preferences["DEBUG_GEOMETRY"]: - _log(f"Creating {side} for {wall.Label} from edge {self._pe(bottom_edge, True)} to {self._pe(offset_bottom_edge, True)} (normal={self._pv(p_normal, True, 4)})") + _log( + f"Creating {side} for {wall.Label} from edge {self._pe(bottom_edge, True)} to {self._pe(offset_bottom_edge, True)} (normal={self._pv(p_normal, True, 4)})" + ) edge0 = bottom_edge.copy() edge1 = Part.makeLine(bottom_edge.Vertexes[1].Point, offset_bottom_edge.Vertexes[1].Point) @@ -2079,13 +2414,13 @@ class WallHandler(BaseHandler): baseboard_id = f"{wall.id} {side}" baseboard = None if self.importer.preferences["MERGE"]: - baseboard = self.get_fc_object(baseboard_id, 'baseboard') + baseboard = self.get_fc_object(baseboard_id, "baseboard") if not baseboard: base = App.ActiveDocument.addObject("Part::Feature", f"{wall.Label} {side} base") - base.Shape = Part.makeFace([ Part.Wire([edge0, edge1, edge2, edge3]) ]) + base.Shape = Part.makeFace([Part.Wire([edge0, edge1, edge2, edge3])]) base.Visibility = False - baseboard = App.ActiveDocument.addObject('Part::Extrusion', f"{wall.Label} {side}") + baseboard = App.ActiveDocument.addObject("Part::Extrusion", f"{wall.Label} {side}") baseboard.Base = base baseboard.DirMode = "Custom" @@ -2101,9 +2436,15 @@ class WallHandler(BaseHandler): set_color_and_transparency(baseboard, getattr(wall, f"{side}Color")) - self.setp(baseboard, "App::PropertyString", "shType", "The element type", 'baseboard') + self.setp(baseboard, "App::PropertyString", "shType", "The element type", "baseboard") self.setp(baseboard, "App::PropertyString", "id", "The element's id", baseboard_id) - self.setp(baseboard, "App::PropertyString", "ReferenceWallName", "The element's wall Name", wall.Name) + self.setp( + baseboard, + "App::PropertyString", + "ReferenceWallName", + "The element's wall Name", + wall.Name, + ) baseboard.recompute(True) floor.getObject(floor.DecorationBaseboardsGroupName).addObject(baseboard) @@ -2125,8 +2466,20 @@ class BaseFurnitureHandler(BaseHandler): self.setp(obj, "App::PropertyString", "information", "The object's information", elm) self.setp(obj, "App::PropertyString", "license", "The object's license", elm) self.setp(obj, "App::PropertyString", "creator", "The object's creator", elm) - self.setp(obj, "App::PropertyBool", "modelMirrored", "Whether the object is mirrored", bool(elm.get('modelMirrored', False))) - self.setp(obj, "App::PropertyBool", "nameVisible", "Whether the object's name is visible", bool(elm.get('nameVisible', False))) + self.setp( + obj, + "App::PropertyBool", + "modelMirrored", + "Whether the object is mirrored", + bool(elm.get("modelMirrored", False)), + ) + self.setp( + obj, + "App::PropertyBool", + "nameVisible", + "Whether the object's name is visible", + bool(elm.get("nameVisible", False)), + ) self.setp(obj, "App::PropertyFloat", "nameAngle", "The object's name angle", elm) self.setp(obj, "App::PropertyFloat", "nameXOffset", "The object's name X offset", elm) self.setp(obj, "App::PropertyFloat", "nameYOffset", "The object's name Y offset", elm) @@ -2138,33 +2491,75 @@ class BaseFurnitureHandler(BaseHandler): self.setp(obj, "App::PropertyFloat", "dropOnTopElevation", "", elm) self.setp(obj, "App::PropertyString", "model", "The object's mesh file", elm) self.setp(obj, "App::PropertyString", "icon", "The object's icon", elm) - self.setp(obj, "App::PropertyString", "planIcon", "The object's icon for the plan view", elm) + self.setp( + obj, "App::PropertyString", "planIcon", "The object's icon for the plan view", elm + ) self.setp(obj, "App::PropertyString", "modelRotation", "The object's model rotation", elm) self.setp(obj, "App::PropertyString", "modelCenteredAtOrigin", "The object's center", elm) - self.setp(obj, "App::PropertyBool", "backFaceShown", "Whether the object's back face is shown", elm) + self.setp( + obj, + "App::PropertyBool", + "backFaceShown", + "Whether the object's back face is shown", + elm, + ) self.setp(obj, "App::PropertyString", "modelFlags", "The object's flags", elm) self.setp(obj, "App::PropertyFloat", "modelSize", "The object's size", elm) - self.setp(obj, "App::PropertyBool", "doorOrWindow", "Whether the object is a door or Window", bool(elm.get('doorOrWindow', False))) + self.setp( + obj, + "App::PropertyBool", + "doorOrWindow", + "Whether the object is a door or Window", + bool(elm.get("doorOrWindow", False)), + ) self.setp(obj, "App::PropertyBool", "resizable", "Whether the object is resizable", elm) self.setp(obj, "App::PropertyBool", "deformable", "Whether the object is deformable", elm) self.setp(obj, "App::PropertyBool", "texturable", "Whether the object is texturable", elm) self.setp(obj, "App::PropertyString", "staircaseCutOutShape", "", elm) - self.setp(obj, "App::PropertyPercent", "shininess", "The object's shininess", percent_sh2fc(elm.get('shininess', 0))) - self.setp(obj, "App::PropertyFloat", "valueAddedTaxPercentage", "The object's VAT percentage", elm) - self.setp(obj, "App::PropertyString", "currency", "The object's price currency", str(elm.get('currency', 'EUR'))) + self.setp( + obj, + "App::PropertyPercent", + "shininess", + "The object's shininess", + percent_sh2fc(elm.get("shininess", 0)), + ) + self.setp( + obj, "App::PropertyFloat", "valueAddedTaxPercentage", "The object's VAT percentage", elm + ) + self.setp( + obj, + "App::PropertyString", + "currency", + "The object's price currency", + str(elm.get("currency", "EUR")), + ) def set_piece_of_furniture_horizontal_rotation_properties(self, obj, elm): - self.setp(obj, "App::PropertyBool", "horizontallyRotatable", "Whether the object horizontally rotatable", elm) + self.setp( + obj, + "App::PropertyBool", + "horizontallyRotatable", + "Whether the object horizontally rotatable", + elm, + ) self.setp(obj, "App::PropertyFloat", "pitch", "The object's pitch", elm) self.setp(obj, "App::PropertyFloat", "roll", "The object's roll", elm) - self.setp(obj, "App::PropertyFloat", "widthInPlan", "The object's width in the plan view", elm) - self.setp(obj, "App::PropertyFloat", "depthInPlan", "The object's depth in the plan view", elm) - self.setp(obj, "App::PropertyFloat", "heightInPlan", "The object's height in the plan view", elm) + self.setp( + obj, "App::PropertyFloat", "widthInPlan", "The object's width in the plan view", elm + ) + self.setp( + obj, "App::PropertyFloat", "depthInPlan", "The object's depth in the plan view", elm + ) + self.setp( + obj, "App::PropertyFloat", "heightInPlan", "The object's height in the plan view", elm + ) def _get_mesh(self, elm): - model = elm.get('model') + model = elm.get("model") if model not in self.importer.zip.namelist(): - raise ValueError(f"Invalid SweetHome3D file: missing model {model} for furniture {elm.get('id')}") + raise ValueError( + f"Invalid SweetHome3D file: missing model {model} for furniture {elm.get('id')}" + ) model_path_obj = None try: # Since mesh.read(model_data) does not work on BytesIO extract it first @@ -2172,7 +2567,7 @@ class BaseFurnitureHandler(BaseHandler): if os.path.isdir(os.path.join(tmp_dir, model)): tmp_dir = os.path.join(tmp_dir, str(uuid.uuid4())) model_path = self.importer.zip.extract(member=model, path=tmp_dir) - model_path_obj = model_path+".obj" + model_path_obj = model_path + ".obj" os.rename(model_path, model_path_obj) mesh = Mesh.Mesh() mesh.read(model_path_obj) @@ -2195,14 +2590,13 @@ class DoorOrWindowHandler(BaseFurnitureHandler): elm (Element): the xml element """ door_id = f"{elm.get('id', elm.get('name'))}-{i}" - level_id = elm.get('level', None) + level_id = elm.get("level", None) floor = self.get_floor(level_id) assert floor != None, f"Missing floor '{level_id}' for '{door_id}'…" - feature = None if self.importer.preferences["MERGE"]: - feature = self.get_fc_object(door_id, 'doorOrWindow') + feature = self.get_fc_object(door_id, "doorOrWindow") if not feature: feature = self._create_door(floor, elm) @@ -2216,13 +2610,17 @@ class DoorOrWindowHandler(BaseFurnitureHandler): self.setp(feature, "App::PropertyString", "id", "The furniture's id", door_id) def _set_properties(self, obj, elm): - self.setp(obj, "App::PropertyString", "shType", "The element type", 'doorOrWindow') - self.setp(obj, "App::PropertyFloat", "wallThickness", "", dim_sh2fc(elm.get('wallThickness', 1))) - self.setp(obj, "App::PropertyFloat", "wallDistance", "", dim_sh2fc(elm.get('wallDistance', 0))) - self.setp(obj, "App::PropertyFloat", "wallWidth", "", dim_sh2fc(elm.get('wallWidth', 1))) - self.setp(obj, "App::PropertyFloat", "wallLeft", "", dim_sh2fc(elm.get('wallLeft', 0))) - self.setp(obj, "App::PropertyFloat", "wallHeight", "", dim_sh2fc(elm.get('wallHeight', 1))) - self.setp(obj, "App::PropertyFloat", "wallTop", "", dim_sh2fc(elm.get('wallTop', 0))) + self.setp(obj, "App::PropertyString", "shType", "The element type", "doorOrWindow") + self.setp( + obj, "App::PropertyFloat", "wallThickness", "", dim_sh2fc(elm.get("wallThickness", 1)) + ) + self.setp( + obj, "App::PropertyFloat", "wallDistance", "", dim_sh2fc(elm.get("wallDistance", 0)) + ) + self.setp(obj, "App::PropertyFloat", "wallWidth", "", dim_sh2fc(elm.get("wallWidth", 1))) + self.setp(obj, "App::PropertyFloat", "wallLeft", "", dim_sh2fc(elm.get("wallLeft", 0))) + self.setp(obj, "App::PropertyFloat", "wallHeight", "", dim_sh2fc(elm.get("wallHeight", 1))) + self.setp(obj, "App::PropertyFloat", "wallTop", "", dim_sh2fc(elm.get("wallTop", 0))) self.setp(obj, "App::PropertyBool", "wallCutOutOnBothSides", "", elm) self.setp(obj, "App::PropertyBool", "widthDepthDeformable", "", elm) self.setp(obj, "App::PropertyString", "cutOutShape", "", elm) @@ -2236,26 +2634,26 @@ class DoorOrWindowHandler(BaseFurnitureHandler): # contains the windows and it references the corner of said face. # Therefore translating the n arbitrary volume in SH3D into a face in # FreeCAD is rather confusing and tricky to get right. - x_center = float(elm.get('x')) - y_center = float(elm.get('y')) - z_center = float(elm.get('elevation', 0)) + x_center = float(elm.get("x")) + y_center = float(elm.get("y")) + z_center = float(elm.get("elevation", 0)) label_prefix = f"dow-{elm.get('id')}" # The absolute coordinate of the center of the doorOrWndow's lower face dow_abs_center = coord_sh2fc(App.Vector(x_center, y_center, z_center)) dow_abs_center.z += floor.Placement.Base.z - width = dim_sh2fc(elm.get('width')) - depth = dim_sh2fc(elm.get('depth')) - height = dim_sh2fc(elm.get('height')) - angle = norm_deg_ang(ang_sh2fc(elm.get('angle', 0))) + width = dim_sh2fc(elm.get("width")) + depth = dim_sh2fc(elm.get("depth")) + height = dim_sh2fc(elm.get("height")) + angle = norm_deg_ang(ang_sh2fc(elm.get("angle", 0))) # Note that we only move on the XY plane since we assume that # only the right and left face will be used for supporting the # doorOrWndow. It might not be correct for roof windows and floor # windows... # The absolute coordinate of the corner of the doorOrWindow - dow_abs_corner = dow_abs_center.add(App.Vector(-width/2, -depth/2, 0)) + dow_abs_corner = dow_abs_center.add(App.Vector(-width / 2, -depth / 2, 0)) # Create a solid representing the BoundingBox of the windows # to find out which walls contains the window... @@ -2282,14 +2680,17 @@ class DoorOrWindowHandler(BaseFurnitureHandler): else: if len(extra_walls) == 0: _err(f"No hosting wall for doorOrWindow#{elm.get('id')}. Bailing out!") - if debug_geometry: self._debug_shape(dow_bounding_box, f"{label_prefix}-no-hosting-wall", RED) + if debug_geometry: + self._debug_shape(dow_bounding_box, f"{label_prefix}-no-hosting-wall", RED) return None # Hum probably open doorOrWndow? is_opened = True main_wall = extra_walls.pop(0) wall_width = main_wall.Width.Value if len(extra_walls) > 0: - _wrn(f"No main hosting wall for doorOrWindow#{elm.get('id')}. Defaulting to first hosting wall#{main_wall.Label} (w/ width {wall_width})…") + _wrn( + f"No main hosting wall for doorOrWindow#{elm.get('id')}. Defaulting to first hosting wall#{main_wall.Label} (w/ width {wall_width})…" + ) # Get the left and right face for the main_wall (_, wall_lface, _, wall_rface) = self.get_faces(main_wall) @@ -2304,11 +2705,16 @@ class DoorOrWindowHandler(BaseFurnitureHandler): # Determine the bounding box face bb_face, bb_face_normal = self._get_bb_face(dow_bounding_box, angle, label_prefix) if not bb_face: - _err(f"Weird: None of BoundingBox's faces for doorOrWindow#{elm.get('id')} has the expected angle ({angle}º). Cannot create the window.") - if debug_geometry: self._debug_shape(dow_bounding_box, f"{label_prefix}-missing-bb-face#{main_wall.Label}", RED) + _err( + f"Weird: None of BoundingBox's faces for doorOrWindow#{elm.get('id')} has the expected angle ({angle}º). Cannot create the window." + ) + if debug_geometry: + self._debug_shape( + dow_bounding_box, f"{label_prefix}-missing-bb-face#{main_wall.Label}", RED + ) return None elif debug_geometry: - self._debug_shape(bb_face, f"{label_prefix}-bb-face", MAGENTA) + self._debug_shape(bb_face, f"{label_prefix}-bb-face", MAGENTA) # Determine the wall's face with the same orientation. Note that # if the window is ever so slightly twisted with respect to the wall @@ -2318,60 +2724,74 @@ class DoorOrWindowHandler(BaseFurnitureHandler): # with curved walls # First get the u,v parameter of the bb_face CoG onto each of the wall faces # Then get the normal at these parameter on each of the wall faces - wall_rface_normal = wall_rface.normalAt(*(wall_rface.Surface.parameter(bb_face.CenterOfGravity))) - wall_lface_normal = wall_lface.normalAt(*(wall_lface.Surface.parameter(bb_face.CenterOfGravity))) + wall_rface_normal = wall_rface.normalAt( + *(wall_rface.Surface.parameter(bb_face.CenterOfGravity)) + ) + wall_lface_normal = wall_lface.normalAt( + *(wall_lface.Surface.parameter(bb_face.CenterOfGravity)) + ) wall_face = wall_rface is_on_right = True if not self._same_dir(bb_face_normal, wall_rface_normal, 1): is_on_right = False wall_face = wall_lface if not self._same_dir(bb_face_normal, wall_lface_normal, 1): - _err(f"Weird: the extracted bb_normal {self._pv(bb_face_normal, True)} does not match neither the right face normal ({self._pv(wall_rface_normal, True)}) nor the left face normal ({self._pv(wall_lface_normal, True)}) of the wall {main_wall.Label}… The doorOrWindow might be slightly skewed. Defaulting to left face.") + _err( + f"Weird: the extracted bb_normal {self._pv(bb_face_normal, True)} does not match neither the right face normal ({self._pv(wall_rface_normal, True)}) nor the left face normal ({self._pv(wall_lface_normal, True)}) of the wall {main_wall.Label}… The doorOrWindow might be slightly skewed. Defaulting to left face." + ) # Project the bounding_box face onto the wall projected_face = wall_face.makeParallelProjection(bb_face.OuterWire, bb_face_normal) if debug_geometry: - self._debug_shape(wall_face, f"{label_prefix}-bb-projected-onto#{main_wall.Label}", MAGENTA) - self._debug_shape(projected_face, f"{label_prefix}-bb-projection#{main_wall.Label}", RED) + self._debug_shape( + wall_face, f"{label_prefix}-bb-projected-onto#{main_wall.Label}", MAGENTA + ) + self._debug_shape( + projected_face, f"{label_prefix}-bb-projection#{main_wall.Label}", RED + ) # Determine the base vertex that I later use for the doorOrWindow # placement base_vertex = self._get_base_vertex(main_wall, is_on_right, projected_face) pl = App.Placement( - base_vertex, # move - App.Rotation(angle, 0, 90), # Yaw, pitch, roll - ORIGIN # rotation@point + base_vertex, # move + App.Rotation(angle, 0, 90), # Yaw, pitch, roll + ORIGIN, # rotation@point ) - if debug_geometry: self._debug_point(pl.Base, f"{label_prefix}-pl-base", MAGENTA) + if debug_geometry: + self._debug_point(pl.Base, f"{label_prefix}-pl-base", MAGENTA) # Then prepare the windows characteristics # NOTE: the windows are not imported as meshes, but we use a simple # correspondence between a catalog ID and a specific window preset from # the parts library. Only using Opening / Fixed / Simple Door - catalog_id = elm.get('catalogId') + catalog_id = elm.get("catalogId") (windowtype, ifc_type) = DOOR_MODELS.get(catalog_id, (None, None)) if not windowtype: - _wrn(f"Unknown catalogId {catalog_id} for element {elm.get('id')}. Defaulting to 'Simple Door'") - (windowtype, ifc_type) = ('Simple door', 'Door') + _wrn( + f"Unknown catalogId {catalog_id} for element {elm.get('id')}. Defaulting to 'Simple Door'" + ) + (windowtype, ifc_type) = ("Simple door", "Door") # See the https://wiki.freecad.org/Arch_Window for details about these values # NOTE: These are simple heuristic to get reasonable windows - h1 = min(50, height*.025) # frame is 2.5% of whole height... - h2 = h1 # panel's frame is the same as frame + h1 = min(50, height * 0.025) # frame is 2.5% of whole height... + h2 = h1 # panel's frame is the same as frame h3 = 0 - w1 = wall_width # frame is 100% of wall width... - w2 = min(20.0, wall_width*.2) # panel is 20% of wall width - o1 = (wall_width-w1)/2 # frame is centered - o2 = (wall_width-w2)/2 # panel is centered + w1 = wall_width # frame is 100% of wall width... + w2 = min(20.0, wall_width * 0.2) # panel is 20% of wall width + o1 = (wall_width - w1) / 2 # frame is centered + o2 = (wall_width - w2) / 2 # panel is centered window = Arch.makeWindowPreset(windowtype, width, height, h1, h2, h3, w1, w2, o1, o2, pl) - window.Label = elm.get('name') + window.Label = elm.get("name") window.IfcType = ifc_type - if is_opened: window.Opening = 30 + if is_opened: + window.Opening = 30 # Adjust symbol plan, Sweet Home has the opening in the opposite side by default window.ViewObject.Proxy.invertOpening() - mirrored = bool(elm.get('modelMirrored', False)) + mirrored = bool(elm.get("modelMirrored", False)) if mirrored: window.ViewObject.Proxy.invertHinge() @@ -2413,20 +2833,26 @@ class DoorOrWindowHandler(BaseFurnitureHandler): # First find out which floor the window might be have an impact on. solid_zmin = dow_bounding_box.BoundBox.ZMin solid_zmax = dow_bounding_box.BoundBox.ZMax - if solid_zmin < floor.Placement.Base.z or solid_zmax > (floor.Placement.Base.z + floor.Height.Value): + if solid_zmin < floor.Placement.Base.z or solid_zmax > ( + floor.Placement.Base.z + floor.Height.Value + ): # determine the impacted floors for other_floor in self.importer.get_all_floors(): if other_floor.id == floor.id: continue floor_zmin = other_floor.Placement.Base.z floor_zmax = other_floor.Placement.Base.z + other_floor.Height.Value - if (floor_zmin < solid_zmin and solid_zmin < floor_zmax) or ( - floor_zmin < solid_zmax and solid_zmax < floor_zmax) or ( - solid_zmin < floor_zmin and floor_zmax < solid_zmax): + if ( + (floor_zmin < solid_zmin and solid_zmin < floor_zmax) + or (floor_zmin < solid_zmax and solid_zmax < floor_zmax) + or (solid_zmin < floor_zmin and floor_zmax < solid_zmax) + ): # Add floor and slabs relevant_walls.extend(self.importer.get_walls(other_floor)) if other_floor.ReferenceSlabName: - relevant_walls.append(App.ActiveDocument.getObject(other_floor.ReferenceSlabName)) + relevant_walls.append( + App.ActiveDocument.getObject(other_floor.ReferenceSlabName) + ) main_wall = None host_walls = [] # Taking the CoG projection on the lower face. @@ -2462,13 +2888,15 @@ class DoorOrWindowHandler(BaseFurnitureHandler): # coordinate). # XXX: Can it be speed up by assuming that left and right are always # Face2 and Face4??? - angle = (angle - 90) % 360 # make sure positive ccw angle + angle = (angle - 90) % 360 # make sure positive ccw angle for i, face in enumerate(dow_bounding_box.Faces): - face_normal = face.normalAt(0,0) # The face is flat. can use u = v = 0 + face_normal = face.normalAt(0, 0) # The face is flat. can use u = v = 0 normal_angle = norm_deg_ang(DraftVecUtils.angle(X_NORM, face_normal)) - if debug_geometry: _msg(f"#{i}/{label_prefix} {normal_angle}º <=> {angle}º") + if debug_geometry: + _msg(f"#{i}/{label_prefix} {normal_angle}º <=> {angle}º") if normal_angle == angle: - if debug_geometry: _msg(f"Found bb#{i}/{label_prefix} (@{normal_angle}º)") + if debug_geometry: + _msg(f"Found bb#{i}/{label_prefix} (@{normal_angle}º)") return face, face_normal return None, None @@ -2497,7 +2925,9 @@ class DoorOrWindowHandler(BaseFurnitureHandler): wall_spine = self.get_wall_spine(wall) wall_ref = wall_spine.Start if is_on_right else wall_spine.End lowest_z = round(projected_face.BoundBox.ZMin) - lower_vertexes = list(filter(lambda v: round(v.Point.z) == lowest_z, projected_face.Vertexes)) + lower_vertexes = list( + filter(lambda v: round(v.Point.z) == lowest_z, projected_face.Vertexes) + ) base_vertex = min(lower_vertexes, key=lambda v: v.Point.distanceToPoint(wall_ref)) return base_vertex.Point @@ -2516,32 +2946,40 @@ class FurnitureHandler(BaseFurnitureHandler): elm (Element): the xml element """ furniture_id = self._get_furniture_id(i, elm) - level_id = elm.get('level', None) + level_id = elm.get("level", None) floor = self.get_floor(level_id) assert floor != None, f"Missing floor '{level_id}' for '{furniture_id}'…" furniture = None if self.importer.preferences["MERGE"]: - furniture = self.get_fc_object(furniture_id, 'pieceOfFurniture') + furniture = self.get_fc_object(furniture_id, "pieceOfFurniture") if not furniture: furniture = self._create_furniture(floor, elm) if not furniture: return - color = elm.get('color', self.importer.preferences["DEFAULT_FURNITURE_COLOR"]) + color = elm.get("color", self.importer.preferences["DEFAULT_FURNITURE_COLOR"]) set_color_and_transparency(furniture, color) - furniture.ViewObject.DisplayMode = 'Flat Lines' + furniture.ViewObject.DisplayMode = "Flat Lines" - self.setp(furniture, "App::PropertyString", "shType", "The element type", 'pieceOfFurniture') + self.setp( + furniture, "App::PropertyString", "shType", "The element type", "pieceOfFurniture" + ) self.set_furniture_common_properties(furniture, elm) self.set_piece_of_furniture_common_properties(furniture, elm) self.set_piece_of_furniture_horizontal_rotation_properties(furniture, elm) self.setp(furniture, "App::PropertyString", "id", "The furniture's id", furniture_id) - if 'FurnitureGroupName' not in floor.PropertiesList: + if "FurnitureGroupName" not in floor.PropertiesList: group = floor.newObject("App::DocumentObjectGroup", "Furnitures") - self.setp(floor, "App::PropertyString", "FurnitureGroupName", "The DocumentObjectGroup name for all furnitures on this floor", group.Name) + self.setp( + floor, + "App::PropertyString", + "FurnitureGroupName", + "The DocumentObjectGroup name for all furnitures on this floor", + group.Name, + ) # if self.importer.preferences["CREATE_ARCH_EQUIPMENT"]: # p = feature.Shape.BoundBox.Center @@ -2569,7 +3007,7 @@ class FurnitureHandler(BaseFurnitureHandler): # - SweetHome3D/src/com/eteks/sweethome3d/j3d/ModelManager#getNormalizedTransform() # - SweetHome3D/src/com/eteks/sweethome3d/j3d/ModelManager#getPieceOfFurnitureNormalizedModelTransformation() - name = elm.get('name', elm.get('id', "NA")) + name = elm.get("name", elm.get("id", "NA")) # The general process is as follow: # - we load the mesh and center it properly. @@ -2577,21 +3015,21 @@ class FurnitureHandler(BaseFurnitureHandler): # - we scale # - we apply the yaw # - then we workout the Placement - x = float(elm.get('x', 0.0)) - y = float(elm.get('y', 0.0)) - z = float(elm.get('elevation', 0.0)) + x = float(elm.get("x", 0.0)) + y = float(elm.get("y", 0.0)) + z = float(elm.get("elevation", 0.0)) - width = dim_sh2fc(elm.get('width', 0.0)) - depth = dim_sh2fc(elm.get('depth', 0.0)) - height = dim_sh2fc(elm.get('height', 0.0)) + width = dim_sh2fc(elm.get("width", 0.0)) + depth = dim_sh2fc(elm.get("depth", 0.0)) + height = dim_sh2fc(elm.get("height", 0.0)) - pitch = norm_rad_ang(elm.get('pitch', 0.0)) - roll = norm_rad_ang(elm.get('roll', 0.0)) - angle = ang_sh2fc(elm.get('angle', 0.0)) + pitch = norm_rad_ang(elm.get("pitch", 0.0)) + roll = norm_rad_ang(elm.get("roll", 0.0)) + angle = ang_sh2fc(elm.get("angle", 0.0)) - model_rotation = elm.get('modelRotation', None) - model_mirrored = elm.get('modelMirrored', "false") == "true" - model_centered_at_origin = elm.get('modelCenteredAtOrigin', "false") == "true" + model_rotation = elm.get("modelRotation", None) + model_mirrored = elm.get("modelMirrored", "false") == "true" + model_centered_at_origin = elm.get("modelCenteredAtOrigin", "false") == "true" mesh = self._get_mesh(elm) @@ -2601,32 +3039,37 @@ class FurnitureHandler(BaseFurnitureHandler): return None model_bb = mesh.BoundBox - if debug_geometry: self._debug_mesh(mesh, f"{name}-original", None, MAGENTA) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-original", None, MAGENTA) mesh_transform = App.Matrix() mesh_transform.move(-model_bb.Center) - if debug_geometry: self._debug_mesh(mesh, f"{name}-centered", mesh_transform, MAGENTA) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-centered", mesh_transform, MAGENTA) # The model rotation is necessary to get the scaling right if model_rotation: - rij = [ float(v) for v in model_rotation.split() ] + rij = [float(v) for v in model_rotation.split()] rotation = App.Matrix( App.Vector(rij[0], rij[3], rij[6]), App.Vector(rij[1], rij[4], rij[7]), - App.Vector(rij[2], rij[5], rij[8]) - ) + App.Vector(rij[2], rij[5], rij[8]), + ) mesh_transform = rotation.multiply(mesh_transform) - if debug_geometry: self._debug_mesh(mesh, f"{name}-rotated", mesh_transform, MAGENTA) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-rotated", mesh_transform, MAGENTA) if model_mirrored: - mesh_transform.scale(-1, 1, 1) # Mirror along X - if debug_geometry: self._debug_mesh(mesh, f"{name}-mirrored", mesh_transform) + mesh_transform.scale(-1, 1, 1) # Mirror along X + if debug_geometry: + self._debug_mesh(mesh, f"{name}-mirrored", mesh_transform) # We add an initial 90º in order for a yaw-pitch-roll-rotation free # model to appear properly in FC - mesh_transform.rotateX(math.pi/2) - if debug_geometry: self._debug_mesh(mesh, f"{name}-x90", mesh_transform) + mesh_transform.rotateX(math.pi / 2) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-x90", mesh_transform) # The scaling is calculated using the models coordinate system. # We use a simple box to calculate the scale factors for each axis. @@ -2643,10 +3086,14 @@ class FurnitureHandler(BaseFurnitureHandler): mesh_transform.scale(x_scale, y_scale, z_scale) if debug_geometry: model_size = App.Vector(model_bb.XLength, model_bb.YLength, model_bb.ZLength) - normalized_size = App.Vector(normilized_bb.XLength, normilized_bb.YLength, normilized_bb.ZLength) + normalized_size = App.Vector( + normilized_bb.XLength, normilized_bb.YLength, normilized_bb.ZLength + ) final_size = App.Vector(width, depth, height) factors = App.Vector(x_scale, y_scale, z_scale) - _msg(f"{name}-size_model={self._pv(model_size, True, 1)} -> {self._pv(normalized_size, True, 1)} (x{self._pv(factors, True, 1)}) -> {self._pv(final_size, True, 1)}") + _msg( + f"{name}-size_model={self._pv(model_size, True, 1)} -> {self._pv(normalized_size, True, 1)} (x{self._pv(factors, True, 1)}) -> {self._pv(final_size, True, 1)}" + ) self._debug_mesh(mesh, f"{name}-scaled", mesh_transform, MAGENTA) # At that point the mesh has the proper scale. We determine the placement. @@ -2655,27 +3102,39 @@ class FurnitureHandler(BaseFurnitureHandler): if pitch != 0: r_pitch = App.Rotation(X_NORM, Radian=-pitch) mesh_transform = r_pitch.toMatrix().multiply(mesh_transform) - if debug_geometry: self._debug_mesh(mesh, f"{name}-pitch", mesh_transform) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-pitch", mesh_transform) elif roll != 0: r_roll = App.Rotation(Y_NORM, Radian=roll) mesh_transform = r_roll.toMatrix().multiply(mesh_transform) - if debug_geometry: self._debug_mesh(mesh, f"{name}-roll", mesh_transform) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-roll", mesh_transform) if angle != 0: r_yaw = App.Rotation(Z_NORM, Radian=angle) mesh_transform = r_yaw.toMatrix().multiply(mesh_transform) - if debug_geometry: self._debug_mesh(mesh, f"{name}-yaw", mesh_transform) + if debug_geometry: + self._debug_mesh(mesh, f"{name}-yaw", mesh_transform) mesh.transform(mesh_transform) # SH(x,y,z) refer to the projection of the CenterOfGravity on the # bottom face of the model bounding box translation = coord_sh2fc(App.Vector(x, y, z)) - if debug_geometry: self._debug_mesh(mesh, f"{name}-xyz", color=MAGENTA, placement=App.Placement(translation, NO_ROT)) + if debug_geometry: + self._debug_mesh( + mesh, f"{name}-xyz", color=MAGENTA, placement=App.Placement(translation, NO_ROT) + ) # Note that the SH coordinates represent the CenterOfGravity of the # lower face of the scaled model bounding box. translation.z += abs(mesh.BoundBox.ZMin) - if debug_geometry: self._debug_mesh(mesh, f"{name}-+zmin", color=MAGENTA_LIGHT, placement=App.Placement(translation, NO_ROT)) + if debug_geometry: + self._debug_mesh( + mesh, + f"{name}-+zmin", + color=MAGENTA_LIGHT, + placement=App.Placement(translation, NO_ROT), + ) # Finally we add the placement of the floor itself. # XXX: strange that is not simply added when we add the object to the floor @@ -2696,7 +3155,7 @@ class FurnitureHandler(BaseFurnitureHandler): furniture.Mesh = mesh furniture.Placement = placement - furniture.Label = elm.get('name') + furniture.Label = elm.get("name") return furniture def _get_furniture_id(self, i, elm): @@ -2717,42 +3176,58 @@ class LightHandler(FurnitureHandler): elm (Element): the xml element """ light_id = super()._get_furniture_id(i, elm) - level_id = elm.get('level', None) + level_id = elm.get("level", None) floor = self.get_floor(level_id) assert floor != None, f"Missing floor '{level_id}' for '{light_id}'…" if self.importer.preferences["IMPORT_FURNITURES"]: super().process(parent, i, elm) - light_apppliance = self.get_fc_object(light_id, 'pieceOfFurniture') + light_apppliance = self.get_fc_object(light_id, "pieceOfFurniture") assert light_apppliance != None, f"Missing furniture {light_id}…" - self.setp(light_apppliance, "App::PropertyFloat", "power", "The power of the light. In percent???", float(elm.get('power', 0.5))) + self.setp( + light_apppliance, + "App::PropertyFloat", + "power", + "The power of the light. In percent???", + float(elm.get("power", 0.5)), + ) if self.importer.preferences["IMPORT_LIGHTS"]: # Import the lightSource sub-elments - for j, sub_elm in enumerate(elm.findall('lightSource')): + for j, sub_elm in enumerate(elm.findall("lightSource")): light_source = None light_source_id = f"{light_id}-{j}" if self.importer.preferences["MERGE"]: - light_source = self.get_fc_object(light_source_id, 'lightSource') + light_source = self.get_fc_object(light_source_id, "lightSource") if not light_source: _, light_source, _ = PointLight.create() - x = float(sub_elm.get('x')) - y = float(sub_elm.get('y')) - z = float(sub_elm.get('z')) - diameter = float(sub_elm.get('diameter')) - color = sub_elm.get('color') + x = float(sub_elm.get("x")) + y = float(sub_elm.get("y")) + z = float(sub_elm.get("z")) + diameter = float(sub_elm.get("diameter")) + color = sub_elm.get("color") - light_source.Label = elm.get('name') + light_source.Label = elm.get("name") light_source.Placement.Base = coord_sh2fc(App.Vector(x, y, z)) light_source.Radius = dim_sh2fc(diameter / 2) light_source.Color = hex2rgb(color) - self.setp(light_source, "App::PropertyString", "shType", "The element type", 'lightSource') - self.setp(light_source, "App::PropertyString", "id", "The elment's id", light_source_id) + self.setp( + light_source, "App::PropertyString", "shType", "The element type", "lightSource" + ) + self.setp( + light_source, "App::PropertyString", "id", "The elment's id", light_source_id + ) if self.importer.preferences["IMPORT_FURNITURES"]: - self.setp(light_source, "App::PropertyLink", "lightAppliance", "The light apppliance", light_apppliance) + self.setp( + light_source, + "App::PropertyLink", + "lightAppliance", + "The light apppliance", + light_apppliance, + ) floor.getObject(floor.LightGroupName).addObject(light_source) @@ -2773,15 +3248,19 @@ class CameraHandler(BaseHandler): Returns: object: the newly created object """ - x = float(elm.get('x')) - y = float(elm.get('y')) - z = float(elm.get('z')) - yaw = float(elm.get('yaw')) - pitch = float(elm.get('pitch')) + x = float(elm.get("x")) + y = float(elm.get("y")) + z = float(elm.get("z")) + yaw = float(elm.get("yaw")) + pitch = float(elm.get("pitch")) - attribute = elm.get('attribute') + attribute = elm.get("attribute") if attribute != "storedCamera": - _log(translate("BIM", f"Type of <{elm.tag}> #{i} is not supported: '{attribute}'. Skipping!")) + _log( + translate( + "BIM", f"Type of <{elm.tag}> #{i} is not supported: '{attribute}'. Skipping!" + ) + ) return camera_id = f"{elm.get('id', attribute)}-{i}" @@ -2792,27 +3271,48 @@ class CameraHandler(BaseHandler): _, camera, _ = Camera.create() # ¿How to convert fov to FocalLength? - fieldOfView = float(elm.get('fieldOfView')) + fieldOfView = float(elm.get("fieldOfView")) fieldOfView = math.degrees(fieldOfView) - camera.Label = elm.get('name', attribute) + camera.Label = elm.get("name", attribute) camera.Placement.Base = coord_sh2fc(App.Vector(x, y, z)) # NOTE: the coordinate system is screen like, thus roll & picth are inverted ZY'X'' camera.Placement.Rotation.setYawPitchRoll( - math.degrees(math.pi-yaw), 0, math.degrees(math.pi/2-pitch)) + math.degrees(math.pi - yaw), 0, math.degrees(math.pi / 2 - pitch) + ) camera.Projection = "Perspective" camera.AspectRatio = 1.33333333 # /home/environment/@photoAspectRatio - self.setp(camera, "App::PropertyString", "shType", "The element type", 'camera') + self.setp(camera, "App::PropertyString", "shType", "The element type", "camera") self.setp(camera, "App::PropertyString", "id", "The object ID", camera_id) self._set_properties(camera, elm) App.ActiveDocument.Cameras.addObject(camera) def _set_properties(self, obj, elm): - self.setp(obj, "App::PropertyEnumeration", "attribute", "The type of camera", elm.get('attribute'), valid_values=["topCamera", "observerCamera", "storedCamera", "cameraPath"]) - self.setp(obj, "App::PropertyBool", "fixedSize", "Whether the object is fixed size", bool(elm.get('fixedSize', False))) - self.setp(obj, "App::PropertyEnumeration", "lens", "The object's lens (PINHOLE | NORMAL | FISHEYE | SPHERICAL)", str(elm.get('lens', "PINHOLE")), valid_values=["PINHOLE", "NORMAL", "FISHEYE", "SPHERICAL"]) + self.setp( + obj, + "App::PropertyEnumeration", + "attribute", + "The type of camera", + elm.get("attribute"), + valid_values=["topCamera", "observerCamera", "storedCamera", "cameraPath"], + ) + self.setp( + obj, + "App::PropertyBool", + "fixedSize", + "Whether the object is fixed size", + bool(elm.get("fixedSize", False)), + ) + self.setp( + obj, + "App::PropertyEnumeration", + "lens", + "The object's lens (PINHOLE | NORMAL | FISHEYE | SPHERICAL)", + str(elm.get("lens", "PINHOLE")), + valid_values=["PINHOLE", "NORMAL", "FISHEYE", "SPHERICAL"], + ) self.setp(obj, "App::PropertyFloat", "yaw", "The object's yaw", elm) self.setp(obj, "App::PropertyFloat", "pitch", "The object's pitch", elm) self.setp(obj, "App::PropertyFloat", "time", "Unknown", elm) @@ -2829,7 +3329,7 @@ def dim_sh2fc(dimension): Returns: float: the FreeCAD dimension """ - return float(dimension)*FACTOR + return float(dimension) * FACTOR def dim_fc2sh(dimension): @@ -2841,7 +3341,7 @@ def dim_fc2sh(dimension): Returns: float: the SweetHome dimension """ - return float(dimension)/FACTOR + return float(dimension) / FACTOR def coord_sh2fc(vector): @@ -2853,10 +3353,10 @@ def coord_sh2fc(vector): Returns: FreeCAD.Vector: the FreeCAD coordinate """ - return App.Vector(vector.x*FACTOR, -vector.y*FACTOR, vector.z*FACTOR) + return App.Vector(vector.x * FACTOR, -vector.y * FACTOR, vector.z * FACTOR) -def ang_sh2fc(angle:float): +def ang_sh2fc(angle: float): """Convert SweetHome angle (º) to FreeCAD angle (º) SweetHome angles are clockwise positive while FreeCAD are anti-clockwise @@ -2872,7 +3372,7 @@ def ang_sh2fc(angle:float): return norm_rad_ang(-float(angle)) -def norm_deg_ang(angle:float): +def norm_deg_ang(angle: float): """Normalize a radian angle into a degree angle.. Args: @@ -2884,7 +3384,7 @@ def norm_deg_ang(angle:float): return round(math.degrees(float(angle)) % 360) -def norm_rad_ang(angle:float): +def norm_rad_ang(angle: float): """Normalize a radian angle into a radian angle.. Args: @@ -2907,8 +3407,8 @@ def set_color_and_transparency(obj, color): mat.DiffuseColor = rgb_color mat.AmbientColor = rgb_color mat.SpecularColor = rgb_color - mat.EmissiveColor = (0.0,0.0,0.0,1.0) - obj.ViewObject.ShapeAppearance = (mat) + mat.EmissiveColor = (0.0, 0.0, 0.0, 1.0) + obj.ViewObject.ShapeAppearance = mat return if hasattr(view_object, "ShapeColor"): view_object.ShapeColor = hex2rgb(color) @@ -2919,7 +3419,7 @@ def set_color_and_transparency(obj, color): def color_fc2sh(hexcode): # 0xRRGGBBAA => AARRGGBB hex_str = hex(int(hexcode))[2:] - return ''.join([hex_str[6:], hex_str[0:6]]) + return "".join([hex_str[6:], hex_str[0:6]]) def hex2rgb(hexcode): @@ -2927,18 +3427,24 @@ def hex2rgb(hexcode): if isinstance(hexcode, list) or isinstance(hexcode, tuple): return hexcode if not isinstance(hexcode, str): - assert False, "Invalid type when calling hex2rgb(), was expecting a list, tuple or string. Got "+str(hexcode) + assert False, ( + "Invalid type when calling hex2rgb(), was expecting a list, tuple or string. Got " + + str(hexcode) + ) offset = 0 if len(hexcode) == 6 else 2 return ( - int(hexcode[offset:offset+2], 16), # Red - int(hexcode[offset+2:offset+4], 16), # Green - int(hexcode[offset+4:offset+6], 16) # Blue - ) + int(hexcode[offset : offset + 2], 16), # Red + int(hexcode[offset + 2 : offset + 4], 16), # Green + int(hexcode[offset + 4 : offset + 6], 16), # Blue + ) def _hex2transparency(hexcode): if not isinstance(hexcode, str): - assert False, "Invalid type when calling _hex2transparency(), was expecting a list, tuple or string. Got "+str(hexcode) + assert False, ( + "Invalid type when calling _hex2transparency(), was expecting a list, tuple or string. Got " + + str(hexcode) + ) return 100 - int(int(hexcode[0:2], 16) * 100 / 255) @@ -2946,11 +3452,11 @@ def _color_section(section): view = section.ViewObject line_colors = [view.LineColor] * len(section.Shape.Edges) for i in range(0, len(line_colors)): - line_colors[i] = hex2rgb(DEBUG_EDGES_COLORS[i%len(DEBUG_EDGES_COLORS)]) + line_colors[i] = hex2rgb(DEBUG_EDGES_COLORS[i % len(DEBUG_EDGES_COLORS)]) view.LineColorArray = line_colors point_colors = [view.PointColor] * len(section.Shape.Vertexes) for i in range(0, len(point_colors)): - point_colors[i] = hex2rgb(DEBUG_POINT_COLORS[i%len(DEBUG_POINT_COLORS)]) + point_colors[i] = hex2rgb(DEBUG_POINT_COLORS[i % len(DEBUG_POINT_COLORS)]) view.PointColorArray = point_colors view.PointSize = 5 @@ -2967,13 +3473,14 @@ def set_shininess(obj, shininess): def percent_sh2fc(percent): # percent goes from 0 -> 1 in SH3d and 0 -> 100 in FC - return int(float(percent)*100) + return int(float(percent) * 100) def cross_product(o, a, b): """Computes the cross product of vectors OA and OB.""" return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) + def convex_hull(points, tol=1e-6): """Return the convex hull of a series of Point @@ -2987,7 +3494,9 @@ def convex_hull(points, tol=1e-6): """ default_z = points[0].z point_coords = np.array([[p.x, p.y] for p in points], dtype=np.float64) - point_coords = point_coords[np.lexsort((point_coords[:, 1], point_coords[:, 0]))] # Sort by x, then y + point_coords = point_coords[ + np.lexsort((point_coords[:, 1], point_coords[:, 0])) + ] # Sort by x, then y def build_half_hull(sorted_points): hull = [] @@ -3004,6 +3513,7 @@ def convex_hull(points, tol=1e-6): new_points = [App.Vector(p[0], p[1], default_z) for p in np.array(lower[:-1] + upper[:-1])] return new_points + # def _convex_hull(points): # """Return the convex hull of a series of Point # diff --git a/src/Mod/BIM/importers/importSHP.py b/src/Mod/BIM/importers/importSHP.py index 0bc7f7ec8f..6224170a57 100644 --- a/src/Mod/BIM/importers/importSHP.py +++ b/src/Mod/BIM/importers/importSHP.py @@ -26,22 +26,21 @@ import os from builtins import open as pyopen import FreeCAD + translate = FreeCAD.Qt.translate def open(filename): - """opens a SHP/SHX/DBF file in a new FreeCAD document""" docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) doc.Label = docname - doc = insert(filename,doc.Name) + doc = insert(filename, doc.Name) return doc -def insert(filename,docname,record=None): - +def insert(filename, docname, record=None): """imports a SHP/SHX/DBF file in an existing FreeCAD document. the record attribute is an optional string indicating the shapefile field to use to give elevations to the different shapes. If not used, @@ -65,12 +64,15 @@ def insert(filename,docname,record=None): if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui - reply = QtGui.QInputDialog.getItem(FreeCADGui.getMainWindow(), - translate("Arch","Shapes elevation"), - translate("Arch","Choose which field provides shapes elevations:"), - fields) + + reply = QtGui.QInputDialog.getItem( + FreeCADGui.getMainWindow(), + translate("Arch", "Shapes elevation"), + translate("Arch", "Choose which field provides shapes elevations:"), + fields, + ) if reply[1] and reply[0] != "None": - record = reply[0] + record = reply[0] # build shapes shapes = [] @@ -79,15 +81,15 @@ def insert(filename,docname,record=None): pts = [] for p in shaperec.shape.points: if len(p) > 2: - pts.append(FreeCAD.Vector(p[0],p[1],p[2])) + pts.append(FreeCAD.Vector(p[0], p[1], p[2])) else: - pts.append(FreeCAD.Vector(p[0],p[1],0)) - if shp.shapeTypeName in ["POLYGON","POLYGONZ"]: + pts.append(FreeCAD.Vector(p[0], p[1], 0)) + if shp.shapeTypeName in ["POLYGON", "POLYGONZ"]: # faces pts.append(pts[0]) shape = Part.makePolygon(pts) shape = Part.Face(shape) - elif shp.shapeTypeName in ["POINT","POINTZ"]: + elif shp.shapeTypeName in ["POINT", "POINTZ"]: # points verts = [Part.Vertex(p) for p in pts] if verts: @@ -98,62 +100,87 @@ def insert(filename,docname,record=None): if record: elev = shaperec.record[record] if elev: - shape.translate(FreeCAD.Vector(0,0,elev)) + shape.translate(FreeCAD.Vector(0, 0, elev)) if shape: shapes.append(shape) if shapes: result = Part.makeCompound(shapes) - obj = FreeCAD.ActiveDocument.addObject("Part::Feature","shapefile") + obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "shapefile") obj.Shape = result obj.Label = os.path.splitext(os.path.basename(filename))[0] FreeCAD.ActiveDocument.recompute() else: - FreeCAD.Console.PrintWarning(translate("Arch","No shape found in this file")+"\n") + FreeCAD.Console.PrintWarning(translate("Arch", "No shape found in this file") + "\n") + def getFields(filename): - """returns the fields found in the given file""" if not checkShapeFileLibrary(): return import shapefile + shp = shapefile.Reader(filename) return [field[0] for field in shp.fields] -def checkShapeFileLibrary(): +def checkShapeFileLibrary(): """Looks for and/or installs the ShapeFile library""" try: import shapefile except Exception: # pinning to pyshp upstream v2.4.1 since in 3.0.0 the path to shapefile.py changes - url = "https://raw.githubusercontent.com/GeospatialPython/pyshp/refs/tags/2.4.1/shapefile.py" + url = ( + "https://raw.githubusercontent.com/GeospatialPython/pyshp/refs/tags/2.4.1/shapefile.py" + ) if FreeCAD.GuiUp: import urllib.request import FreeCADGui from PySide import QtGui - reply = QtGui.QMessageBox.question(FreeCADGui.getMainWindow(), - translate("Arch","Shapefile module not found"), - translate("Arch","The shapefile Python library was not found on your system. Would you like to download it now from %1? It will be placed in your macros folder.").replace("%1","https://github.com/GeospatialPython/pyshp"), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, - QtGui.QMessageBox.No) + + reply = QtGui.QMessageBox.question( + FreeCADGui.getMainWindow(), + translate("Arch", "Shapefile module not found"), + translate( + "Arch", + "The shapefile Python library was not found on your system. Would you like to download it now from %1? It will be placed in your macros folder.", + ).replace( + "%1", + 'https://github.com/GeospatialPython/pyshp', + ), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.No, + ) if reply == QtGui.QMessageBox.Yes: u = urllib.request.urlopen(url) if not u: - FreeCAD.Console.PrintError(translate("Arch","Error: Unable to download from %1").replace("%1",url)+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Error: Unable to download from %1").replace("%1", url) + + "\n" + ) return False b = u.read() - fp = os.path.join(FreeCAD.getUserMacroDir(True),"shapefile.py") - f = pyopen(fp,"wb") + fp = os.path.join(FreeCAD.getUserMacroDir(True), "shapefile.py") + f = pyopen(fp, "wb") f.write(b) f.close() else: - FreeCAD.Console.PrintError(translate("Arch","Shapefile module not downloaded. Aborting.")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Shapefile module not downloaded. Aborting.") + "\n" + ) return False else: - FreeCAD.Console.PrintError(translate("Arch","Shapefile module not found. Aborting.")+"\n") - FreeCAD.Console.PrintMessage(translate("Arch","The shapefile library can be downloaded from the following URL and installed in your macros folder:")+"\n") + FreeCAD.Console.PrintError( + translate("Arch", "Shapefile module not found. Aborting.") + "\n" + ) + FreeCAD.Console.PrintMessage( + translate( + "Arch", + "The shapefile library can be downloaded from the following URL and installed in your macros folder:", + ) + + "\n" + ) FreeCAD.Console.PrintMessage(url) return False return True diff --git a/src/Mod/BIM/importers/importWebGL.py b/src/Mod/BIM/importers/importWebGL.py index b02e478cd5..0e213dbce3 100644 --- a/src/Mod/BIM/importers/importWebGL.py +++ b/src/Mod/BIM/importers/importWebGL.py @@ -92,8 +92,7 @@ def getHTMLTemplate(): """Helper function to safely read a template file.""" if not os.path.isfile(path): FreeCAD.Console.PrintWarning( - f"{description.capitalize()} file '{path}' does not " - "exist or is not a file.\n" + f"{description.capitalize()} file '{path}' does not " "exist or is not a file.\n" ) return None try: @@ -101,35 +100,25 @@ def getHTMLTemplate(): return f.read() except Exception as e: FreeCAD.Console.PrintWarning( - f"Failed to read {description} file '{path}' " - f"due to: {str(e)}\n" + f"Failed to read {description} file '{path}' " f"due to: {str(e)}\n" ) return None - using_custom_template = params.get_param( - "useCustomWebGLExportTemplate", path="Mod/BIM" - ) + using_custom_template = params.get_param("useCustomWebGLExportTemplate", path="Mod/BIM") # Try to use custom template if enabled if using_custom_template: - custom_path = params.get_param( - "WebGLTemplateCustomPath", path="Mod/BIM" - ) - custom_content = try_read_template( - custom_path, "custom WebGL template" - ) + custom_path = params.get_param("WebGLTemplateCustomPath", path="Mod/BIM") + custom_content = try_read_template(custom_path, "custom WebGL template") if custom_content: - FreeCAD.Console.PrintMessage( - f"Using custom template file '{custom_path}'.\n" - ) + FreeCAD.Console.PrintMessage(f"Using custom template file '{custom_path}'.\n") return custom_content else: # Custom template failed - ask user or auto-fallback if not FreeCADGui: # In non-GUI mode, cancel export when custom template fails FreeCAD.Console.PrintError( - f"Export cancelled: Custom template '{custom_path}' " - "not available.\n" + f"Export cancelled: Custom template '{custom_path}' " "not available.\n" ) return None @@ -151,8 +140,7 @@ def getHTMLTemplate(): if reply != QtWidgets.QMessageBox.Yes: # User chose not to proceed - return None to indicate failure FreeCAD.Console.PrintError( - f"Export cancelled: Custom template '{custom_path}' " - "not available.\n" + f"Export cancelled: Custom template '{custom_path}' " "not available.\n" ) return None @@ -165,13 +153,9 @@ def getHTMLTemplate(): "templates", "webgl_export_template.html", ) - default_content = try_read_template( - default_template_path, "default WebGL template" - ) + default_content = try_read_template(default_template_path, "default WebGL template") if default_content: - FreeCAD.Console.PrintMessage( - f"Using default template file '{default_template_path}'.\n" - ) + FreeCAD.Console.PrintMessage(f"Using default template file '{default_template_path}'.\n") return default_content # No template available - export cannot proceed @@ -192,8 +176,7 @@ def getHTMLTemplate(): else: # In headless mode, print to console FreeCAD.Console.PrintError( - "Default WebGL export template not available at" - f"path: {default_template_path}\n" + "Default WebGL export template not available at" f"path: {default_template_path}\n" ) return None diff --git a/src/Mod/BIM/nativeifc/ifc_classification.py b/src/Mod/BIM/nativeifc/ifc_classification.py index adc9faae65..628fefb4ca 100644 --- a/src/Mod/BIM/nativeifc/ifc_classification.py +++ b/src/Mod/BIM/nativeifc/ifc_classification.py @@ -34,7 +34,7 @@ def edit_classification(obj): if not element or not ifcfile: return # TODO: remove previous reference? - #ifc_tools.api_run("classification.remove_reference", + # ifc_tools.api_run("classification.remove_reference", # ifcfile, reference=ref, products=[obj]) classifications = ifcfile.by_type("IfcClassification") classification = getattr(obj, "Classification", "") @@ -44,8 +44,9 @@ def edit_classification(obj): if cname in cnames: system = classifications[cnames.index(cname)] else: - system = ifc_tools.api_run("classification.add_classification", ifcfile, - classification=cname) + system = ifc_tools.api_run( + "classification.add_classification", ifcfile, classification=cname + ) for ref in getattr(system, "HasReferences", []): rname = ref.Name or ref.Identification if code == rname: @@ -54,24 +55,27 @@ def edit_classification(obj): if getattr(ref, "ClassificationRefForObjects", None): rel = ref.ClassificationRefForObjects[0] if not element in rel.RelatedObjects: - ifc_tools.edit_attribute(rel, "RelatedObjects", - rel.RelatedObjects + [element] + ifc_tools.edit_attribute( + rel, "RelatedObjects", rel.RelatedObjects + [element] ) else: # we have a reference, but no classForObjects # this is weird and shouldn't exist... rel = ifcfile.createIfcRelAssociatesClassification( ifc_tools.ifcopenshell.guid.new(), - history,'FreeCADClassificationRel', + history, + "FreeCADClassificationRel", None, [element], - ref + ref, ) else: - ifc_tools.api_run("classification.add_reference", ifcfile, - products = [element], - classification = system, - identification = code + ifc_tools.api_run( + "classification.add_reference", + ifcfile, + products=[element], + classification=system, + identification=code, ) else: # classification property is empty @@ -79,10 +83,11 @@ def edit_classification(obj): if rel.is_a("IfcRelAssociatesClassification"): # removing existing classification if only user if len(rel.RelatedObjects) == 1 and rel.RelatedObjects[0] == element: - ifc_tools.api_run("classification.remove_reference", - ifcfile, - reference=rel.RelatingClassification, - products=[element] + ifc_tools.api_run( + "classification.remove_reference", + ifcfile, + reference=rel.RelatingClassification, + products=[element], ) # TODO: Remove IfcClassification too? diff --git a/src/Mod/BIM/nativeifc/ifc_commands.py b/src/Mod/BIM/nativeifc/ifc_commands.py index 3fa27aa01f..14853a0c13 100644 --- a/src/Mod/BIM/nativeifc/ifc_commands.py +++ b/src/Mod/BIM/nativeifc/ifc_commands.py @@ -48,9 +48,7 @@ class IFC_Diff: """Shows a diff of the changes in the current IFC document""" def GetResources(self): - tt = QT_TRANSLATE_NOOP( - "IFC_Diff", "Shows the current unsaved changes in the IFC file" - ) + tt = QT_TRANSLATE_NOOP("IFC_Diff", "Shows the current unsaved changes in the IFC file") return { "Pixmap": "IFC", "MenuText": QT_TRANSLATE_NOOP("IFC_Diff", "IFC Diff"), @@ -123,11 +121,7 @@ class IFC_ConvertDocument: def Activated(self): doc = FreeCAD.ActiveDocument - if ( - hasattr(doc, "Proxy") - and hasattr(doc.Proxy, "ifcfile") - and doc.Proxy.ifcfile - ): + if hasattr(doc, "Proxy") and hasattr(doc.Proxy, "ifcfile") and doc.Proxy.ifcfile: FreeCAD.Console.PrintError( translate("BIM", "The active document is already an IFC document") ) @@ -181,9 +175,7 @@ class IFC_Save: """Saves the current IFC document""" def GetResources(self): - tt = QT_TRANSLATE_NOOP( - "IFC_Save", "Saves the current IFC document" - ) + tt = QT_TRANSLATE_NOOP("IFC_Save", "Saves the current IFC document") return { "Pixmap": "IFC_document", "MenuText": QT_TRANSLATE_NOOP("IFC_Save", "Save IFC File"), @@ -216,9 +208,7 @@ class IFC_SaveAs: """Saves the current IFC document as another name""" def GetResources(self): - tt = QT_TRANSLATE_NOOP( - "IFC_SaveAs", "Saves the current IFC document as another file" - ) + tt = QT_TRANSLATE_NOOP("IFC_SaveAs", "Saves the current IFC document as another file") return { "Pixmap": "IFC_document", "MenuText": QT_TRANSLATE_NOOP("IFC_SaveAs", "Save IFC File As…"), diff --git a/src/Mod/BIM/nativeifc/ifc_diff.py b/src/Mod/BIM/nativeifc/ifc_diff.py index a9cf4bcace..46a17f95eb 100644 --- a/src/Mod/BIM/nativeifc/ifc_diff.py +++ b/src/Mod/BIM/nativeifc/ifc_diff.py @@ -66,9 +66,15 @@ def htmlize(diff): html = "\n" if diff == 1: - html += translate("BIM", "The IFC file is not saved. Save once" - " to have an existing IFC file to compare with." - " Then, run this command again.") + "
\n" + html += ( + translate( + "BIM", + "The IFC file is not saved. Save once" + " to have an existing IFC file to compare with." + " Then, run this command again.", + ) + + "
\n" + ) elif diff: diff = diff.split("\n") for l in diff: diff --git a/src/Mod/BIM/nativeifc/ifc_export.py b/src/Mod/BIM/nativeifc/ifc_export.py index ec9288de75..9b1f85f0a5 100644 --- a/src/Mod/BIM/nativeifc/ifc_export.py +++ b/src/Mod/BIM/nativeifc/ifc_export.py @@ -68,18 +68,22 @@ def get_export_preferences(ifcfile, preferred_context=None, create=None): second_choice = None for context in contexts: if len(preferred_context) > 2: - if (context.TargetView == preferred_context[2] + if ( + context.TargetView == preferred_context[2] and context.ContextType == preferred_context[1] - and context.ContextIdentifier == preferred_context[0]): - best_context = context - exact_match = True + and context.ContextIdentifier == preferred_context[0] + ): + best_context = context + exact_match = True if len(preferred_context) > 1: - if (context.ContextType == preferred_context[1] - and context.ContextIdentifier == preferred_context[0]): - if not exact_match: - best_context = context - if len(preferred_context) == 2: - exact_match = True + if ( + context.ContextType == preferred_context[1] + and context.ContextIdentifier == preferred_context[0] + ): + if not exact_match: + best_context = context + if len(preferred_context) == 2: + exact_match = True if context.ContextType == preferred_context[0]: if not exact_match: best_context = context @@ -91,52 +95,58 @@ def get_export_preferences(ifcfile, preferred_context=None, create=None): if create: if not exact_match: if isinstance(preferred_context, str): - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context) + best_context = ifc_tools.api_run( + "context.add_context", ifcfile, context_type=preferred_context + ) elif best_context: if len(preferred_context) > 2: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[1], - context_identifier = preferred_context[0], - target_view = preferred_context[2], - parent = best_context) + best_context = ifc_tools.api_run( + "context.add_context", + ifcfile, + context_type=preferred_context[1], + context_identifier=preferred_context[0], + target_view=preferred_context[2], + parent=best_context, + ) elif len(preferred_context) > 1: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[1], - context_identifier = preferred_context[0], - parent = best_context) + best_context = ifc_tools.api_run( + "context.add_context", + ifcfile, + context_type=preferred_context[1], + context_identifier=preferred_context[0], + parent=best_context, + ) else: if len(preferred_context) > 1: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[1]) + best_context = ifc_tools.api_run( + "context.add_context", ifcfile, context_type=preferred_context[1] + ) if len(preferred_context) > 2: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[1], - context_identifier = preferred_context[0], - target_view = preferred_context[2], - parent = best_context) + best_context = ifc_tools.api_run( + "context.add_context", + ifcfile, + context_type=preferred_context[1], + context_identifier=preferred_context[0], + target_view=preferred_context[2], + parent=best_context, + ) else: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[1], - context_identifier = preferred_context[0], - parent = best_context) + best_context = ifc_tools.api_run( + "context.add_context", + ifcfile, + context_type=preferred_context[1], + context_identifier=preferred_context[0], + parent=best_context, + ) else: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = preferred_context[0]) + best_context = ifc_tools.api_run( + "context.add_context", ifcfile, context_type=preferred_context[0] + ) if not best_context: if contexts: best_context = contexts[0] else: - best_context = ifc_tools.api_run("context.add_context", - ifcfile, - context_type = "Model") + best_context = ifc_tools.api_run("context.add_context", ifcfile, context_type="Model") return prefs, best_context @@ -215,23 +225,27 @@ def is_annotation(obj): return True elif obj.isDerivedFrom("App::Annotation"): return True - elif Draft.getType(obj) in ["BezCurve", - "BSpline", - "Wire", - "DraftText", - "Text", - "Dimension", - "LinearDimension", - "AngularDimension", - "SectionPlane"]: + elif Draft.getType(obj) in [ + "BezCurve", + "BSpline", + "Wire", + "DraftText", + "Text", + "Dimension", + "LinearDimension", + "AngularDimension", + "SectionPlane", + ]: return True elif obj.isDerivedFrom("Part::Feature"): if obj.Shape and (not obj.Shape.Solids) and obj.Shape.Edges: if not obj.Shape.Faces: return True - elif (obj.Shape.BoundBox.XLength < 0.0001) \ - or (obj.Shape.BoundBox.YLength < 0.0001) \ - or (obj.Shape.BoundBox.ZLength < 0.0001): + elif ( + (obj.Shape.BoundBox.XLength < 0.0001) + or (obj.Shape.BoundBox.YLength < 0.0001) + or (obj.Shape.BoundBox.ZLength < 0.0001) + ): return True return False @@ -271,7 +285,7 @@ def get_dimension(annotation): res.append(shape[0].Vertexes[1].Point) return res else: - print(annotation,"NOT A DIMENSION") + print(annotation, "NOT A DIMENSION") return None @@ -288,9 +302,9 @@ def get_sectionplane(annotation): for item in rep.Items: if item.is_a("IfcCsgSolid"): if item.TreeRootExpression.is_a("IfcBlock"): - result.append(item.TreeRootExpression.XLength*s) - result.append(item.TreeRootExpression.YLength*s) - result.append(item.TreeRootExpression.ZLength*s) + result.append(item.TreeRootExpression.XLength * s) + result.append(item.TreeRootExpression.YLength * s) + result.append(item.TreeRootExpression.ZLength * s) return result return None @@ -314,7 +328,7 @@ def get_axis(obj): length = edge.Length placement = FreeCAD.Placement() placement.Base = p0 - placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0,1,0), p1.sub(p0)) + placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), p1.sub(p0)) return (placement, length, tag) return None @@ -334,7 +348,7 @@ def create_annotation(obj, ifcfile): else: context_type = "Model" prefs, context = get_export_preferences(ifcfile, preferred_context=context_type, create=True) - prefs["BBIMDIMS"] = True # Save dimensions as 2-point polylines + prefs["BBIMDIMS"] = True # Save dimensions as 2-point polylines history = get_history(ifcfile) # TODO The following prints each edge as a separate IfcGeometricCurveSet # It should be refined to create polylines instead diff --git a/src/Mod/BIM/nativeifc/ifc_generator.py b/src/Mod/BIM/nativeifc/ifc_generator.py index c68a489bd7..29a48c1466 100644 --- a/src/Mod/BIM/nativeifc/ifc_generator.py +++ b/src/Mod/BIM/nativeifc/ifc_generator.py @@ -107,7 +107,7 @@ def generate_geometry(obj, cached=False): node, placement = generate_coin(ifcfile, elements, cached) if node: # TODO this still needs to be fixed - #QtCore.QTimer.singleShot(0, lambda: set_representation(obj.ViewObject, node)) + # QtCore.QTimer.singleShot(0, lambda: set_representation(obj.ViewObject, node)) set_representation(obj.ViewObject, node) colors = node[0] else: @@ -203,9 +203,7 @@ def generate_shape(ifcfile, elements, cached=False): ) if len(colors) < len(shape.Faces): for i in range(len(shape.Faces) - len(colors)): - scolors.append( - (sstyle[0], sstyle[1], sstyle[2], 1.0 - sstyle[3]) - ) + scolors.append((sstyle[0], sstyle[1], sstyle[2], 1.0 - sstyle[3])) else: color = (sstyle[0], sstyle[1], sstyle[2], 1.0 - sstyle[3]) for f in shape.Faces: @@ -311,15 +309,11 @@ def generate_coin(ifcfile, elements, cached=False): # faces faces = list(item.geometry.faces) - faces = [ - f for i in range(0, len(faces), 3) for f in faces[i : i + 3] + [-1] - ] + faces = [f for i in range(0, len(faces), 3) for f in faces[i : i + 3] + [-1]] # edges edges = list(item.geometry.edges) - edges = [ - e for i in range(0, len(edges), 2) for e in edges[i : i + 2] + [-1] - ] + edges = [e for i in range(0, len(edges), 2) for e in edges[i : i + 2] + [-1]] # update cache node = [color, verts, faces, edges] @@ -491,8 +485,8 @@ def set_representation(vobj, node): # the number of display modes under switch can vary. # the last 4 ones are the ones that are defined for # Part features - faces = switch.getChild(num_modes-3) - edges = switch.getChild(num_modes-2) + faces = switch.getChild(num_modes - 3) + edges = switch.getChild(num_modes - 2) fset = None if faces.getNumChildren() >= 7: fset = faces.getChild(6) # SoBrepFaceSet @@ -614,8 +608,8 @@ def get_annotation_shape(annotation, ifcfile, coin=False): if coin: iv = shape.writeInventor() iv = iv.replace("\n", "") - segs = re.findall(r"point \[.*?\]",iv) - segs = [s.replace("point [","").replace("]","").strip() for s in segs] + segs = re.findall(r"point \[.*?\]", iv) + segs = [s.replace("point [", "").replace("]", "").strip() for s in segs] segs = [s.split(" ") for s in segs] verts = [] edges = [] diff --git a/src/Mod/BIM/nativeifc/ifc_geometry.py b/src/Mod/BIM/nativeifc/ifc_geometry.py index 3cf785bdba..424635c4b4 100644 --- a/src/Mod/BIM/nativeifc/ifc_geometry.py +++ b/src/Mod/BIM/nativeifc/ifc_geometry.py @@ -56,9 +56,7 @@ def add_geom_properties(obj): obj.addProperty( "App::PropertyVector", "ExtrusionDirection", "Geometry", locked=True ) - obj.ExtrusionDirection = FreeCAD.Vector( - ext.ExtrudedDirection.DirectionRatios - ) + obj.ExtrusionDirection = FreeCAD.Vector(ext.ExtrudedDirection.DirectionRatios) # Extrusion of a rectangle if ext.SweptArea.is_a("IfcRectangleProfileDef"): @@ -83,13 +81,9 @@ def add_geom_properties(obj): "Geometry", locked=True, ) - points = [ - p.Coordinates for p in ext.SweptArea.OuterCurve.Points - ] + points = [p.Coordinates for p in ext.SweptArea.OuterCurve.Points] points = [p + (0,) for p in points if len(p) < 3] - points = [ - FreeCAD.Vector(p).multiply(scaling) for p in points - ] + points = [FreeCAD.Vector(p).multiply(scaling) for p in points] obj.PolylinePoints = points # I profile @@ -239,9 +233,7 @@ def set_geom_property(obj, prop): rest.append(elem_points.pop()) elem.Points = elem_points for r in rest: - ifc_tools.api_run( - "root.remove_product", ifcfile, product=r - ) + ifc_tools.api_run("root.remove_product", ifcfile, product=r) if len(points) == len(elem_points): for i in range(len(points)): v = FreeCAD.Vector(points[i]).multiply(scaling) diff --git a/src/Mod/BIM/nativeifc/ifc_import.py b/src/Mod/BIM/nativeifc/ifc_import.py index 3d79e66a44..a79da554bf 100644 --- a/src/Mod/BIM/nativeifc/ifc_import.py +++ b/src/Mod/BIM/nativeifc/ifc_import.py @@ -90,9 +90,7 @@ def insert( prj_obj = ifc_tools.convert_document(document, filename, shapemode, strategy) QtCore.QTimer.singleShot(100, toggle_lock_on) else: - prj_obj = ifc_tools.create_document_object( - document, filename, shapemode, strategy - ) + prj_obj = ifc_tools.create_document_object(document, filename, shapemode, strategy) QtCore.QTimer.singleShot(100, toggle_lock_off) if PARAMS.GetBool("LoadOrphans", True): ifc_tools.load_orphans(prj_obj) @@ -208,6 +206,7 @@ def get_project_type(silent=False): # convenience functions + def toggle_lock_on(): ifc_status.on_toggle_lock(True, noconvert=True, setchecked=True) diff --git a/src/Mod/BIM/nativeifc/ifc_materials.py b/src/Mod/BIM/nativeifc/ifc_materials.py index 259e21f148..271e5587d7 100644 --- a/src/Mod/BIM/nativeifc/ifc_materials.py +++ b/src/Mod/BIM/nativeifc/ifc_materials.py @@ -109,9 +109,7 @@ def get_material(obj): elif element.is_a("IfcMaterialProfileSet"): return element.MaterialProfiles else: - material = ifcopenshell.util.element.get_material( - element, should_skip_usage=True - ) + material = ifcopenshell.util.element.get_material(element, should_skip_usage=True) return material @@ -125,9 +123,7 @@ def set_material(material, obj): return new = False if not material_element or ifc_tools.get_ifcfile(material) != ifcfile: - material_element = ifc_tools.api_run( - "material.add_material", ifcfile, name=material.Label - ) + material_element = ifc_tools.api_run("material.add_material", ifcfile, name=material.Label) new = True delete = not (ifc_tools.PARAMS.GetBool("KeepAggregated", False)) if delete and len(material.InList) == 1: diff --git a/src/Mod/BIM/nativeifc/ifc_objects.py b/src/Mod/BIM/nativeifc/ifc_objects.py index f13bd21752..5a5adaeae0 100644 --- a/src/Mod/BIM/nativeifc/ifc_objects.py +++ b/src/Mod/BIM/nativeifc/ifc_objects.py @@ -31,8 +31,19 @@ translate = FreeCAD.Qt.translate # the property groups below should not be treated as psets -NON_PSETS = ["Base", "IFC", "", "Geometry", "Dimension", "Linear/radial dimension", - "SectionPlane", "Axis", "PhysicalProperties", "BuildingPart", "IFC Attributes"] +NON_PSETS = [ + "Base", + "IFC", + "", + "Geometry", + "Dimension", + "Linear/radial dimension", + "SectionPlane", + "Axis", + "PhysicalProperties", + "BuildingPart", + "IFC Attributes", +] class ifc_object: @@ -42,9 +53,7 @@ class ifc_object: self.cached = True # this marks that the object is freshly created and its shape should be taken from cache self.virgin_placement = True # this allows one to set the initial placement without triggering any placement change if otype: - self.Type = ( - otype[0].upper() + otype[1:] - ) # capitalize to match Draft standard + self.Type = otype[0].upper() + otype[1:] # capitalize to match Draft standard else: self.Type = "IfcObject" @@ -80,7 +89,7 @@ class ifc_object: self.edit_annotation(obj, "Text", "\n".join(obj.Text)) elif prop in ["Start", "End"]: self.edit_annotation(obj, prop) - elif prop in ["DisplayLength","DisplayHeight","Depth"]: + elif prop in ["DisplayLength", "DisplayHeight", "Depth"]: self.edit_annotation(obj, prop) elif prop == "Placement": if getattr(self, "virgin_placement", False): @@ -244,8 +253,8 @@ class ifc_object: value[-1] = ifc_export.get_scaled_point(obj.End, ifcfile, is2d) ifc_tools.set_attribute(ifcfile, points, "CoordList", value) else: - print("DEBUG: unknown dimension curve type:",sub) - elif attribute in ["DisplayLength","DisplayHeight","Depth"]: + print("DEBUG: unknown dimension curve type:", sub) + elif attribute in ["DisplayLength", "DisplayHeight", "Depth"]: l = w = h = 1000.0 if obj.ViewObject: if obj.ViewObject.DisplayLength.Value: @@ -264,7 +273,9 @@ class ifc_object: ifc_tools.set_attribute(ifcfile, block, "XLength", l) ifc_tools.set_attribute(ifcfile, block, "YLength", w) ifc_tools.set_attribute(ifcfile, block, "ZLength", h) - ifc_tools.set_attribute(ifcfile, loc, "Coordinates", (-l/2, -h/2, -h)) + ifc_tools.set_attribute( + ifcfile, loc, "Coordinates", (-l / 2, -h / 2, -h) + ) def edit_geometry(self, obj, prop): """Edits a geometry property of an object""" @@ -294,11 +305,7 @@ class ifc_object: ifcfile, migration_table = ifc_tools.migrate_schema(ifcfile, schema) self.ifcfile = ifcfile for old_id, new_id in migration_table.items(): - child = [ - o - for o in obj.OutListRecursive - if getattr(o, "StepId", None) == old_id - ] + child = [o for o in obj.OutListRecursive if getattr(o, "StepId", None) == old_id] if len(child) == 1: child[0].StepId = new_id @@ -331,10 +338,7 @@ class ifc_object: return newlist = [] for child in obj.Group: - if ( - not getattr(child, "StepId", None) - or ifc_tools.get_ifcfile(child) != ifcfile - ): + if not getattr(child, "StepId", None) or ifc_tools.get_ifcfile(child) != ifcfile: print( "DEBUG: Not an IFC object. Removing", child.Label, @@ -386,22 +390,21 @@ class ifc_object: if s: FreeCAD.Console.PrintLog("DEBUG: Generating shapes. This might take some time...\n") for o in s: - o.ShapeMode = "Shape" - o.recompute() + o.ShapeMode = "Shape" + o.recompute() l = 1 h = 1 if obj.ViewObject: - if hasattr(obj.ViewObject,"DisplayLength"): + if hasattr(obj.ViewObject, "DisplayLength"): l = obj.ViewObject.DisplayLength.Value h = obj.ViewObject.DisplayHeight.Value - plane = Part.makePlane(l,h,FreeCAD.Vector(l/2,-h/2,0),FreeCAD.Vector(0,0,1)) + plane = Part.makePlane(l, h, FreeCAD.Vector(l / 2, -h / 2, 0), FreeCAD.Vector(0, 0, 1)) plane.Placement = obj.Placement return objs, plane else: print("DEBUG: Section plane returned no objects") return [], None - def edit_classification(self, obj): """Edits the classification of this object""" diff --git a/src/Mod/BIM/nativeifc/ifc_observer.py b/src/Mod/BIM/nativeifc/ifc_observer.py index f0c4c097ce..b26684f31f 100644 --- a/src/Mod/BIM/nativeifc/ifc_observer.py +++ b/src/Mod/BIM/nativeifc/ifc_observer.py @@ -98,11 +98,7 @@ class ifc_observer: doc.Proxy.ifcfile = ifcfile # migrate children for old_id, new_id in migration_table.items(): - child = [ - o - for o in doc.Objects - if getattr(o, "StepId", None) == old_id - ] + child = [o for o in doc.Objects if getattr(o, "StepId", None) == old_id] if len(child) == 1: child[0].StepId = new_id elif prop == "Label": @@ -133,6 +129,7 @@ class ifc_observer: def slotRemoveDynamicProperty(self, obj, prop): from . import ifc_psets + ifc_psets.remove_property(obj, prop) # implementation methods @@ -204,9 +201,11 @@ class ifc_observer: return del self.docname del self.objname - if obj.isDerivedFrom("Part::Feature") \ - or "IfcType" in obj.PropertiesList \ - or "CreateSpreadsheet" in obj.PropertiesList: + if ( + obj.isDerivedFrom("Part::Feature") + or "IfcType" in obj.PropertiesList + or "CreateSpreadsheet" in obj.PropertiesList + ): FreeCAD.Console.PrintLog("Converting " + obj.Label + " to IFC\n") from . import ifc_geometry # lazy loading from . import ifc_tools # lazy loading diff --git a/src/Mod/BIM/nativeifc/ifc_openshell.py b/src/Mod/BIM/nativeifc/ifc_openshell.py index a994c3b3f7..efc813cf30 100644 --- a/src/Mod/BIM/nativeifc/ifc_openshell.py +++ b/src/Mod/BIM/nativeifc/ifc_openshell.py @@ -64,13 +64,16 @@ class IFC_UpdateIOS: else: self.show_dialog("failed") - def show_dialog(self, mode, version=None): """Shows a dialog to the user""" from PySide import QtGui + title = translate("BIM", "IfcOpenShell update") - note = translate("BIM", "The update is installed in your FreeCAD's user directory and will not affect the rest of your system.") + note = translate( + "BIM", + "The update is installed in your FreeCAD's user directory and will not affect the rest of your system.", + ) if mode == "update": text = translate("BIM", "An update to your installed IfcOpenShell version is available") text += ": " + version + ". " @@ -87,7 +90,10 @@ class IFC_UpdateIOS: text += " (" + version + ") " + note buttons = QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok elif mode == "failed": - text = translate("BIM", "IfcOpenShell is not installed, and FreeCAD failed to find a suitable version to install. You can still install IfcOpenShell manually, visit https://wiki.freecad.org/IfcOpenShell for further instructions.") + text = translate( + "BIM", + "IfcOpenShell is not installed, and FreeCAD failed to find a suitable version to install. You can still install IfcOpenShell manually, visit https://wiki.freecad.org/IfcOpenShell for further instructions.", + ) buttons = QtGui.QMessageBox.Ok reply = QtGui.QMessageBox.information(None, title, text, buttons) if reply == QtGui.QMessageBox.Ok: @@ -99,20 +105,26 @@ class IFC_UpdateIOS: buttons = QtGui.QMessageBox.Ok reply = QtGui.QMessageBox.information(None, title, text, buttons) - def install(self): """Installs the given version""" import addonmanager_utilities as utils from PySide import QtCore, QtGui + QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) vendor_path = utils.get_pip_target_directory() - args = ["install", "--upgrade", "--disable-pip-version-check", "--target", vendor_path, "ifcopenshell"] + args = [ + "install", + "--upgrade", + "--disable-pip-version-check", + "--target", + vendor_path, + "ifcopenshell", + ] result = self.run_pip(args) QtGui.QApplication.restoreOverrideCursor() return result - def run_pip(self, args): """Runs a pip command""" @@ -127,11 +139,10 @@ class IFC_UpdateIOS: except CalledProcessError as pe: FreeCAD.Console.PrintError(pe.stderr) except Exception as e: - text = translate("BIM","Unable to run pip. Ensure pip is installed on your system.") + text = translate("BIM", "Unable to run pip. Ensure pip is installed on your system.") FreeCAD.Console.PrintError(f"{text} {str(e)}\n") return result - def get_current_version(self): """Retrieves the current ifcopenshell version""" @@ -140,6 +151,7 @@ class IFC_UpdateIOS: try: import ifcopenshell + version = ifcopenshell.version try: Version(version) @@ -151,7 +163,6 @@ class IFC_UpdateIOS: return version - def get_avail_version(self): """Retrieves an available ifcopenshell version""" @@ -159,7 +170,7 @@ class IFC_UpdateIOS: if result: if result.stdout and "versions" in result.stdout: result = result.stdout.split() - result = result[result.index("versions:")+1:] + result = result[result.index("versions:") + 1 :] result = [r.strip(",") for r in result] return result[0] # we return the biggest return None @@ -168,7 +179,6 @@ class IFC_UpdateIOS: FreeCADGui.addCommand("IFC_UpdateIOS", IFC_UpdateIOS()) - # >>> utils.get_pip_target_directory() # '/home/yorik/.local/share/FreeCAD/Mod/../AdditionalPythonPackages/py311' # >>> import freecad.utils diff --git a/src/Mod/BIM/nativeifc/ifc_performance_test.py b/src/Mod/BIM/nativeifc/ifc_performance_test.py index 692d4c4c9c..a186f44a2e 100644 --- a/src/Mod/BIM/nativeifc/ifc_performance_test.py +++ b/src/Mod/BIM/nativeifc/ifc_performance_test.py @@ -132,12 +132,8 @@ class NativeIFCTest(unittest.TestCase): # self.results.append(register(n, t, "shape")) def testfinal(self): - print( - "| File | File size | Import time (coin) | Import time (shape) | BlenderBIM |" - ) - print( - "| ---- | --------- | ------------------- | ------------------ | ---------- |" - ) + print("| File | File size | Import time (coin) | Import time (shape) | BlenderBIM |") + print("| ---- | --------- | ------------------- | ------------------ | ---------- |") for i in range(len(self.results)): if self.results[i][0] == "coin": l = [ @@ -150,8 +146,7 @@ class NativeIFCTest(unittest.TestCase): b = [ j for j in range(len(self.results)) - if self.results[j][0] == "shape" - and self.results[j][1] == self.results[i][1] + if self.results[j][0] == "shape" and self.results[j][1] == self.results[i][1] ] if b: l[3] = self.results[b[0]][3] @@ -181,9 +176,7 @@ def import_file(n, shape=False): shapemode = 1 stime = time.time() f = os.path.join(os.path.expanduser("~"), FILES[n]) - ifc_import.insert( - f, "IfcTest", strategy=0, shapemode=shapemode, switchwb=0, silent=True - ) + ifc_import.insert(f, "IfcTest", strategy=0, shapemode=shapemode, switchwb=0, silent=True) return "%02d:%02d" % (divmod(round(time.time() - stime, 1), 60)) diff --git a/src/Mod/BIM/nativeifc/ifc_psets.py b/src/Mod/BIM/nativeifc/ifc_psets.py index 0f1b2c5eee..1ef501f75d 100644 --- a/src/Mod/BIM/nativeifc/ifc_psets.py +++ b/src/Mod/BIM/nativeifc/ifc_psets.py @@ -105,11 +105,9 @@ def show_psets(obj): pname = re.sub(r"[^0-9a-zA-Z]+", "", pname) if pname[0].isdigit(): pname = "_" + pname - ttip = ( - ptype + ":" + oname - ) # setting IfcType:PropName as a tooltip to desambiguate - #while pname in obj.PropertiesList: - # print("DEBUG: property", pname, "(", value, ") already exists in", obj.Label) + ttip = ptype + ":" + oname # setting IfcType:PropName as a tooltip to desambiguate + # while pname in obj.PropertiesList: + # print("DEBUG: property", pname, "(", value, ") already exists in", obj.Label) # pname += "_" ftype = None if ptype in [ @@ -157,12 +155,13 @@ def show_psets(obj): ftype = "App::PropertyString" # print("DEBUG: setting",pname, ptype, value) if ftype: - if pname in obj.PropertiesList \ - and obj.getGroupOfProperty(pname) == gname: + if pname in obj.PropertiesList and obj.getGroupOfProperty(pname) == gname: if obj.getTypeOfProperty(pname) == ftype: pass - if ftype == "App::PropertyString" \ - and obj.getTypeOfProperty(pname) == "App::PropertyStringList": + if ( + ftype == "App::PropertyString" + and obj.getTypeOfProperty(pname) == "App::PropertyStringList" + ): value = [value] else: print(pname, gname, obj.PropertiesList) @@ -258,7 +257,7 @@ def edit_pset(obj, prop, value=None, force=False, ifcfile=None, element=None): elif value.Unit.Type == "Length": value = value.getValueAs("mm").Value * ifc_tools.get_scale(ifcfile) else: - print("DEBUG: unhandled quantity type:",value, value.Unit.Type) + print("DEBUG: unhandled quantity type:", value, value.Unit.Type) return False if value == value_exist: return False @@ -286,9 +285,7 @@ def edit_pset(obj, prop, value=None, force=False, ifcfile=None, element=None): # TODO the property type is automatically determined by ifcopenhell # https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/pset/edit_pset/index.html # and is therefore wrong for Quantity types. Research a way to overcome that - ifc_tools.api_run( - "pset.edit_pset", ifcfile, pset=ifcpset, properties={target_prop: value} - ) + ifc_tools.api_run("pset.edit_pset", ifcfile, pset=ifcpset, properties={target_prop: value}) # TODO manage quantities return True @@ -311,9 +308,7 @@ def add_pset(obj, psetname): ifcfile = ifc_tools.get_ifcfile(obj) element = ifc_tools.get_ifc_element(obj) if ifcfile and element: - pset = ifc_tools.api_run( - "pset.add_pset", ifcfile, product=element, name=psetname - ) + pset = ifc_tools.api_run("pset.add_pset", ifcfile, product=element, name=psetname) return pset @@ -349,6 +344,7 @@ def read_properties_conversion(): """Reads the properties conversion table""" import csv + csvfile = os.path.join( FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "properties_conversion.csv" ) @@ -365,6 +361,7 @@ def remove_property(obj, prop): """Removes a custom property""" from . import ifc_tools + ifcfile = ifc_tools.get_ifcfile(obj) if not ifcfile: return @@ -376,11 +373,13 @@ def remove_property(obj, prop): if prop in props: pset = get_pset(psetname, element) if pset: - FreeCAD.Console.PrintMessage(translate("BIM","Removing property")+": "+prop) + FreeCAD.Console.PrintMessage(translate("BIM", "Removing property") + ": " + prop) ifc_tools.api_run("pset.edit_pset", ifcfile, pset=pset, properties={prop: None}) if len(props) == 1: # delete the pset too - FreeCAD.Console.PrintMessage(translate("BIM","Removing property set")+": "+psetname) + FreeCAD.Console.PrintMessage( + translate("BIM", "Removing property set") + ": " + psetname + ) ifc_tools.api_run("pset.remove_pset", ifcfile, product=element, pset=pset) diff --git a/src/Mod/BIM/nativeifc/ifc_selftest.py b/src/Mod/BIM/nativeifc/ifc_selftest.py index 19648a3751..2f9ec4870e 100644 --- a/src/Mod/BIM/nativeifc/ifc_selftest.py +++ b/src/Mod/BIM/nativeifc/ifc_selftest.py @@ -84,10 +84,7 @@ def compare(file1, file2): with open(file2) as f2: f2_text = f2.readlines() res = [ - l - for l in difflib.unified_diff( - f1_text, f2_text, fromfile=file1, tofile=file2, lineterm="" - ) + l for l in difflib.unified_diff(f1_text, f2_text, fromfile=file1, tofile=file2, lineterm="") ] res = [l for l in res if l.startswith("+") or l.startswith("-")] res = [l for l in res if not l.startswith("+++") and not l.startswith("---")] @@ -110,9 +107,7 @@ class NativeIFCTest(unittest.TestCase): pass def test01_ImportCoinSingle(self): - FreeCAD.Console.PrintMessage( - "NativeIFC 01: Importing single object, coin mode..." - ) + FreeCAD.Console.PrintMessage("NativeIFC 01: Importing single object, coin mode...") clearObjects() fp = getIfcFilePath() ifc_import.insert( @@ -128,9 +123,7 @@ class NativeIFCTest(unittest.TestCase): self.assertTrue(fco == 1 - SDU, "ImportCoinSingle failed") def test02_ImportCoinStructure(self): - FreeCAD.Console.PrintMessage( - "NativeIFC 02: Importing model structure, coin mode..." - ) + FreeCAD.Console.PrintMessage("NativeIFC 02: Importing model structure, coin mode...") clearObjects() fp = getIfcFilePath() ifc_import.insert( @@ -182,9 +175,7 @@ class NativeIFCTest(unittest.TestCase): self.assertTrue(fco > 4 - SDU, "ImportShapeFull failed") def test05_ImportFreeCAD(self): - FreeCAD.Console.PrintMessage( - "NativeIFC 05: FreeCAD import of NativeIFC coin file..." - ) + FreeCAD.Console.PrintMessage("NativeIFC 05: FreeCAD import of NativeIFC coin file...") clearObjects() doc = FreeCAD.open(FCSTD_FILE_PATH) obj = doc.Objects[-1] @@ -204,9 +195,7 @@ class NativeIFCTest(unittest.TestCase): ifc_diff = compare(IFC_FILE_PATH, proj.IfcFilePath) obj.ShapeMode = 0 obj.Proxy.execute(obj) - self.assertTrue( - obj.Shape.Volume > 2 and len(ifc_diff) <= 5, "ModifyObjects failed" - ) + self.assertTrue(obj.Shape.Volume > 2 and len(ifc_diff) <= 5, "ModifyObjects failed") def test07_CreateDocument(self): FreeCAD.Console.PrintMessage("NativeIFC 07: Creating new IFC document...") @@ -307,6 +296,7 @@ class NativeIFCTest(unittest.TestCase): def test12_RemoveObject(self): from . import ifc_observer + ifc_observer.add_observer() FreeCAD.Console.PrintMessage("NativeIFC 12: Remove object...") clearObjects() @@ -393,7 +383,7 @@ class NativeIFCTest(unittest.TestCase): self.assertTrue(ifc_psets.has_psets(obj), "Psets failed") -IFCFILECONTENT="""ISO-10303-21; +IFCFILECONTENT = """ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1'); FILE_NAME('IfcOpenHouse.ifc','2014-05-05T15:42:14',(''),('',''),'IfcOpenShell 0.5.0-dev','IfcOpenShell 0.5.0-dev',''); diff --git a/src/Mod/BIM/nativeifc/ifc_status.py b/src/Mod/BIM/nativeifc/ifc_status.py index 4d4dcbbf91..29d0125d15 100644 --- a/src/Mod/BIM/nativeifc/ifc_status.py +++ b/src/Mod/BIM/nativeifc/ifc_status.py @@ -70,17 +70,17 @@ def set_properties_editor(statuswidget): from PySide import QtCore, QtGui # lazy loading mw = FreeCADGui.getMainWindow() - editor = mw.findChild(QtGui.QTabWidget,"propertyTab") + editor = mw.findChild(QtGui.QTabWidget, "propertyTab") if editor: pTabCornerWidget = QtGui.QWidget() pButton1 = QtGui.QToolButton(pTabCornerWidget) pButton1.setText("") - pButton1.setToolTip(translate("BIM","Add IFC property...")) + pButton1.setToolTip(translate("BIM", "Add IFC property...")) pButton1.setIcon(QtGui.QIcon(":/icons/IFC.svg")) pButton1.clicked.connect(on_add_property) pButton2 = QtGui.QToolButton(pTabCornerWidget) pButton2.setText("") - pButton2.setToolTip(translate("BIM","Add standard IFC Property Set...")) + pButton2.setToolTip(translate("BIM", "Add standard IFC Property Set...")) pButton2.setIcon(QtGui.QIcon(":/icons/BIM_IfcProperties.svg")) pButton2.clicked.connect(on_add_pset) pHLayout = QtGui.QHBoxLayout(pTabCornerWidget) @@ -91,7 +91,7 @@ def set_properties_editor(statuswidget): pHLayout.insertStretch(0) editor.setCornerWidget(pTabCornerWidget, QtCore.Qt.BottomRightCorner) statuswidget.propertybuttons = pTabCornerWidget - QtCore.QTimer.singleShot(0,pTabCornerWidget.show) + QtCore.QTimer.singleShot(0, pTabCornerWidget.show) def on_add_property(): @@ -102,12 +102,13 @@ def on_add_property(): return from PySide import QtGui # lazy loading from . import ifc_psets + obj = sel[0] psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList])) psets = [p for p in psets if p] psets = [p for p in psets if p not in ["Base", "IFC", "Geometry"]] mw = FreeCADGui.getMainWindow() - editor = mw.findChild(QtGui.QTabWidget,"propertyTab") + editor = mw.findChild(QtGui.QTabWidget, "propertyTab") pset = None if editor: wid = editor.currentWidget() @@ -130,21 +131,26 @@ def on_add_property(): return pname = form.field_name.text() if pname in obj.PropertiesList: - print("DEBUG: property already exists",pname) + print("DEBUG: property already exists", pname) return pset = form.field_pset.currentText() if not pset: # TODO disable the OK button if empty - t = translate("BIM","No Property set provided") - FreeCAD.Console.PrintError(t+"\n") + t = translate("BIM", "No Property set provided") + FreeCAD.Console.PrintError(t + "\n") ptype = form.field_type.currentIndex() - ptype = ["IfcLabel", "IfcBoolean", - "IfcInteger", "IfcReal", - "IfcLengthMeasure", "IfcAreaMeasure"][ptype] + ptype = [ + "IfcLabel", + "IfcBoolean", + "IfcInteger", + "IfcReal", + "IfcLengthMeasure", + "IfcAreaMeasure", + ][ptype] fctype = ifc_psets.get_freecad_type(ptype) - FreeCAD.ActiveDocument.openTransaction(translate("BIM","add property")) + FreeCAD.ActiveDocument.openTransaction(translate("BIM", "add property")) for obj in sel: - obj.addProperty(fctype, pname, pset, ptype+":"+pname) + obj.addProperty(fctype, pname, pset, ptype + ":" + pname) ifc_psets.edit_pset(obj, pname, force=True) FreeCAD.ActiveDocument.commitTransaction() @@ -180,6 +186,7 @@ def on_add_pset(): if not sel: return from . import ifc_psets + obj = sel[0] mw = FreeCADGui.getMainWindow() # read standard psets @@ -203,22 +210,22 @@ def on_add_pset(): pset = form.field_pset.currentText() existing_psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList])) if pset in existing_psets: - t = translate("BIM","Property set already exists") - FreeCAD.Console.PrintError(t+": "+pset+"\n") + t = translate("BIM", "Property set already exists") + FreeCAD.Console.PrintError(t + ": " + pset + "\n") return - props = [psetdefs[pset][i:i+2] for i in range(0, len(psetdefs[pset]), 2)] + props = [psetdefs[pset][i : i + 2] for i in range(0, len(psetdefs[pset]), 2)] props = [[p[0], p[1]] for p in props] - FreeCAD.ActiveDocument.openTransaction(translate("BIM","add property set")) + FreeCAD.ActiveDocument.openTransaction(translate("BIM", "add property set")) for obj in sel: existing_psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList])) if pset not in existing_psets: ifc_psets.add_pset(obj, pset) for prop in props: if prop[0] in obj.PropertiesList: - t = translate("BIM","Property already exists") - FreeCAD.Console.PrintWarning(t+": "+obj.Label+","+prop[0]+"\n") + t = translate("BIM", "Property already exists") + FreeCAD.Console.PrintWarning(t + ": " + obj.Label + "," + prop[0] + "\n") else: - obj.addProperty(get_fcprop(prop[1]),prop[0],pset,prop[1]+":"+prop[0]) + obj.addProperty(get_fcprop(prop[1]), prop[0], pset, prop[1] + ":" + prop[0]) FreeCAD.ActiveDocument.commitTransaction() @@ -239,7 +246,7 @@ def on_toggle_lock(checked=None, noconvert=False, setchecked=False): def on_open(): """What happens when opening an existing document""" - pass # TODO implement + pass # TODO implement def on_activate(): @@ -266,7 +273,7 @@ def on_activate(): def on_new(): """What happens when creating a new document""" - pass # TODO implement + pass # TODO implement def set_menu(locked=False): @@ -279,7 +286,7 @@ def set_menu(locked=False): wb = FreeCADGui.activeWorkbench() save_action = mw.findChild(QtGui.QAction, "Std_Save") if locked and "IFC_Save" in FreeCADGui.listCommands(): - if not hasattr(FreeCADGui,"IFC_WBManipulator"): + if not hasattr(FreeCADGui, "IFC_WBManipulator"): FreeCADGui.IFC_WBManipulator = IFC_WBManipulator() # we need to void the shortcut otherwise it keeps active # even if the command is not shown @@ -288,10 +295,10 @@ def set_menu(locked=False): FreeCADGui.addWorkbenchManipulator(FreeCADGui.IFC_WBManipulator) wb.reloadActive() else: - if hasattr(FreeCADGui,"IFC_saveshortcut"): + if hasattr(FreeCADGui, "IFC_saveshortcut"): save_action.setShortcut(FreeCADGui.IFC_saveshortcut) del FreeCADGui.IFC_saveshortcut - if hasattr(FreeCADGui,"IFC_WBManipulator"): + if hasattr(FreeCADGui, "IFC_WBManipulator"): FreeCADGui.removeWorkbenchManipulator(FreeCADGui.IFC_WBManipulator) del FreeCADGui.IFC_WBManipulator wb.reloadActive() @@ -338,9 +345,7 @@ def unlock_document(): children = [o for o in doc.Objects if not o.InList] project = None if children: - project = ifc_tools.create_document_object( - doc, filename=doc.IfcFilePath, silent=True - ) + project = ifc_tools.create_document_object(doc, filename=doc.IfcFilePath, silent=True) project.Group = children props = ["IfcFilePath", "Modified", "Proxy", "Schema"] props += [p for p in doc.PropertiesList if doc.getGroupOfProperty(p) == "IFC"] @@ -377,9 +382,7 @@ def lock_document(): children = project.OutListRecursive rest = [o for o in doc.Objects if o not in children and o != project] doc.openTransaction("Lock document") - ifc_tools.convert_document( - doc, filename=project.IfcFilePath, strategy=3, silent=True - ) + ifc_tools.convert_document(doc, filename=project.IfcFilePath, strategy=3, silent=True) ifcfile = doc.Proxy.ifcfile if rest: # 1b some objects are outside @@ -420,7 +423,7 @@ def lock_document(): doc.openTransaction("Lock document") objs = find_toplevel(doc.Objects) deletelist = [o.Name for o in doc.Objects] - #ifc_export.export_and_convert(objs, doc) + # ifc_export.export_and_convert(objs, doc) ifc_export.direct_conversion(objs, doc) for n in deletelist: if doc.getObject(n): @@ -458,9 +461,9 @@ def find_toplevel(objs): for parent in obj.InListRecursive: if parent in objs: # exception: The object is hosting another - if hasattr(parent,"Host") and parent.Host == obj: + if hasattr(parent, "Host") and parent.Host == obj: nobjs.append(obj) - elif hasattr(parent,"Hosts") and obj in parent.Hosts: + elif hasattr(parent, "Hosts") and obj in parent.Hosts: nobjs.append(obj) break else: @@ -486,14 +489,20 @@ def filter_out(objs): # only append groups that contain exportable objects nobjs.append(obj) else: - print("DEBUG: Filtering out",obj.Label) + print("DEBUG: Filtering out", obj.Label) elif obj.isDerivedFrom("App::Feature"): - if Draft.get_type(obj) in ("Dimension","LinearDimension","Layer","Text","DraftText"): + if Draft.get_type(obj) in ( + "Dimension", + "LinearDimension", + "Layer", + "Text", + "DraftText", + ): nobjs.append(obj) else: - print("DEBUG: Filtering out",obj.Label) + print("DEBUG: Filtering out", obj.Label) else: - print("DEBUG: Filtering out",obj.Label) + print("DEBUG: Filtering out", obj.Label) return nobjs @@ -503,6 +512,7 @@ def get_lock_status(): if not FreeCAD.GuiUp: return PARAMS.GetBool("SingleDoc") from PySide import QtGui + mw = FreeCADGui.getMainWindow() statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget") if hasattr(statuswidget, "lock_button"): @@ -515,12 +525,15 @@ def get_lock_status(): # https://github.com/FreeCAD/FreeCAD/pull/10933 class IFC_WBManipulator: def modifyMenuBar(self): - return [{"remove":"Std_Save"}, - {"remove":"Std_SaveAs"}, - {"insert":"IFC_Save", "menuItem":"Std_SaveCopy"}, - {"insert":"IFC_SaveAs", "menuItem":"Std_SaveCopy"}, - ] + return [ + {"remove": "Std_Save"}, + {"remove": "Std_SaveAs"}, + {"insert": "IFC_Save", "menuItem": "Std_SaveCopy"}, + {"insert": "IFC_SaveAs", "menuItem": "Std_SaveCopy"}, + ] + def modifyToolBars(self): - return [{"remove" : "Std_Save"}, - {"append" : "IFC_Save", "toolBar" : "File"}, - ] + return [ + {"remove": "Std_Save"}, + {"append": "IFC_Save", "toolBar": "File"}, + ] diff --git a/src/Mod/BIM/nativeifc/ifc_tools.py b/src/Mod/BIM/nativeifc/ifc_tools.py index 619c58419f..7c2cfd755d 100644 --- a/src/Mod/BIM/nativeifc/ifc_tools.py +++ b/src/Mod/BIM/nativeifc/ifc_tools.py @@ -51,6 +51,7 @@ try: import ifcopenshell.entity_instance except ImportError as e: import FreeCAD + FreeCAD.Console.PrintError( translate( "BIM", @@ -94,9 +95,7 @@ def create_document(document, filename=None, shapemode=0, strategy=0, silent=Fal return create_document_object(document, filename, shapemode, strategy, silent) -def create_document_object( - document, filename=None, shapemode=0, strategy=0, silent=False -): +def create_document_object(document, filename=None, shapemode=0, strategy=0, silent=False): """Creates a IFC document object in the given FreeCAD document. filename: If not given, a blank IFC document is created @@ -220,10 +219,7 @@ def create_ifcfile(): version = FreeCAD.Version() version = ".".join([str(v) for v in version[0:3]]) freecadorg = api_run( - "owner.add_organisation", - ifcfile, - identification="FreeCAD.org", - name="The FreeCAD project" + "owner.add_organisation", ifcfile, identification="FreeCAD.org", name="The FreeCAD project" ) application = api_run( "owner.add_application", @@ -294,9 +290,11 @@ def create_object(ifcentity, document, ifcfile, shapemode=0, objecttype=None): add_properties(obj, ifcfile, ifcentity, shapemode=shapemode) ifc_layers.add_layers(obj, ifcentity, ifcfile) if FreeCAD.GuiUp: - if ifcentity.is_a("IfcSpace") or\ - ifcentity.is_a("IfcOpeningElement") or\ - ifcentity.is_a("IfcAnnotation"): + if ( + ifcentity.is_a("IfcSpace") + or ifcentity.is_a("IfcOpeningElement") + or ifcentity.is_a("IfcAnnotation") + ): try: obj.ViewObject.DisplayMode = "Wireframe" except: @@ -325,9 +323,7 @@ def create_children( def create_child(parent, element): subresult = [] # do not create if a child with same stepid already exists - if element.id() not in [ - getattr(c, "StepId", 0) for c in get_parent_objects(parent) - ]: + if element.id() not in [getattr(c, "StepId", 0) for c in get_parent_objects(parent)]: doc = getattr(parent, "Document", parent) mode = getattr(parent, "ShapeMode", "Coin") child = create_object(element, doc, ifcfile, mode) @@ -336,26 +332,20 @@ def create_children( parent.Proxy.addObject(parent, child) if element.is_a("IfcSite"): # force-create contained buildings too if we just created a site - buildings = [ - o for o in get_children(child, ifcfile) if o.is_a("IfcBuilding") - ] + buildings = [o for o in get_children(child, ifcfile) if o.is_a("IfcBuilding")] for building in buildings: subresult.extend(create_child(child, building)) elif element.is_a("IfcOpeningElement"): # force-create contained windows too if we just created an opening windows = [ - o - for o in get_children(child, ifcfile) - if o.is_a() in ("IfcWindow", "IfcDoor") + o for o in get_children(child, ifcfile) if o.is_a() in ("IfcWindow", "IfcDoor") ] for window in windows: subresult.extend(create_child(child, window)) if recursive: subresult.extend( - create_children( - child, ifcfile, recursive, only_structure, assemblies - ) + create_children(child, ifcfile, recursive, only_structure, assemblies) ) return subresult @@ -420,9 +410,7 @@ def get_children( children.extend([rel.RelatedOpeningElement]) for rel in getattr(ifcentity, "HasFillings", []): children.extend([rel.RelatedBuildingElement]) - result = filter_elements( - children, ifcfile, expand=expand, spaces=True, assemblies=assemblies - ) + result = filter_elements(children, ifcfile, expand=expand, spaces=True, assemblies=assemblies) if ifctype: result = [r for r in result if r.is_a(ifctype)] return result @@ -474,7 +462,9 @@ def get_ifcfile(obj): project.Proxy.ifcfile = ifcfile return ifcfile else: - FreeCAD.Console.PrintError("Error: No IFC file attached to this project: "+project.Label) + FreeCAD.Console.PrintError( + "Error: No IFC file attached to this project: " + project.Label + ) return None @@ -550,7 +540,7 @@ def add_object(document, otype=None, oname="IfcObject"): if obj.ViewObject: obj.ViewObject.DisplayMode = "Flat Lines" elif otype == "dimension": - obj = Draft.make_dimension(FreeCAD.Vector(), FreeCAD.Vector(1,0,0)) + obj = Draft.make_dimension(FreeCAD.Vector(), FreeCAD.Vector(1, 0, 0)) obj.Proxy = ifc_objects.ifc_object(otype) obj.removeProperty("Diameter") obj.removeProperty("Distance") @@ -586,7 +576,7 @@ def add_object(document, otype=None, oname="IfcObject"): obj.ViewObject.ShowLabel = False obj.ViewObject.Proxy = ifc_viewproviders.ifc_vp_buildingpart(obj.ViewObject) for p in obj.PropertiesList: - if obj.getGroupOfProperty(p) in ["BuildingPart","IFC Attributes","Children"]: + if obj.getGroupOfProperty(p) in ["BuildingPart", "IFC Attributes", "Children"]: obj.removeProperty(p) obj.Proxy = ifc_objects.ifc_object(otype) else: # default case, standard IFC object @@ -596,9 +586,7 @@ def add_object(document, otype=None, oname="IfcObject"): return obj -def add_properties( - obj, ifcfile=None, ifcentity=None, links=False, shapemode=0, short=SHORT -): +def add_properties(obj, ifcfile=None, ifcentity=None, links=False, shapemode=0, short=SHORT): """Adds the properties of the given IFC object to a FreeCAD object""" if not ifcfile: @@ -644,11 +632,7 @@ def add_properties( if short and attr not in ("Class", "StepId"): continue attr_def = next((a for a in attr_defs if a.name() == attr), None) - data_type = ( - ifcopenshell.util.attribute.get_primitive_type(attr_def) - if attr_def - else None - ) + data_type = ifcopenshell.util.attribute.get_primitive_type(attr_def) if attr_def else None if attr == "Class": # main enum property, not saved to file if attr not in obj.PropertiesList: @@ -716,7 +700,7 @@ def add_properties( obj.addProperty("App::PropertyFloat", attr, "IFC", locked=True) if value is not None: # convert from list of 4 ints - value = value[0] + value[1]/60. + value[2]/3600. + value[3]/3600.e6 + value = value[0] + value[1] / 60.0 + value[2] / 3600.0 + value[3] / 3600.0e6 setattr(obj, attr, value) else: if attr not in obj.PropertiesList: @@ -742,7 +726,10 @@ def add_properties( # Try to get the source classification name if hasattr(cref, "ReferencedSource") and cref.ReferencedSource: - if hasattr(cref.ReferencedSource, "Name") and cref.ReferencedSource.Name: + if ( + hasattr(cref.ReferencedSource, "Name") + and cref.ReferencedSource.Name + ): classification_name += cref.ReferencedSource.Name + " " # Add the Identification if present @@ -751,7 +738,9 @@ def add_properties( classification_name = classification_name.strip() if classification_name: - obj.addProperty("App::PropertyString", "Classification", "IFC", locked=True) + obj.addProperty( + "App::PropertyString", "Classification", "IFC", locked=True + ) setattr(obj, "Classification", classification_name) break # Found the relevant one, stop # annotation properties @@ -764,7 +753,7 @@ def add_properties( obj.setPropertyStatus("CustomText", "Hidden") obj.setExpression("CustomText", "AxisTag") if "Length" not in obj.PropertiesList: - obj.addProperty("App::PropertyLength","Length","Axis", locked=True) + obj.addProperty("App::PropertyLength", "Length", "Axis", locked=True) if "Text" not in obj.PropertiesList: obj.addProperty("App::PropertyStringList", "Text", "Base", locked=True) obj.Text = [text.Literal] @@ -776,16 +765,20 @@ def add_properties( if "Placement" not in obj.PropertiesList: obj.addProperty("App::PropertyPlacement", "Placement", "Base", locked=True) if "Depth" not in obj.PropertiesList: - obj.addProperty("App::PropertyLength","Depth","SectionPlane", locked=True) + obj.addProperty("App::PropertyLength", "Depth", "SectionPlane", locked=True) obj.Placement = sectionplane[0] if len(sectionplane) > 3: obj.Depth = sectionplane[3] vobj = obj.ViewObject if vobj: if "DisplayLength" not in vobj.PropertiesList: - vobj.addProperty("App::PropertyLength","DisplayLength","SectionPlane", locked=True) + vobj.addProperty( + "App::PropertyLength", "DisplayLength", "SectionPlane", locked=True + ) if "DisplayHeight" not in vobj.PropertiesList: - vobj.addProperty("App::PropertyLength","DisplayHeight","SectionPlane", locked=True) + vobj.addProperty( + "App::PropertyLength", "DisplayHeight", "SectionPlane", locked=True + ) if len(sectionplane) > 1: vobj.DisplayLength = sectionplane[1] if len(sectionplane) > 2: @@ -916,11 +909,7 @@ def filter_elements(elements, ifcfile, expand=True, spaces=False, assemblies=Tru else: if elem.Representation.Representations: rep = elem.Representation.Representations[0] - if ( - rep.Items - and rep.Items[0].is_a() == "IfcPolyline" - and elem.IsDecomposedBy - ): + if rep.Items and rep.Items[0].is_a() == "IfcPolyline" and elem.IsDecomposedBy: # only use the decomposition and not the polyline # happens for multilayered walls exported by VectorWorks # the Polyline is the wall axis @@ -972,29 +961,21 @@ def set_attribute(ifcfile, element, attribute, value): if value and value.startswith("Ifc"): cmd = "root.reassign_class" FreeCAD.Console.PrintLog( - "Changing IFC class value: " - + element.is_a() - + " to " - + str(value) - + "\n" + "Changing IFC class value: " + element.is_a() + " to " + str(value) + "\n" ) product = api_run(cmd, ifcfile, product=element, ifc_class=value) # TODO fix attributes return product if attribute in ["RefLongitude", "RefLatitude"]: - c = [int(value)] - c.append(int((value - c[0]) * 60)) - c.append(int(((value - c[0]) * 60 - c[1]) * 60)) - c.append(int((((value - c[0]) * 60 - c[1]) * 60 - c[2]) * 1.e6)) - value = c + c = [int(value)] + c.append(int((value - c[0]) * 60)) + c.append(int(((value - c[0]) * 60 - c[1]) * 60)) + c.append(int((((value - c[0]) * 60 - c[1]) * 60 - c[2]) * 1.0e6)) + value = c cmd = "attribute.edit_attributes" attribs = {attribute: value} if hasattr(element, attribute): - if ( - attribute == "Name" - and getattr(element, attribute) is None - and value.startswith("_") - ): + if attribute == "Name" and getattr(element, attribute) is None and value.startswith("_"): # do not consider default FreeCAD names given to unnamed alements return False if differs(getattr(element, attribute, None), value): @@ -1003,7 +984,9 @@ def set_attribute(ifcfile, element, attribute, value): + str(attribute) + ": " + str(value) - + " (original value:" +str(getattr(element, attribute))+")" + + " (original value:" + + str(getattr(element, attribute)) + + ")" + "\n" ) api_run(cmd, ifcfile, product=element, attributes=attribs) @@ -1061,9 +1044,7 @@ def get_body_context_ids(ifcfile): body_contexts.extend( [ c.id() - for c in ifcfile.by_type( - "IfcGeometricRepresentationContext", include_subtypes=False - ) + for c in ifcfile.by_type("IfcGeometricRepresentationContext", include_subtypes=False) if c.ContextType == "Model" ] ) @@ -1143,14 +1124,12 @@ def set_placement(obj): if element.is_a("IfcGridAxis"): return set_axis_points(obj, element, ifcfile) # other cases of objects without ObjectPlacement? - print("DEBUG: object without ObjectPlacement",element) + print("DEBUG: object without ObjectPlacement", element) return False placement = FreeCAD.Placement(obj.Placement) placement.Base = FreeCAD.Vector(placement.Base).multiply(get_scale(ifcfile)) new_matrix = get_ios_matrix(placement) - old_matrix = ifcopenshell.util.placement.get_local_placement( - element.ObjectPlacement - ) + old_matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) # conversion from numpy array old_matrix = old_matrix.tolist() old_matrix = [[round(c, ROUND) for c in r] for r in old_matrix] @@ -1189,7 +1168,7 @@ def set_axis_points(obj, element, ifcfile): attributes={"Coordinates": tuple(p2)}, ) return True - print("DEBUG: unhandled axis type:",element.AxisCurve.is_a()) + print("DEBUG: unhandled axis type:", element.AxisCurve.is_a()) return False @@ -1247,7 +1226,7 @@ def aggregate(obj, parent, mode=None): objecttype = None if ifc_export.is_annotation(obj): product = ifc_export.create_annotation(obj, ifcfile) - if Draft.get_type(obj) in ["DraftText","Text"]: + if Draft.get_type(obj) in ["DraftText", "Text"]: objecttype = "text" elif "CreateSpreadsheet" in obj.PropertiesList: obj.Proxy.create_ifc(obj, ifcfile) @@ -1268,18 +1247,16 @@ def aggregate(obj, parent, mode=None): if FreeCAD.GuiUp: import FreeCADGui - autogroup = getattr( - getattr(FreeCADGui, "draftToolBar", None), "autogroup", None - ) + autogroup = getattr(getattr(FreeCADGui, "draftToolBar", None), "autogroup", None) if autogroup is not None: layer = FreeCAD.ActiveDocument.getObject(autogroup) if hasattr(layer, "StepId"): ifc_layers.add_to_layer(newobj, layer) # aggregate dependent objects for child in obj.InList: - if hasattr(child,"Host") and child.Host == obj: + if hasattr(child, "Host") and child.Host == obj: aggregate(child, newobj) - elif hasattr(child,"Hosts") and obj in child.Hosts: + elif hasattr(child, "Hosts") and obj in child.Hosts: aggregate(child, newobj) for child in getattr(obj, "Group", []): if newobj.IfcClass == "IfcGroup" and child in obj.Group: @@ -1316,10 +1293,10 @@ def get_ifctype(obj): if hasattr(obj, "Class"): if "ifc" in str(obj.Class).lower(): return obj.Class - if hasattr(obj,"IfcType") and obj.IfcType != "Undefined": - return "Ifc" + obj.IfcType.replace(" ","") + if hasattr(obj, "IfcType") and obj.IfcType != "Undefined": + return "Ifc" + obj.IfcType.replace(" ", "") dtype = Draft.getType(obj) - if dtype in ["App::Part","Part::Compound","Array"]: + if dtype in ["App::Part", "Part::Compound", "Array"]: return "IfcElementAssembly" if dtype in ["App::DocumentObjectGroup"]: return "IfcGroup" @@ -1378,25 +1355,15 @@ def create_relationship(old_obj, obj, parent, element, ifcfile, mode=None): api_run("spatial.unassign_container", ifcfile, product=parent_element) # IFC objects can be part of multiple groups but we do the FreeCAD way here # and remove from any previous group - for assignment in getattr(element,"HasAssignments",[]): + for assignment in getattr(element, "HasAssignments", []): if assignment.is_a("IfcRelAssignsToGroup"): if element in assignment.RelatedObjects: oldgroup = assignment.RelatingGr try: - api_run( - "group.unassign_group", - ifcfile, - products=[element], - group=oldgroup - ) + api_run("group.unassign_group", ifcfile, products=[element], group=oldgroup) except: # older version of IfcOpenShell - api_run( - "group.unassign_group", - ifcfile, - product=element, - group=oldgroup - ) + api_run("group.unassign_group", ifcfile, product=element, group=oldgroup) try: uprel = api_run("group.assign_group", ifcfile, products=[element], group=parent_element) except: @@ -1451,9 +1418,7 @@ def create_relationship(old_obj, obj, parent, element, ifcfile, mode=None): old_obj.Document.removeObject(tempobj.Name) if tempface: old_obj.Document.removeObject(tempface.Name) - api_run( - "void.add_opening", ifcfile, opening=opening, element=parent_element - ) + api_run("void.add_opening", ifcfile, opening=opening, element=parent_element) api_run("void.add_filling", ifcfile, opening=opening, element=element) # windows must also be part of a spatial container try: @@ -1496,11 +1461,10 @@ def create_relationship(old_obj, obj, parent, element, ifcfile, mode=None): relating_object=container, ) # case 4: void element - elif (parent_element.is_a("IfcElement") and element.is_a("IfcOpeningElement"))\ - or (mode == "opening"): - uprel = api_run( - "void.add_opening", ifcfile, opening=element, element=parent_element - ) + elif (parent_element.is_a("IfcElement") and element.is_a("IfcOpeningElement")) or ( + mode == "opening" + ): + uprel = api_run("void.add_opening", ifcfile, opening=element, element=parent_element) # case 3: element aggregated inside other element elif element.is_a("IfcProduct"): try: @@ -1578,7 +1542,7 @@ def migrate_schema(ifcfile, schema): return newfile, table -def remove_ifc_element(obj,delete_obj=False): +def remove_ifc_element(obj, delete_obj=False): """removes the IFC data associated with an object. If delete_obj is True, the FreeCAD object is also deleted""" @@ -1600,9 +1564,7 @@ def get_orphan_elements(ifcfile): products = ifcfile.by_type("IfcProduct") products = [p for p in products if not p.Decomposes] products = [p for p in products if not getattr(p, "ContainedInStructure", [])] - products = [ - p for p in products if not hasattr(p, "VoidsElements") or not p.VoidsElements - ] + products = [p for p in products if not hasattr(p, "VoidsElements") or not p.VoidsElements] # add control elements proj = ifcfile.by_type("IfcProject")[0] for rel in getattr(proj, "Declares", []): diff --git a/src/Mod/BIM/nativeifc/ifc_tree.py b/src/Mod/BIM/nativeifc/ifc_tree.py index 4a704767a2..4264e1f0b7 100644 --- a/src/Mod/BIM/nativeifc/ifc_tree.py +++ b/src/Mod/BIM/nativeifc/ifc_tree.py @@ -166,9 +166,7 @@ def show_properties(current, previous): # props = [p for p in props if isfloat(str(getattr(elt,p)))] props = [p for p in props if not str(getattr(elt, p)).startswith("#")] props = [p for p in props if not str(getattr(elt, p)).startswith("(")] - props = [ - p for p in props if p not in ["Position", "LayerAssignments", "StyledByItem"] - ] + props = [p for p in props if p not in ["Position", "LayerAssignments", "StyledByItem"]] proptree = box.children()[0].itemAt(0).widget() proptree.clear() proptree.setHorizontalHeaderLabels(["Property", "Value"]) @@ -191,9 +189,7 @@ def show_properties(current, previous): position = FreeCAD.Vector(elt.Position.Location.Coordinates) axis = FreeCAD.Vector(elt.Position.Axis.DirectionRatios) xref = FreeCAD.Vector(elt.Position.RefDirection.DirectionRatios) - rotation = FreeCAD.Rotation(axis, xref, FreeCAD.Vector(), "ZXY").toEulerAngles( - "XYZ" - ) + rotation = FreeCAD.Rotation(axis, xref, FreeCAD.Vector(), "ZXY").toEulerAngles("XYZ") rotation = FreeCAD.Vector(rotation) for c in ["x", "y", "z"]: r1 = QtWidgets.QTableWidgetItem("Position " + c.upper()) diff --git a/src/Mod/BIM/nativeifc/ifc_types.py b/src/Mod/BIM/nativeifc/ifc_types.py index ce95c8222d..0001c0381c 100644 --- a/src/Mod/BIM/nativeifc/ifc_types.py +++ b/src/Mod/BIM/nativeifc/ifc_types.py @@ -61,11 +61,11 @@ def load_types(prj_obj): def process_object(obj): """Recursively process an object and its children""" # Check if this object has IFC data and can have types - if hasattr(obj, 'StepId') and obj.StepId: + if hasattr(obj, "StepId") and obj.StepId: show_type(obj) # Process children recursively - if hasattr(obj, 'Group'): + if hasattr(obj, "Group"): for child in obj.Group: process_object(child) @@ -75,7 +75,7 @@ def load_types(prj_obj): else: # Handle document case - process all IFC objects in the document for obj in prj_obj.Objects: - if hasattr(obj, 'StepId') and obj.StepId: + if hasattr(obj, "StepId") and obj.StepId: show_type(obj) @@ -111,10 +111,11 @@ def convert_to_type(obj, keep_object=False): if FreeCAD.GuiUp and ask_again: import FreeCADGui + dlg = FreeCADGui.PySideUic.loadUi(":/ui/dialogConvertType.ui") original_text = dlg.label.text() - dlg.label.setText(original_text.replace("%1", obj.Class+"Type")) + dlg.label.setText(original_text.replace("%1", obj.Class + "Type")) # Set the initial state of the checkbox from the "always keep" preference dlg.checkKeepObject.setChecked(always_keep) @@ -139,7 +140,9 @@ def convert_to_type(obj, keep_object=False): if not element or not ifcfile or not project: return type_element = ifc_tools.api_run("root.copy_class", ifcfile, product=element) - type_element = ifc_tools.api_run("root.reassign_class", ifcfile, product=type_element, ifc_class=obj.Class+"Type") + type_element = ifc_tools.api_run( + "root.reassign_class", ifcfile, product=type_element, ifc_class=obj.Class + "Type" + ) type_obj = ifc_tools.create_object(type_element, obj.Document, ifcfile) if keep_object: obj.Type = type_obj @@ -160,8 +163,8 @@ def edit_type(obj): if obj.Type: # verify the type is compatible -ex IFcWall in IfcWallType if obj.Type.Class != element.is_a() + "Type": - t = translate("BIM","Error: Incompatible type") - FreeCAD.Console.PrintError(obj.Label+": "+t+": "+obj.Type.Class+"\n") + t = translate("BIM", "Error: Incompatible type") + FreeCAD.Console.PrintError(obj.Label + ": " + t + ": " + obj.Type.Class + "\n") obj.Type = None return # change type @@ -172,10 +175,8 @@ def edit_type(obj): if rel.RelatingType == new_type: return # assign the new type - ifc_tools.api_run("type.assign_type", - ifcfile, - related_objects=[element], - relating_type=new_type + ifc_tools.api_run( + "type.assign_type", ifcfile, related_objects=[element], relating_type=new_type ) elif typerel: # TODO remove type? diff --git a/src/Mod/BIM/nativeifc/ifc_viewproviders.py b/src/Mod/BIM/nativeifc/ifc_viewproviders.py index ff6d22bc7d..205e99bed5 100644 --- a/src/Mod/BIM/nativeifc/ifc_viewproviders.py +++ b/src/Mod/BIM/nativeifc/ifc_viewproviders.py @@ -90,7 +90,7 @@ class ifc_vp_object: from . import ifc_types from PySide import QtGui # lazy import - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return icon = QtGui.QIcon(":/icons/IFC.svg") @@ -100,10 +100,7 @@ class ifc_vp_object: # IFC actions actions = [] if element.is_a("IfcSpatialElement"): - if ( - FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") - == vobj.Object - ): + if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") == vobj.Object: t = translate("BIM", "Deactivate Container") else: t = translate("BIM", "Make Active Container") @@ -324,6 +321,7 @@ class ifc_vp_object: """Add an object to the view provider by d&d""" from PySide import QtCore # lazy import + # delay the action to prevent the object to be deleted # before the end of the drop QtCore.QTimer.singleShot(100, lambda: self.onDrop(incoming_object)) @@ -332,6 +330,7 @@ class ifc_vp_object: """Delayed action to be taken when dropping an object""" from . import ifc_tools # lazy import + ifc_tools.aggregate(incoming_object, self.Object) if self.hasChildren(self.Object): self.expandChildren(self.Object) @@ -339,15 +338,10 @@ class ifc_vp_object: def activate(self): """Marks this container as active""" - if ( - FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") - == self.Object - ): + if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") == self.Object: FreeCADGui.ActiveDocument.ActiveView.setActiveObject("NativeIFC", None) else: - FreeCADGui.ActiveDocument.ActiveView.setActiveObject( - "NativeIFC", self.Object - ) + FreeCADGui.ActiveDocument.ActiveView.setActiveObject("NativeIFC", self.Object) def createGroup(self): """Creates a group under this object""" @@ -402,11 +396,11 @@ class ifc_vp_object: if not hasattr(self, "Object"): return from . import ifc_types + ifc_types.convert_to_type(self.Object) self.Object.Document.recompute() - class ifc_vp_document(ifc_vp_object): """View provider for the IFC document object""" @@ -423,7 +417,7 @@ class ifc_vp_document(ifc_vp_object): from PySide import QtGui # lazy import - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return ifc_menu = super().setupContextMenu(vobj, menu) @@ -576,17 +570,13 @@ class ifc_vp_material: im = QtGui.QImage(48, 48, QtGui.QImage.Format_ARGB32) im.fill(QtCore.Qt.transparent) pt = QtGui.QPainter(im) - pt.setPen( - QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap) - ) + pt.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) gradient = QtGui.QLinearGradient(0, 0, 48, 48) gradient.setColorAt(0, matcolor) gradient.setColorAt(1, darkcolor) pt.setBrush(QtGui.QBrush(gradient)) pt.drawEllipse(6, 6, 36, 36) - pt.setPen( - QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap) - ) + pt.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap)) pt.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern)) pt.drawEllipse(12, 12, 12, 12) pt.end() @@ -606,7 +596,7 @@ class ifc_vp_material: from . import ifc_psets from PySide import QtGui # lazy import - if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench': + if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": return icon = QtGui.QIcon(":/icons/IFC.svg") @@ -628,7 +618,7 @@ class ifc_vp_buildingpart(ifc_vp_object, ArchBuildingPart.ViewProviderBuildingPa """A vp that inherits the Arch BuildingPart vp, but keeps aggregating properties of ifc vp""" def __init__(self, vobj): - ArchBuildingPart.ViewProviderBuildingPart.__init__(self,vobj) + ArchBuildingPart.ViewProviderBuildingPart.__init__(self, vobj) def overlay(icon1, icon2): @@ -686,8 +676,8 @@ def get_icon(vp): if hasattr(vp, "Object"): if hasattr(vp.Object, "IfcClass"): - rclass = vp.Object.IfcClass.replace("StandardCase","") - rclass = vp.Object.IfcClass.replace("Type","") + rclass = vp.Object.IfcClass.replace("StandardCase", "") + rclass = vp.Object.IfcClass.replace("Type", "") ifcicon = ":/icons/IFC/" + rclass + ".svg" if QtCore.QFile.exists(ifcicon): if getattr(vp, "ifcclass", "") != rclass: diff --git a/src/Mod/BIM/utils/buildPsets.py b/src/Mod/BIM/utils/buildPsets.py index 33a3a1c2b7..56c78f85e6 100644 --- a/src/Mod/BIM/utils/buildPsets.py +++ b/src/Mod/BIM/utils/buildPsets.py @@ -23,8 +23,8 @@ # *************************************************************************** """This script retrieves a list of standard property sets from the IFC4 official - documentation website and stores them into 1) a pset_definitions.csv and 2) - a qto_definitions.csv files in the directory ../Presets.""" +documentation website and stores them into 1) a pset_definitions.csv and 2) +a qto_definitions.csv files in the directory ../Presets.""" import os from urllib.request import urlopen @@ -44,6 +44,7 @@ QTO_TYPES = { "Q_WEIGHT": "IfcQuantityWeight", } + class PropertyDefHandler(xml.sax.ContentHandler): "A XML handler to process pset definitions" @@ -100,13 +101,12 @@ class PropertyDefHandler(xml.sax.ContentHandler): self.currenttype = None - # MAIN print("Getting psets xml definitions…") -with open("psd.zip","wb") as f: +with open("psd.zip", "wb") as f: u = urlopen(URL) p = u.read() f.write(p) @@ -116,9 +116,9 @@ print("Reading xml definitions…") psets = [] qtos = [] -with ZipFile("psd.zip", 'r') as z: +with ZipFile("psd.zip", "r") as z: for entry in z.namelist(): - print("Parsing",entry) + print("Parsing", entry) xml_data = z.read(entry).decode(encoding="utf-8") handler = PropertyDefHandler(entry) xml.sax.parseString(xml_data, handler) diff --git a/src/Mod/BIM/utils/ifctree.py b/src/Mod/BIM/utils/ifctree.py index a368680352..fc512d448d 100644 --- a/src/Mod/BIM/utils/ifctree.py +++ b/src/Mod/BIM/utils/ifctree.py @@ -43,7 +43,6 @@ from PySide import QtWidgets class ViewProvider: - """A simple view provider to gather children""" def __init__(self, vobj):