| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import tempfile |
| |
|
| | import ifcopenshell |
| |
|
| | import FreeCAD |
| | import Draft |
| |
|
| | from importers import exportIFC |
| | from importers import exportIFCHelper |
| | from importers import importIFCHelper |
| |
|
| | from . import ifc_tools |
| | from . import ifc_import |
| |
|
| |
|
| | PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC") |
| |
|
| |
|
| | def get_export_preferences(ifcfile, preferred_context=None, create=None): |
| | """returns a preferences dict for exportIFC. |
| | Preferred context can either indicate a ContextType like 'Model' or 'Plan', |
| | or a [ContextIdentifier,ContextType,TargetView] list or tuple, for ex. |
| | ('Annotation','Plan') or ('Body','Model','MODEL_VIEW'). This function |
| | will do its best to find the most appropriate context. If create is True, |
| | if the exact context is not found, a new one is created""" |
| |
|
| | prefs = exportIFC.getPreferences() |
| | prefs["SCHEMA"] = ifcfile.wrapped_data.schema_name() |
| | s = ifcopenshell.util.unit.calculate_unit_scale(ifcfile) |
| | |
| | prefs["SCALE_FACTOR"] = 0.001 / s |
| | cids = ifc_tools.get_body_context_ids(ifcfile) |
| | contexts = [ifcfile[i] for i in cids] |
| | best_context = None |
| | exact_match = False |
| | if preferred_context: |
| | if isinstance(preferred_context, str): |
| | for context in contexts: |
| | if context.ContextType == preferred_context: |
| | best_context = context |
| | exact_match = True |
| | break |
| | elif isinstance(preferred_context, (list, tuple)): |
| | second_choice = None |
| | for context in contexts: |
| | if len(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 |
| | 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[0]: |
| | if not exact_match: |
| | best_context = context |
| | if len(preferred_context) == 1: |
| | exact_match = True |
| | if contexts: |
| | if not best_context: |
| | best_context = contexts[0] |
| | 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 |
| | ) |
| | 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, |
| | ) |
| | 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, |
| | ) |
| | else: |
| | if len(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, |
| | ) |
| | else: |
| | 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] |
| | ) |
| | if not best_context: |
| | if contexts: |
| | best_context = contexts[0] |
| | else: |
| | best_context = ifc_tools.api_run("context.add_context", ifcfile, context_type="Model") |
| | return prefs, best_context |
| |
|
| |
|
| | def create_product(obj, parent, ifcfile, ifcclass=None): |
| | """Creates an IFC product out of a FreeCAD object""" |
| |
|
| | name = obj.Label |
| | description = getattr(obj, "Description", None) |
| | if not ifcclass: |
| | ifcclass = ifc_tools.get_ifctype(obj) |
| | representation, placement = create_representation(obj, ifcfile) |
| | product = ifc_tools.api_run("root.create_entity", ifcfile, ifc_class=ifcclass, name=name) |
| | ifc_tools.set_attribute(ifcfile, product, "Description", description) |
| | ifc_tools.set_attribute(ifcfile, product, "ObjectPlacement", placement) |
| | |
| | |
| | |
| | ifc_tools.set_attribute(ifcfile, product, "Representation", representation) |
| | |
| | return product |
| |
|
| |
|
| | def create_representation(obj, ifcfile): |
| | """Creates a geometry representation for the given object""" |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | exportIFC.clones = {} |
| | exportIFC.profiledefs = {} |
| | exportIFC.surfstyles = {} |
| | exportIFC.shapedefs = {} |
| | exportIFC.ifcopenshell = ifcopenshell |
| | exportIFC.ifcbin = exportIFCHelper.recycler(ifcfile, template=False) |
| | prefs, context = get_export_preferences(ifcfile) |
| | representation, placement, shapetype = exportIFC.getRepresentation( |
| | ifcfile, context, obj, preferences=prefs |
| | ) |
| | return representation, placement |
| |
|
| |
|
| | def get_object_type(ifcentity, objecttype=None): |
| | """Determines a creation type for this object""" |
| |
|
| | if not objecttype: |
| | if ifcentity.is_a("IfcAnnotation"): |
| | if get_sectionplane(ifcentity): |
| | objecttype = "sectionplane" |
| | elif get_dimension(ifcentity): |
| | objecttype = "dimension" |
| | elif get_text(ifcentity): |
| | objecttype = "text" |
| | elif ifcentity.is_a("IfcGridAxis"): |
| | objecttype = "axis" |
| | elif ifcentity.is_a("IfcControl"): |
| | objecttype = "schedule" |
| | elif ifcentity.is_a() in ["IfcBuilding", "IfcBuildingStorey"]: |
| | objecttype = "buildingpart" |
| | return objecttype |
| |
|
| |
|
| | def is_annotation(obj): |
| | """Determines if the given FreeCAD object should be saved as an IfcAnnotation""" |
| |
|
| | if getattr(obj, "IfcClass", None) in ["IfcAnnotation", "IfcGridAxis"]: |
| | return True |
| | if getattr(obj, "IfcType", None) == "Annotation": |
| | return True |
| | if obj.isDerivedFrom("Part::Part2DObject"): |
| | return True |
| | elif obj.isDerivedFrom("App::Annotation"): |
| | return True |
| | 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) |
| | ): |
| | return True |
| | return False |
| |
|
| |
|
| | def get_text(annotation): |
| | """Determines if an IfcAnnotation contains an IfcTextLiteral. |
| | Returns the IfcTextLiteral or None""" |
| |
|
| | if annotation.is_a("IfcAnnotation"): |
| | for rep in annotation.Representation.Representations: |
| | for item in rep.Items: |
| | if item.is_a("IfcTextLiteral"): |
| | return item |
| | return None |
| |
|
| |
|
| | def get_dimension(annotation): |
| | """Determines if an IfcAnnotation is representing a dimension. |
| | Returns a list containing the representation, two points indicating |
| | the measured points, and optionally a third point indicating where |
| | the dimension line is located, if available""" |
| |
|
| | if annotation.is_a("IfcAnnotation"): |
| | if annotation.ObjectType == "DIMENSION": |
| | s = ifcopenshell.util.unit.calculate_unit_scale(annotation.file) * 1000 |
| | for rep in annotation.Representation.Representations: |
| | shape = importIFCHelper.get2DShape(rep, s, notext=True) |
| | pl = get_placement(annotation.ObjectPlacement, scale=s) |
| | if pl: |
| | shape[0].Placement = pl |
| | if shape and len(shape) == 1: |
| | if len(shape[0].Vertexes) >= 2: |
| | |
| | res = [rep, shape[0].Vertexes[0].Point, shape[0].Vertexes[-1].Point] |
| | if len(shape[0].Vertexes) > 2: |
| | |
| | res.append(shape[0].Vertexes[1].Point) |
| | return res |
| | else: |
| | print(annotation, "NOT A DIMENSION") |
| | return None |
| |
|
| |
|
| | def get_sectionplane(annotation): |
| | """Determines if an IfcAnnotation is representing a section plane. |
| | Returns a list containing a placement, and optionally an X dimension, |
| | an Y dimension and a depth dimension""" |
| |
|
| | if annotation.is_a("IfcAnnotation"): |
| | if annotation.ObjectType == "DRAWING": |
| | s = ifcopenshell.util.unit.calculate_unit_scale(annotation.file) * 1000 |
| | result = [get_placement(annotation.ObjectPlacement, scale=s)] |
| | for rep in annotation.Representation.Representations: |
| | 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) |
| | return result |
| | return None |
| |
|
| |
|
| | def get_axis(obj): |
| | """Determines if a given IFC entity is an IfcGridAxis. Returns a tuple |
| | containing a Placement, a length value in millimeters, and a tag""" |
| |
|
| | if obj.is_a("IfcGridAxis"): |
| | tag = obj.AxisTag |
| | s = ifcopenshell.util.unit.calculate_unit_scale(obj.file) * 1000 |
| | shape = importIFCHelper.get2DShape(obj.AxisCurve, s, notext=True) |
| | if shape: |
| | edge = shape[0].Edges[0] |
| | if obj.SameSense: |
| | p0 = edge.Vertexes[0].Point |
| | p1 = edge.Vertexes[-1].Point |
| | else: |
| | p0 = edge.Vertexes[-1].Point |
| | p1 = edge.Vertexes[0].Point |
| | length = edge.Length |
| | placement = FreeCAD.Placement() |
| | placement.Base = p0 |
| | placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), p1.sub(p0)) |
| | return (placement, length, tag) |
| | return None |
| |
|
| |
|
| | def create_annotation(obj, ifcfile): |
| | """Adds an IfcAnnotation from the given object to the given IFC file""" |
| |
|
| | exportIFC.clones = {} |
| | exportIFC.profiledefs = {} |
| | exportIFC.surfstyles = {} |
| | exportIFC.shapedefs = {} |
| | exportIFC.curvestyles = {} |
| | exportIFC.ifcopenshell = ifcopenshell |
| | exportIFC.ifcbin = exportIFCHelper.recycler(ifcfile, template=False) |
| | if is_annotation(obj) and Draft.getType(obj) != "SectionPlane": |
| | context_type = "Plan" |
| | else: |
| | context_type = "Model" |
| | prefs, context = get_export_preferences(ifcfile, preferred_context=context_type, create=True) |
| | prefs["BBIMDIMS"] = True |
| | history = get_history(ifcfile) |
| | |
| | |
| | anno = exportIFC.create_annotation(obj, ifcfile, context, history, prefs) |
| | return anno |
| |
|
| |
|
| | def get_history(ifcfile): |
| | """Returns the owner history or None""" |
| |
|
| | history = ifcfile.by_type("IfcOwnerHistory") |
| | if history: |
| | history = history[0] |
| | else: |
| | |
| | history = None |
| | return history |
| |
|
| |
|
| | def get_placement(ifcelement, ifcfile=None, scale=None): |
| | """Returns a FreeCAD placement from an IFC placement""" |
| |
|
| | if not scale: |
| | if not ifcfile: |
| | ifcfile = ifcelement.file |
| | scale = 0.001 / ifcopenshell.util.unit.calculate_unit_scale(ifcfile) |
| | return importIFCHelper.getPlacement(ifcelement, scaling=scale) |
| |
|
| |
|
| | def get_scaled_point(point, ifcfile=None, is2d=False): |
| | """Returns a scaled 2d or 3d point tuple form a FreeCAD point""" |
| |
|
| | if not ifcfile: |
| | ifcfile = ifcelement.file |
| | s = 0.001 / ifcopenshell.util.unit.calculate_unit_scale(ifcfile) |
| | v = FreeCAD.Vector(point) |
| | v.multiply(s) |
| | v = tuple(v) |
| | if is2d: |
| | v = v[:2] |
| | return v |
| |
|
| |
|
| | def get_scaled_value(value, ifcfile): |
| | """Returns a scaled dimension value""" |
| |
|
| | s = 0.001 / ifcopenshell.util.unit.calculate_unit_scale(ifcfile) |
| | return value * s |
| |
|
| |
|
| | def export_and_convert(objs, doc): |
| | """Exports the given objects and their descendents to the given IFC file |
| | and re-imports it into the given document. This is slower than direct_conversion() |
| | but gives an intermediate file which can be useful for debugging""" |
| |
|
| | tf = tempfile.mkstemp(suffix=".ifc")[1] |
| | exportIFC.export(objs, tf) |
| | ifc_import.insert(tf, doc.Name, singledoc=True) |
| |
|
| |
|
| | def direct_conversion(objs, doc): |
| | """Exports the given objects to the given ifcfile and recreates the contents""" |
| |
|
| | prj_obj = ifc_tools.convert_document(doc, silent=True) |
| | exportIFC.export(objs, doc.Proxy.ifcfile) |
| | if PARAMS.GetBool("LoadOrphans", True): |
| | ifc_tools.load_orphans(prj_obj) |
| | if PARAMS.GetBool("LoadMaterials", False): |
| | ifc_materials.load_materials(prj_obj) |
| | if PARAMS.GetBool("LoadLayers", False): |
| | ifc_layers.load_layers(prj_obj) |
| | if PARAMS.GetBool("LoadPsets", False): |
| | ifc_psets.load_psets(prj_obj) |
| |
|