Download src/Mod/BIM/nativeifc/ifc_objects.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 17 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/nativeifc/ifc_objects.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/BIM/nativeifc/ifc_objects.py
-
curl -L -o ifc_objects.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/nativeifc/ifc_objects.py
17 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * * | |
| # * Copyright (c) 2022 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/>. * | |
| # * * | |
| # *************************************************************************** | |
| """This module contains IFC object definitions""" | |
| import FreeCAD | |
| import FreeCADGui | |
| 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", | |
| ] | |
| class ifc_object: | |
| """Base class for all IFC-based objects""" | |
| def __init__(self, otype=None): | |
| 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 | |
| else: | |
| self.Type = "IfcObject" | |
| def onBeforeChange(self, obj, prop): | |
| if prop == "Schema": | |
| self.old_schema = obj.Schema | |
| elif prop == "Placement": | |
| self.old_placement = obj.Placement | |
| def onChanged(self, obj, prop): | |
| # link class property to its hidder IfcClass counterpart | |
| if prop == "IfcClass" and hasattr(obj, "Class") and obj.Class != obj.IfcClass: | |
| obj.Class = obj.IfcClass | |
| self.rebuild_classlist(obj, setprops=True) | |
| elif prop == "Class" and hasattr(obj, "IfcClass") and obj.Class != obj.IfcClass: | |
| obj.IfcClass = obj.Class | |
| self.rebuild_classlist(obj, setprops=True) | |
| elif prop == "Schema": | |
| self.edit_schema(obj, obj.Schema) | |
| elif prop == "Type": | |
| self.edit_type(obj) | |
| self.assign_classification(obj) | |
| elif prop == "Classification": | |
| self.edit_classification(obj) | |
| elif prop == "Group": | |
| self.edit_group(obj) | |
| elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "IFC": | |
| if prop not in ["StepId"]: | |
| self.edit_attribute(obj, prop) | |
| elif prop == "Label": | |
| self.edit_attribute(obj, "Name", obj.Label) | |
| elif prop == "Text": | |
| 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"]: | |
| self.edit_annotation(obj, prop) | |
| elif prop == "Placement": | |
| if getattr(self, "virgin_placement", False): | |
| self.virgin_placement = False | |
| elif obj.Placement != getattr(self, "old_placement", None): | |
| # print("placement changed for",obj.Label,"to",obj.Placement) | |
| self.edit_placement(obj) | |
| elif prop == "Modified": | |
| if obj.ViewObject: | |
| obj.ViewObject.signalChangeIcon() | |
| elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Geometry": | |
| self.edit_geometry(obj, prop) | |
| elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Quantities": | |
| self.edit_quantity(obj, prop) | |
| elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) not in NON_PSETS: | |
| # Treat all property groups outside the default ones as Psets | |
| # print("DEBUG: editinog pset prop",prop) | |
| self.edit_pset(obj, prop) | |
| def onDocumentRestored(self, obj): | |
| self.rebuild_classlist(obj) | |
| if hasattr(obj, "IfcFilePath"): | |
| # once we have loaded the project, recalculate child coin nodes | |
| from PySide import QtCore # lazy loading | |
| if obj.OutListRecursive: | |
| for child in obj.OutListRecursive: | |
| if getattr(child, "ShapeMode", None) == "Coin": | |
| child.Proxy.cached = True | |
| child.touch() | |
| else: | |
| obj.Proxy.cached = True | |
| QtCore.QTimer.singleShot(100, obj.touch) | |
| QtCore.QTimer.singleShot(100, obj.Document.recompute) | |
| QtCore.QTimer.singleShot(100, self.fit_all) | |
| def assign_classification(self, obj): | |
| """ | |
| Assigns Classification to an IFC object in a case where | |
| the object references a Type that has a Classification property, | |
| so we move copy the Type's property to our actual object. | |
| """ | |
| if not getattr(obj, "Type", None): | |
| return | |
| type_obj = obj.Type | |
| if getattr(type_obj, "Classification", None): | |
| # Check if there is Classification already, since user can just change | |
| # the IFC type, but there could be one previously assigned which had | |
| # Classification | |
| if getattr(obj, "Classification", None) is None: | |
| obj.addProperty("App::PropertyString", "Classification", "IFC") | |
| obj.Classification = type_obj.Classification | |
| obj.recompute() | |
| elif getattr(obj, "Classification", None): | |
| # This means user has assigned type that has no classification, so clear | |
| # the one that they have currently selected | |
| obj.Classification = "" | |
| obj.recompute() | |
| def fit_all(self): | |
| """Fits the view""" | |
| if FreeCAD.GuiUp: | |
| FreeCADGui.SendMsgToActiveView("ViewFit") | |
| def rebuild_classlist(self, obj, setprops=False): | |
| """rebuilds the list of Class enum property according to current class""" | |
| from . import ifc_tools # lazy import | |
| obj.Class = [obj.IfcClass] | |
| obj.Class = ifc_tools.get_ifc_classes(obj, obj.IfcClass) | |
| obj.Class = obj.IfcClass | |
| if setprops: | |
| ifc_tools.remove_unused_properties(obj) | |
| ifc_tools.add_properties(obj) | |
| def __getstate__(self): | |
| return getattr(self, "Type", None) | |
| def __setstate__(self, state): | |
| self.loads(state) | |
| def dumps(self): | |
| return getattr(self, "Type", None) | |
| def loads(self, state): | |
| if state: | |
| self.Type = state | |
| def execute(self, obj): | |
| from . import ifc_generator # lazy import | |
| if obj.isDerivedFrom("Part::Feature"): | |
| cached = getattr(self, "cached", False) | |
| ifc_generator.generate_geometry(obj, cached=cached) | |
| self.cached = False | |
| self.rebuild_classlist(obj) | |
| def addObject(self, obj, child): | |
| if child not in obj.Group: | |
| g = obj.Group | |
| g.append(child) | |
| obj.Group = g | |
| def removeObject(self, obj, child): | |
| if child in obj.Group: | |
| g = obj.Group | |
| g.remove(child) | |
| obj.Group = g | |
| def edit_attribute(self, obj, attribute, value=None): | |
| """Edits an attribute of an underlying IFC object""" | |
| from . import ifc_tools # lazy import | |
| if not value: | |
| value = obj.getPropertyByName(attribute) | |
| ifcfile = ifc_tools.get_ifcfile(obj) | |
| elt = ifc_tools.get_ifc_element(obj, ifcfile) | |
| if elt: | |
| result = ifc_tools.set_attribute(ifcfile, elt, attribute, value) | |
| if result: | |
| if hasattr(result, "id") and (result.id() != obj.StepId): | |
| obj.StepId = result.id() | |
| def edit_annotation(self, obj, attribute, value=None): | |
| """Edits an attribute of an underlying IFC annotation""" | |
| from . import ifc_tools # lazy import | |
| from . import ifc_export | |
| if not value: | |
| if hasattr(obj, attribute): | |
| value = obj.getPropertyByName(attribute) | |
| ifcfile = ifc_tools.get_ifcfile(obj) | |
| elt = ifc_tools.get_ifc_element(obj, ifcfile) | |
| if elt: | |
| if attribute == "Text": | |
| text = ifc_export.get_text(elt) | |
| if text: | |
| ifc_tools.set_attribute(ifcfile, text, "Literal", value) | |
| elif attribute in ["Start", "End"]: | |
| dim = ifc_export.get_dimension(elt) | |
| if dim: | |
| rep = dim[0] | |
| for curve in rep.Items: | |
| if not hasattr(curve, "Elements"): | |
| # this is a TextLiteral for the dimension text - skip it | |
| continue | |
| for sub in curve.Elements: | |
| if sub.is_a("IfcIndexedPolyCurve"): | |
| points = sub.Points | |
| value = list(points.CoordList) | |
| is2d = "2D" in points.is_a() | |
| if attribute == "Start": | |
| value[0] = ifc_export.get_scaled_point(obj.Start, ifcfile, is2d) | |
| else: | |
| 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"]: | |
| l = w = h = 1000.0 | |
| if obj.ViewObject: | |
| if obj.ViewObject.DisplayLength.Value: | |
| l = ifc_export.get_scaled_value(obj.ViewObject.DisplayLength.Value, ifcfile) | |
| if obj.ViewObject.DisplayHeight.Value: | |
| w = ifc_export.get_scaled_value(obj.ViewObject.DisplayHeight.Value, ifcfile) | |
| if obj.Depth.Value: | |
| h = ifc_export.get_scaled_value(obj.Depth.Value, ifcfile) | |
| if elt.Representation.Representations: | |
| for rep in elt.Representation.Representations: | |
| for item in rep.Items: | |
| if item.is_a("IfcCsgSolid"): | |
| if item.TreeRootExpression.is_a("IfcBlock"): | |
| block = item.TreeRootExpression | |
| loc = block.Position.Location | |
| 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) | |
| ) | |
| def edit_geometry(self, obj, prop): | |
| """Edits a geometry property of an object""" | |
| from . import ifc_geometry # lazy loading | |
| from . import ifc_tools # lazy import | |
| result = ifc_geometry.set_geom_property(obj, prop) | |
| if result: | |
| obj.touch() | |
| def edit_schema(self, obj, schema): | |
| """Changes the schema of an IFC document""" | |
| from . import ifc_tools # lazy import | |
| ifcfile = ifc_tools.get_ifcfile(obj) | |
| if not ifcfile: | |
| return | |
| if not getattr(self, "old_schema", None): | |
| return | |
| if schema != ifcfile.wrapped_data.schema_name(): | |
| # set obj.Proxy.silent = True to disable the schema change warning | |
| if obj.ViewObject and not getattr(self, "silent", False): | |
| if not obj.ViewObject.Proxy.schema_warning(): | |
| return | |
| 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] | |
| if len(child) == 1: | |
| child[0].StepId = new_id | |
| def edit_placement(self, obj): | |
| """Syncs the internal IFC placement""" | |
| from . import ifc_tools # lazy import | |
| ifc_tools.set_placement(obj) | |
| def edit_pset(self, obj, prop): | |
| """Edits a Pset value""" | |
| from . import ifc_psets # lazy import | |
| ifc_psets.edit_pset(obj, prop) | |
| def edit_group(self, obj): | |
| """Edits the children list""" | |
| from . import ifc_tools # lazy import | |
| from . import ifc_layers | |
| if obj.Class in [ | |
| "IfcPresentationLayerAssignment", | |
| "IfcPresentationLayerWithStyle", | |
| ]: | |
| ifcfile = ifc_tools.get_ifcfile(obj) | |
| if not ifcfile: | |
| return | |
| newlist = [] | |
| for child in obj.Group: | |
| if not getattr(child, "StepId", None) or ifc_tools.get_ifcfile(child) != ifcfile: | |
| print( | |
| "DEBUG: Not an IFC object. Removing", | |
| child.Label, | |
| "from layer", | |
| obj.Label, | |
| ) | |
| else: | |
| # print("DEBUG: adding", child.Label, "to layer", obj.Label) | |
| newlist.append(child) | |
| ifc_layers.add_to_layer(child, obj) | |
| if newlist != obj.Group: | |
| obj.Group = newlist | |
| def edit_type(self, obj): | |
| """Edits the type of this object""" | |
| from . import ifc_types # lazy import | |
| ifc_types.edit_type(obj) | |
| def edit_quantity(self, obj, prop): | |
| """Edits the given quantity""" | |
| pass # TODO implement | |
| def get_section_data(self, obj): | |
| """Returns two things: a list of objects and a cut plane""" | |
| from . import ifc_tools # lazy import | |
| import Part | |
| if not obj.IfcClass == "IfcAnnotation": | |
| return [], None | |
| if obj.ObjectType != "DRAWING": | |
| return [], None | |
| objs = getattr(obj, "Objects", []) | |
| if not objs: | |
| # no object defined, we automatically use the project | |
| objs = [] | |
| proj = ifc_tools.get_project(obj) | |
| if isinstance(proj, FreeCAD.DocumentObject): | |
| objs.append(proj) | |
| objs.extend(ifc_tools.get_freecad_children(proj)) | |
| if objs: | |
| s = [] | |
| for o in objs: | |
| # TODO print a better message | |
| if o.ShapeMode != "Shape": | |
| s.append(o) | |
| if s: | |
| FreeCAD.Console.PrintLog("DEBUG: Generating shapes. This might take some time...\n") | |
| for o in s: | |
| o.ShapeMode = "Shape" | |
| o.recompute() | |
| l = 1 | |
| h = 1 | |
| if obj.ViewObject: | |
| 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.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""" | |
| from . import ifc_classification # lazy loading | |
| ifc_classification.edit_classification(obj) | |
| class document_object: | |
| """Holder for the document's IFC objects""" | |
| def __init__(self): | |
| pass | |