Download src/Mod/BIM/ArchBuildingPart.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 51.1 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/ArchBuildingPart.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/BIM/ArchBuildingPart.py
-
curl -L -o ArchBuildingPart.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/ArchBuildingPart.py
51.1 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * * | |
| # * Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net> * | |
| # * * | |
| # * This file is part of FreeCAD. * | |
| # * * | |
| # * FreeCAD is free software: you can redistribute it and/or modify it * | |
| # * under the terms of the GNU Lesser General Public License as * | |
| # * published by the Free Software Foundation, either version 2.1 of the * | |
| # * License, or (at your option) any later version. * | |
| # * * | |
| # * FreeCAD is distributed in the hope that it will be useful, but * | |
| # * WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | |
| # * Lesser General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Lesser General Public * | |
| # * License along with FreeCAD. If not, see * | |
| # * <https://www.gnu.org/licenses/>. * | |
| # * * | |
| # *************************************************************************** | |
| __title__ = "FreeCAD Arch BuildingPart" | |
| __author__ = "Yorik van Havre" | |
| __url__ = "https://www.freecad.org" | |
| ## @package ArchBuildingPart | |
| # \ingroup ARCH | |
| # \brief The BuildingPart object and tools | |
| # | |
| # This module provides tools to build BuildingPart objects. | |
| # BuildingParts are used to group different Arch objects | |
| import os | |
| import tempfile | |
| import FreeCAD | |
| import Arch | |
| import ArchCommands | |
| import ArchIFC | |
| import Draft | |
| import DraftVecUtils | |
| from draftutils import params | |
| if FreeCAD.GuiUp: | |
| from PySide.QtCore import QT_TRANSLATE_NOOP | |
| import FreeCADGui | |
| from draftutils.translate import translate | |
| import draftutils.units as units | |
| else: | |
| # \cond | |
| def translate(ctxt, txt): | |
| return txt | |
| def QT_TRANSLATE_NOOP(ctxt, txt): | |
| return txt | |
| # \endcond | |
| unicode = str | |
| # fmt: off | |
| BuildingTypes = ['Undefined', | |
| 'Agricultural - Barn', | |
| 'Agricultural - Chicken coop or chickenhouse', | |
| 'Agricultural - Cow-shed', | |
| 'Agricultural - Farmhouse', | |
| 'Agricultural - Granary', | |
| 'Agricultural - Greenhouse', | |
| 'Agricultural - Hayloft', | |
| 'Agricultural - Pigpen or sty', | |
| 'Agricultural - Root cellar', | |
| 'Agricultural - Shed', | |
| 'Agricultural - Silo', | |
| 'Agricultural - Stable', | |
| 'Agricultural - Storm cellar', | |
| 'Agricultural - Well house', | |
| 'Agricultural - Underground pit', | |
| 'Commercial - Automobile repair shop', | |
| 'Commercial - Bank', | |
| 'Commercial - Car wash', | |
| 'Commercial - Convention center', | |
| 'Commercial - Forum', | |
| 'Commercial - Gas station', | |
| 'Commercial - Hotel', | |
| 'Commercial - Market', | |
| 'Commercial - Market house', | |
| 'Commercial - Skyscraper', | |
| 'Commercial - Shop', | |
| 'Commercial - Shopping mall', | |
| 'Commercial - Supermarket', | |
| 'Commercial - Warehouse', | |
| 'Commercial - Restaurant', | |
| 'Residential - Apartment block', | |
| 'Residential - Asylum', | |
| 'Residential - Condominium', | |
| 'Residential - Dormitory', | |
| 'Residential - Duplex', | |
| 'Residential - House', | |
| 'Residential - Nursing home', | |
| 'Residential - Townhouse', | |
| 'Residential - Villa', | |
| 'Residential - Bungalow', | |
| 'Educational - Archive', | |
| 'Educational - College classroom building', | |
| 'Educational - College gymnasium', | |
| 'Educational - College students union', | |
| 'Educational - School', | |
| 'Educational - Library', | |
| 'Educational - Museum', | |
| 'Educational - Art gallery', | |
| 'Educational - Theater', | |
| 'Educational - Amphitheater', | |
| 'Educational - Concert hall', | |
| 'Educational - Cinema', | |
| 'Educational - Opera house', | |
| 'Educational - Boarding school', | |
| 'Government - Capitol', | |
| 'Government - City hall', | |
| 'Government - Consulate', | |
| 'Government - Courthouse', | |
| 'Government - Embassy', | |
| 'Government - Fire station', | |
| 'Government - Meeting house', | |
| 'Government - Moot hall', | |
| 'Government - Palace', | |
| 'Government - Parliament', | |
| 'Government - Police station', | |
| 'Government - Post office', | |
| 'Government - Prison', | |
| 'Industrial - Brewery', | |
| 'Industrial - Factory', | |
| 'Industrial - Foundry', | |
| 'Industrial - Power plant', | |
| 'Industrial - Mill', | |
| 'Military - Arsenal', | |
| 'Military -Barracks', | |
| 'Parking - Boathouse', | |
| 'Parking - Garage', | |
| 'Parking - Hangar', | |
| 'Storage - Silo', | |
| 'Storage - Hangar', | |
| 'Religious - Church', | |
| 'Religious - Basilica', | |
| 'Religious - Cathedral', | |
| 'Religious - Chapel', | |
| 'Religious - Oratory', | |
| 'Religious - Martyrium', | |
| 'Religious - Mosque', | |
| 'Religious - Mihrab', | |
| 'Religious - Surau', | |
| 'Religious - Imambargah', | |
| 'Religious - Monastery', | |
| 'Religious - Mithraeum', | |
| 'Religious - Fire temple', | |
| 'Religious - Shrine', | |
| 'Religious - Synagogue', | |
| 'Religious - Temple', | |
| 'Religious - Pagoda', | |
| 'Religious - Gurdwara', | |
| 'Religious - Hindu temple', | |
| 'Transport - Airport terminal', | |
| 'Transport - Bus station', | |
| 'Transport - Metro station', | |
| 'Transport - Taxi station', | |
| 'Transport - Railway station', | |
| 'Transport - Signal box', | |
| 'Transport - Lighthouse', | |
| 'Infrastructure - Data centre', | |
| 'Power station - Fossil-fuel power station', | |
| 'Power station - Nuclear power plant', | |
| 'Power station - Geothermal power', | |
| 'Power station - Biomass-fuelled power plant', | |
| 'Power station - Waste heat power plant', | |
| 'Power station - Renewable energy power station', | |
| 'Power station - Atomic energy plant', | |
| 'Other - Apartment', | |
| 'Other - Clinic', | |
| 'Other - Community hall', | |
| 'Other - Eatery', | |
| 'Other - Folly', | |
| 'Other - Food court', | |
| 'Other - Hospice', | |
| 'Other - Hospital', | |
| 'Other - Hut', | |
| 'Other - Bathhouse', | |
| 'Other - Workshop', | |
| 'Other - World trade centre' | |
| ] | |
| # fmt: on | |
| class BuildingPart(ArchIFC.IfcProduct): | |
| "The BuildingPart object" | |
| def __init__(self, obj): | |
| obj.Proxy = self | |
| self.Type = "BuildingPart" | |
| obj.addExtension("App::GroupExtensionPython") | |
| # obj.addExtension('App::OriginGroupExtensionPython') | |
| self.setProperties(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, | |
| ) | |
| 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.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, | |
| ) | |
| 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, | |
| ) | |
| if not "Description" in pl: | |
| 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, | |
| ) | |
| if not "Shape" in pl: | |
| 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) | |
| 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.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, | |
| ) | |
| def onDocumentRestored(self, obj): | |
| self.setProperties(obj) | |
| def dumps(self): | |
| return None | |
| def loads(self, state): | |
| self.Type = "BuildingPart" | |
| def onBeforeChange(self, obj, prop): | |
| if prop == "Placement": | |
| self.oldPlacement = FreeCAD.Placement(obj.Placement) | |
| def onChanged(self, obj, prop): | |
| import math | |
| ArchIFC.IfcProduct.onChanged(self, obj, prop) | |
| # clean svg cache if needed | |
| if prop in ["Placement", "Group"]: | |
| self.svgcache = None | |
| self.shapecache = None | |
| if (prop == "Height" or prop == "HeightPropagate") and obj.Height.Value: | |
| self.touchChildren(obj) | |
| elif prop == "Placement": | |
| if hasattr(self, "oldPlacement") and self.oldPlacement != obj.Placement: | |
| deltap = obj.Placement.Base.sub(self.oldPlacement.Base) | |
| if deltap.Length == 0: | |
| deltap = None | |
| deltar = obj.Placement.Rotation * self.oldPlacement.Rotation.inverted() | |
| if deltar.Angle < 0.0001: | |
| deltar = None | |
| for child in self.getMovableChildren(obj): | |
| if deltar: | |
| child.Placement.rotate( | |
| self.oldPlacement.Base, | |
| deltar.Axis, | |
| math.degrees(deltar.Angle), | |
| comp=True, | |
| ) | |
| if deltap: | |
| child.Placement.move(deltap) | |
| def execute(self, obj): | |
| "gather all the child shapes into a compound" | |
| pl = obj.Placement | |
| 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)) | |
| obj.Shape = Part.makeCompound(f) | |
| # print("faces after compound:",len(obj.Shape.Faces)) | |
| # print("recomputing ",obj.Label) | |
| else: | |
| obj.Shape = Part.makeCompound(shapes) | |
| obj.Placement = pl | |
| obj.Area = self.getArea(obj) | |
| obj.MaterialsTable = materialstable | |
| if obj.ViewObject: | |
| # update the autogroup box if needed | |
| 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"): | |
| result.append(child) | |
| return result | |
| def getArea(self, obj): | |
| "computes the area of this floor by adding its inner spaces" | |
| area = 0 | |
| if hasattr(obj, "Group"): | |
| for child in obj.Group: | |
| if (Draft.get_type(child) in ["Space", "BuildingPart"]) and hasattr( | |
| child, "IfcType" | |
| ): | |
| area += child.Area.Value | |
| return area | |
| def getShapes(self, obj): | |
| "recursively get the shapes of objects inside this BuildingPart" | |
| shapes = [] | |
| solidindex = 0 | |
| 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: | |
| shapes.append(child.Shape) | |
| for solid in child.Shape.Solids: | |
| matname = "Undefined" | |
| if hasattr(child, "Material") and child.Material: | |
| matname = child.Material.Name | |
| if matname in materialstable: | |
| materialstable[matname] = ( | |
| materialstable[matname] + "," + str(solidindex) | |
| ) | |
| else: | |
| materialstable[matname] = str(solidindex) | |
| solidindex += 1 | |
| 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 o.Zone == obj: | |
| g.append(o) | |
| return g | |
| def touchChildren(self, obj): | |
| "Touches all descendents where applicable" | |
| g = [] | |
| if hasattr(obj, "Group"): | |
| g = obj.Group | |
| elif Draft.getType(obj) in ["Wall", "Structure"]: | |
| g = obj.Additions | |
| for child in g: | |
| if Draft.getType(child) in ["Wall", "Structure"]: | |
| if not child.Height.Value: | |
| 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"]: | |
| self.touchChildren(child) | |
| def addObject(self, obj, child): | |
| "Adds an object to the group of this BuildingPart" | |
| if not child in obj.Group: | |
| g = obj.Group | |
| g.append(child) | |
| obj.Group = g | |
| 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: | |
| abb = obj.ViewObject.Proxy.autobbox | |
| cbb = child.Shape.BoundBox | |
| if abb.isValid(): | |
| if not cbb.isValid(): | |
| FreeCAD.ActiveDocument.recompute() | |
| if not cbb.isValid(): | |
| cbb = FreeCAD.BoundBox() | |
| for v in child.Shape.Vertexes: | |
| print(v.Point) | |
| cbb.add(v.Point) | |
| if cbb.isValid() and abb.isInside(cbb): | |
| self.addObject(obj, child) | |
| return True | |
| return False | |
| class ViewProviderBuildingPart: | |
| "A View Provider for the BuildingPart object" | |
| def __init__(self, vobj): | |
| if vobj: | |
| vobj.addExtension("Gui::ViewProviderGroupExtensionPython") | |
| vobj.Proxy = self | |
| self.setProperties(vobj) | |
| vobj.ShapeColor = ArchCommands.getDefaultColor("Helpers") | |
| self.Object = vobj.Object | |
| 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.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, | |
| ) | |
| 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) | |
| ) | |
| if not "ShowLevel" in pl: | |
| 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, | |
| ) | |
| 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, | |
| ) | |
| 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.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.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" | |
| ) | |
| if not "DiffuseColor" in pl: | |
| 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.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, | |
| ) | |
| 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) | |
| 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, | |
| ) | |
| 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.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, | |
| ) | |
| 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) | |
| # 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, | |
| ) | |
| 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.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.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.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.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, | |
| ) | |
| 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.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, | |
| ) | |
| # 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, | |
| ) | |
| if not "AutogroupBox" in pl: | |
| 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, | |
| ) | |
| 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, | |
| ) | |
| def onDocumentRestored(self, vobj): | |
| self.setProperties(vobj) | |
| def getIcon(self): | |
| import Arch_rc | |
| 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"): | |
| from nativeifc import ifc_viewproviders | |
| return ifc_viewproviders.get_icon(self) | |
| return ":/icons/Arch_BuildingPart_Tree.svg" | |
| 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) | |
| self.dst = coin.SoDrawStyle() | |
| 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). | |
| lin = coin.SoType.fromName("SoBrepEdgeSet").createInstance() | |
| if lin: | |
| lin.coordIndex.setValues([0, 1, -1, 2, 3, -1, 4, 5, -1]) | |
| self.sep.addChild(lin) | |
| self.bbox = coin.SoSwitch() | |
| self.bbox.whichChild = -1 | |
| bboxsep = coin.SoSeparator() | |
| self.bbox.addChild(bboxsep) | |
| drawstyle = coin.SoDrawStyle() | |
| drawstyle.style = coin.SoDrawStyle.LINES | |
| drawstyle.lineWidth = 3 | |
| 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] | |
| ) | |
| bboxsep.addChild(lin) | |
| self.sep.addChild(self.bbox) | |
| self.tra = coin.SoTransform() | |
| self.tra.rotation.setValue(FreeCAD.Rotation(0, 0, 90).Q) | |
| self.sep.addChild(self.tra) | |
| self.fon = coin.SoFont() | |
| self.sep.addChild(self.fon) | |
| self.txt = coin.SoAsciiText() | |
| 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") | |
| self.setProperties(vobj) | |
| return | |
| def getDisplayModes(self, vobj): | |
| return ["Default"] | |
| def getDefaultDisplayMode(self): | |
| return "Default" | |
| def setDisplayMode(self, mode): | |
| return mode | |
| def updateData(self, obj, prop): | |
| 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 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") | |
| elif prop == "Group": | |
| self.onChanged(obj.ViewObject, "ChildrenOverride") | |
| elif prop == "Label": | |
| 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): | |
| colors.extend(child.ViewObject.DiffuseColor) | |
| else: | |
| 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): | |
| # print(vobj.Object.Label," - ",prop) | |
| if prop == "ShapeColor": | |
| if hasattr(vobj, "ShapeColor"): | |
| l = vobj.ShapeColor | |
| self.mat.diffuseColor.setValue([l[0], l[1], l[2]]) | |
| elif prop == "LineWidth": | |
| if hasattr(vobj, "LineWidth"): | |
| self.dst.lineWidth = vobj.LineWidth | |
| elif prop == "FontName": | |
| 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") | |
| ): | |
| 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]) | |
| 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], | |
| ] | |
| ) | |
| 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") | |
| ): | |
| 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) | |
| txt = "" | |
| if vobj.ShowLabel: | |
| txt += vobj.Object.Label | |
| if vobj.ShowLevel: | |
| if txt: | |
| txt += " " | |
| if z >= 0: | |
| txt += "+" | |
| if vobj.OverrideUnit: | |
| u = vobj.OverrideUnit | |
| else: | |
| u = q.getUserPreferred()[2] | |
| try: | |
| 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}" | |
| 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 = 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", | |
| ] | |
| 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") | |
| ): | |
| 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"): | |
| o.ViewObject.Lighting = "One side" | |
| self.clip = coin.SoClipPlane() | |
| self.clip.on.setValue(True) | |
| 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 | |
| norm = norm.negative() | |
| marg = 1 | |
| 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) | |
| self.clip.plane.setValue(plane) | |
| sg.insertChild(self.clip, 0) | |
| else: | |
| 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"): | |
| 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"): | |
| vobj.CutView = False | |
| elif prop == "SaveInventor": | |
| self.writeInventor(vobj.Object) | |
| 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]) | |
| self.autobbox.move(vobj.Object.Placement.Base) | |
| pts = [list(self.autobbox.getPoint(i)) for i in range(8)] | |
| self.bbco.point.setValues(pts) | |
| self.bbox.whichChild = 0 | |
| else: | |
| self.autobbox = None | |
| self.bbox.whichChild = -1 | |
| 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] | |
| ] | |
| def onDelete(self, vobj, subelements): | |
| if self.clip: | |
| sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() | |
| sg.removeChild(self.clip) | |
| self.clip = None | |
| for o in Draft.get_group_contents(vobj.Object.Group, walls=True): | |
| if hasattr(o.ViewObject, "Lighting"): | |
| o.ViewObject.Lighting = "Two side" | |
| return True | |
| def setEdit(self, vobj, mode): | |
| # mode == 1 if Transform is selected in the Tree view context menu. | |
| # mode == 2 has been added for consistency. | |
| if mode == 1 or mode == 2: | |
| return None | |
| # For some reason mode is always 0 if the object is double-clicked in | |
| # the Tree view. Using FreeCADGui.getUserEditMode() as a workaround. | |
| if FreeCADGui.getUserEditMode() in ("Transform", "Cutting"): | |
| return None | |
| self.activate() | |
| return False # Return `False` as we don't want to enter edit mode. | |
| def unsetEdit(self, vobj, mode): | |
| if mode == 1 or mode == 2: | |
| return None | |
| if FreeCADGui.getUserEditMode() in ("Transform", "Cutting"): | |
| return None | |
| return True | |
| def setupContextMenu(self, vobj, menu): | |
| from PySide import QtCore, QtGui | |
| import Draft_rc | |
| if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": | |
| return | |
| if (not hasattr(vobj, "DoubleClickActivates")) or vobj.DoubleClickActivates: | |
| menuTxt = translate("Arch", "Active") | |
| actionActivate = QtGui.QAction(menuTxt, menu) | |
| actionActivate.setCheckable(True) | |
| if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") == self.Object: | |
| actionActivate.setChecked(True) | |
| else: | |
| actionActivate.setChecked(False) | |
| 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 | |
| ) | |
| 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) | |
| menu.addAction(actionWriteCamera) | |
| 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) | |
| menu.addAction(actionReorder) | |
| 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.gui_utils import toggle_working_plane | |
| vobj = self.Object.ViewObject | |
| 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: | |
| print("Deactivating working plane from: ", self.Object.Label) | |
| FreeCADGui.Selection.clearSelection() | |
| def setWorkingPlane(self, restore=False): | |
| vobj = self.Object.ViewObject | |
| import WorkingPlane | |
| wp = WorkingPlane.get_working_plane(update=False) | |
| autoclip = False | |
| if hasattr(vobj, "AutoCutView"): | |
| autoclip = vobj.AutoCutView | |
| if restore: | |
| if wp.label.rstrip("*") == self.Object.Label: | |
| prev_data = wp._previous() | |
| if prev_data: | |
| prev_label = prev_data.get("label", "").rstrip("*") | |
| prev_obj = None | |
| for obj in FreeCAD.ActiveDocument.Objects: | |
| if hasattr(obj, "Label") and obj.Label == prev_label: | |
| prev_obj = obj | |
| break | |
| if prev_obj: | |
| # check in which context we need to set the active object | |
| context = "Arch" | |
| obj_type = Draft.getType(prev_obj) | |
| if obj_type == "IfcBuildingStorey": | |
| context = "NativeIFC" | |
| FreeCADGui.ActiveDocument.ActiveView.setActiveObject(context, prev_obj) | |
| print(f"Set active object to: {prev_obj.Label} (context: {context})") | |
| if autoclip: | |
| vobj.CutView = False | |
| else: | |
| wp.align_to_selection() | |
| if autoclip: | |
| vobj.CutView = True | |
| def writeCamera(self): | |
| if hasattr(self, "Object"): | |
| from pivy import coin | |
| n = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() | |
| 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): | |
| cdata.append(n.height.getValue()) | |
| cdata.append(0.0) # orthograhic camera | |
| elif isinstance(n, coin.SoPerspectiveCamera): | |
| cdata.append(n.heightAngle.getValue()) | |
| 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 | |
| ) | |
| FreeCADGui.doCommand(s) | |
| def reorder(self): | |
| 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 | |
| FreeCAD.ActiveDocument.recompute() | |
| def cloneUp(self): | |
| 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" | |
| ) | |
| return | |
| height = self.Object.Height.Value | |
| ng = [] | |
| 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)) | |
| ng.append(no) | |
| nobj = Arch.makeBuildingPart() | |
| Draft.formatObject(nobj, self.Object) | |
| nobj.Placement = self.Object.Placement | |
| 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) | |
| ): | |
| 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") | |
| ): | |
| no.LongName = no.CloneOf.LongName | |
| FreeCAD.ActiveDocument.recompute() | |
| def dumps(self): | |
| return None | |
| def loads(self, state): | |
| return None | |
| 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"): | |
| colors = obj.ViewObject.DiffuseColor | |
| if len(colors) != len(obj.Shape.Faces): | |
| 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): | |
| # convert colors to iv representations | |
| 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) | |
| else: | |
| print("Debug: IndexedFaceSet mismatch in", obj.Label) | |
| # save embedded file | |
| tf = tempfile.mkstemp(prefix=obj.Name, suffix=".iv")[1] | |
| f = open(tf, "w") | |
| f.write(iv) | |
| f.close() | |
| obj.SavedInventor = tf | |
| os.remove(tf) | |