| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """This module contains IFC object definitions""" |
|
|
| import FreeCAD |
| import FreeCADGui |
|
|
| translate = FreeCAD.Qt.translate |
|
|
|
|
| |
| 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 |
| self.virgin_placement = True |
| if otype: |
| self.Type = otype[0].upper() + otype[1:] |
| 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): |
| |
| 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): |
| |
| 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: |
| |
| |
| self.edit_pset(obj, prop) |
|
|
| def onDocumentRestored(self, obj): |
| self.rebuild_classlist(obj) |
| if hasattr(obj, "IfcFilePath"): |
| |
| from PySide import QtCore |
|
|
| 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): |
| |
| |
| |
| 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): |
| |
| |
| 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 |
|
|
| 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 |
|
|
| 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 |
|
|
| 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 |
| 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"): |
| |
| 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 |
| from . import ifc_tools |
|
|
| 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 |
|
|
| 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(): |
| |
| 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 |
|
|
| ifc_tools.set_placement(obj) |
|
|
| def edit_pset(self, obj, prop): |
| """Edits a Pset value""" |
|
|
| from . import ifc_psets |
|
|
| ifc_psets.edit_pset(obj, prop) |
|
|
| def edit_group(self, obj): |
| """Edits the children list""" |
|
|
| from . import ifc_tools |
| 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: |
| |
| 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 |
|
|
| ifc_types.edit_type(obj) |
|
|
| def edit_quantity(self, obj, prop): |
| """Edits the given quantity""" |
| pass |
|
|
| def get_section_data(self, obj): |
| """Returns two things: a list of objects and a cut plane""" |
|
|
| from . import ifc_tools |
| import Part |
|
|
| if not obj.IfcClass == "IfcAnnotation": |
| return [], None |
| if obj.ObjectType != "DRAWING": |
| return [], None |
| objs = getattr(obj, "Objects", []) |
| if not objs: |
| |
| 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: |
| |
| 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 |
|
|
| ifc_classification.edit_classification(obj) |
|
|
|
|
| class document_object: |
| """Holder for the document's IFC objects""" |
|
|
| def __init__(self): |
| pass |
|
|