diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index b85b8e5576..c2ae75143d 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -36,7 +36,7 @@ jobs: with: repository: IfcOpenShell/build-outputs path: ${{ matrix.deps_dir }} - ref: windows-${{ matrix.arch }} + ref: ${{ matrix.build_branch }} lfs: true token: ${{ secrets.BUILD_REPO_TOKEN }} @@ -106,4 +106,4 @@ jobs: foreach ($zip in Get-ChildItem -Path "$env:USERPROFILE\output" -Filter *.zip) { aws s3 cp "$($zip.FullName)" s3://ifcopenshell-builds/ --debug Start-Sleep -Seconds 5 - } \ No newline at end of file + } diff --git a/.github/workflows/ci-ifcopenshell-docker.yml b/.github/workflows/ci-ifcopenshell-docker.yml index 8f6bb21189..2eee5eff1a 100644 --- a/.github/workflows/ci-ifcopenshell-docker.yml +++ b/.github/workflows/ci-ifcopenshell-docker.yml @@ -91,7 +91,7 @@ jobs: lfs: true - name: Download - uses: actions/download-artifact@v8.0.0 + uses: actions/download-artifact@v8.0.1 with: # Artifact name name: ifcos-artifacts diff --git a/.gitignore b/.gitignore index 8a7482ab8e..a92e504001 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,9 @@ dev_environment.bat src/ifcopenshell-python/ifcopenshell/express/*.exp src/ifcopenshell-python/ifcopenshell/express/*.exp.cache.dat + + +# temp files from AI coding tools +*.claude +*.py.tmp* +*.json.tmp* diff --git a/pyproject.toml b/pyproject.toml index ab0636ab62..7234815c08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,9 @@ name = "IfcOpenShell" version = "0.0.0" dependencies = [ "black==26.3.1", - "ruff==0.15.5", + "ruff==0.15.6", "poethepoet", - "gersemi==0.26.0", + "gersemi==0.26.1", ] [tool.black] diff --git a/src/bonsai/bonsai/bim/module/bsdd/ui.py b/src/bonsai/bonsai/bim/module/bsdd/ui.py index c6966c01d8..f49f48abb9 100644 --- a/src/bonsai/bonsai/bim/module/bsdd/ui.py +++ b/src/bonsai/bonsai/bim/module/bsdd/ui.py @@ -83,7 +83,6 @@ class BIM_PT_bsdd(Panel): row = self.layout.row() row.operator("bim.load_bsdd_dictionaries") - class BIM_UL_bsdd_dictionaries(UIList): def draw_item( self, diff --git a/src/bonsai/bonsai/bim/module/drawing/decoration.py b/src/bonsai/bonsai/bim/module/drawing/decoration.py index 3910acd995..b9b4dd40cb 100644 --- a/src/bonsai/bonsai/bim/module/drawing/decoration.py +++ b/src/bonsai/bonsai/bim/module/drawing/decoration.py @@ -1787,7 +1787,7 @@ class CutDecorator: # Handle both old float64 and new float32 checksums for version compatibility rot_checksum_bytes: bytes = eval(DecoratorData.camera_rotation_checksum) - rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9) + rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9).reshape(3, 3) rot_real = tool.Blender.np_array_legacy(obj.matrix_world.to_3x3()) rot_dot = np.dot(rot_check, rot_real.T) angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1)) diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 8b30737176..fa17be9fb5 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -3305,9 +3305,8 @@ class AddTextLiteral(bpy.types.Operator): attr.data_type = "string" attr.string_value = literal_attr_values[attr_name] - box_alignment_mask = [False] * 9 - box_alignment_mask[6] = True # bottom_left box_alignment - literal_props.box_alignment = box_alignment_mask + literal_props.align_vertical = "bottom" + literal_props.align_horizontal = "left" return {"FINISHED"} @@ -4178,10 +4177,7 @@ class SelectSimilarTextLiteralValue(bpy.types.Operator): should_select = True break elif self.attribute_type == "box_alignment": - box_alignment_attr = next( - (attr for attr in literal.attributes if attr.name == "BoxAlignment"), None - ) - if box_alignment_attr and box_alignment_attr.string_value == self.literal_value: + if literal.get_box_alignment() == self.literal_value: should_select = True break diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py index cc597a4802..6019c480b9 100644 --- a/src/bonsai/bonsai/bim/module/drawing/prop.py +++ b/src/bonsai/bonsai/bim/module/drawing/prop.py @@ -673,20 +673,6 @@ class BIMCameraProperties(PropertyGroup): return ortho_scale, aspect_ratio -DEFAULT_BOX_ALIGNMENT = [False] * 6 + [True] + [False] * 2 -BOX_ALIGNMENT_POSITIONS = [ - "top-left", - "top-middle", - "top-right", - "middle-left", - "center", - "middle-right", - "bottom-left", - "bottom-middle", - "bottom-right", -] - - class ElementValueRow(PropertyGroup): """Represents a single element value row with category, key, and formatted value""" @@ -789,40 +775,38 @@ def get_category_items_with_counts(self, context): class LiteralProps(PropertyGroup): - def set_box_alignment(self, new_value): - markers = new_value.count(True) - if not markers: - return - - if markers > 1: - prev_value = self.get("box_alignment", DEFAULT_BOX_ALIGNMENT) - # looking for the first value changed to positive - first_changed_value = next((i for i in range(9) if new_value[i] and new_value[i] != prev_value[i]), None) - - # if nothing have changed we just keep the previous value - if first_changed_value is None: - return - new_value = [False] * 9 - new_value[first_changed_value] = True - - self["box_alignment"] = new_value - position_string = BOX_ALIGNMENT_POSITIONS[next(i for i in range(9) if new_value[i])] - self.attributes["BoxAlignment"].set_value(position_string) - - def get_box_alignment(self): - return self.get("box_alignment", DEFAULT_BOX_ALIGNMENT) - attributes: CollectionProperty(name="Attributes", type=Attribute) - box_alignment: BoolVectorProperty( - name="Box alignment", size=9, set=set_box_alignment, get=get_box_alignment, default=DEFAULT_BOX_ALIGNMENT - ) ifc_definition_id: IntProperty(name="IFC definition ID", default=0) + align_horizontal: EnumProperty( + items=[ + ("left", "Left", "", "ALIGN_LEFT", 0), + ("middle", "Middle", "", "ALIGN_CENTER", 1), + ("right", "Right", "", "ALIGN_RIGHT", 2), + ], + default="left", + name="Horizontal Alignment", + ) + align_vertical: EnumProperty( + items=[ + ("top", "Top", "", "ALIGN_TOP", 0), + ("middle", "Middle", "", "ALIGN_MIDDLE", 1), + ("bottom", "Bottom", "", "ALIGN_BOTTOM", 2), + ], + default="middle", + name="Vertical Alignment", + ) + + def get_box_alignment(self) -> str: + alignment = self.align_vertical + "-" + self.align_horizontal + if alignment == "middle-middle": + alignment = "center" + return alignment def get_literal_edited_data(self) -> dict[str, str]: text_data = { "CurrentValue": self.attributes["Literal"].string_value, "Literal": self.attributes["Literal"].string_value, - "BoxAlignment": self.attributes["BoxAlignment"].string_value, + "BoxAlignment": self.get_box_alignment(), } return text_data @@ -860,12 +844,19 @@ class LiteralProps(PropertyGroup): if TYPE_CHECKING: attributes: bpy.types.bpy_prop_collection_idprop[Attribute] value: str - box_alignment: tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool] ifc_definition_id: int + align_horizontal: str + align_vertical: str element_value_rows: bpy.types.bpy_prop_collection_idprop[ElementValueRow] category_for_adding: str +def update_text_alignment(self, context): + for literal_props in self.literals: + literal_props.align_horizontal = self.align_horizontal + literal_props.align_vertical = self.align_vertical + + class BIMTextProperties(PropertyGroup): is_editing: BoolProperty(name="Is Editing", default=False) literals: CollectionProperty(name="Literals", type=LiteralProps) @@ -899,6 +890,7 @@ class BIMTextProperties(PropertyGroup): ], default="left", name="Horizontal Alignment", + update=update_text_alignment, ) align_vertical: EnumProperty( items=[ @@ -908,6 +900,7 @@ class BIMTextProperties(PropertyGroup): ], default="middle", name="Vertical Alignment", + update=update_text_alignment, ) if TYPE_CHECKING: diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index 1b6a11cd75..796c36b484 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -781,33 +781,10 @@ class BIM_PT_text(Panel): if other_attributes: bonsai.bim.helper.draw_attributes(other_attributes, box) - row = box.row(align=True) - cols = [row.column(align=True) for j in range(3)] - for j in range(9): - cols[j % 3].prop( - literal_props, - "box_alignment", - text="", - index=j, - icon="RADIOBUT_ON" if literal_props.box_alignment[j] else "RADIOBUT_OFF", - ) - - col = row.column(align=True) - alignment_label_row = col.row(align=True) - alignment_label_row.label(text=" Text box alignment:") - - box_alignment_value = ( - literal_props.attributes[ - next( - (idx for idx, attr in enumerate(literal_props.attributes) if attr.name == "BoxAlignment"), - -1, - ) - ].string_value - if any(attr.name == "BoxAlignment" for attr in literal_props.attributes) - else "N/A" - ) - - col.label(text=f" {box_alignment_value}") + row = box.row() + row.label(text="Alignment") + row.prop(literal_props, "align_horizontal", text="", expand=True) + row.prop(literal_props, "align_vertical", text="", expand=True) def draw(self, context): obj = context.active_object diff --git a/src/bonsai/bonsai/bim/module/material/data.py b/src/bonsai/bonsai/bim/module/material/data.py index 543a44824b..6d9e568087 100644 --- a/src/bonsai/bonsai/bim/module/material/data.py +++ b/src/bonsai/bonsai/bim/module/material/data.py @@ -102,7 +102,6 @@ class MaterialsData: if (style_name := s.Name) is not None ] results = natsorted(results, key=lambda i: i[1]) - results.insert(0, ("-", "No Surface Style", "")) return results @classmethod diff --git a/src/bonsai/bonsai/bim/module/material/operator.py b/src/bonsai/bonsai/bim/module/material/operator.py index e0761bf416..08cddbb928 100644 --- a/src/bonsai/bonsai/bim/module/material/operator.py +++ b/src/bonsai/bonsai/bim/module/material/operator.py @@ -210,14 +210,15 @@ class AssignMaterialToSelected(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_material_to_selected" bl_label = "Assign Material To Selected" bl_description = ( - "Assign currently selected material in Materials UI to the selected objects.\n\n" - "ALT+CLICK to assign material as a usage." + "Assign currently selected material in Materials UI to the selected objects.\n" + "Occurrences automatically get usages for layer/profile sets.\n\n" + "ALT+CLICK to assign without a usage." ) bl_options = {"REGISTER", "UNDO"} material: bpy.props.IntProperty(name="Material IFC ID") - assign_as_usage: bpy.props.BoolProperty( - name="Assign Material As A Usage", - default=False, + should_auto_assign_usage: bpy.props.BoolProperty( + name="Auto Assign Usage", + default=True, options={"SKIP_SAVE"}, ) @@ -230,25 +231,19 @@ class AssignMaterialToSelected(bpy.types.Operator, tool.Ifc.Operator): def invoke(self, context, event): if event.type == "LEFTMOUSE" and event.alt: - material_class = tool.Ifc.get().by_id(self.material).is_a() - if material_class not in ("IfcMaterialProfileSet", "IfcMaterialLayerSet"): - self.report({"ERROR"}, f"{material_class} cannot be assigned as a usage.") - return {"CANCELLED"} - self.assign_as_usage = True + self.should_auto_assign_usage = False return self.execute(context) def _execute(self, context): material = tool.Ifc.get().by_id(self.material) objects = tool.Blender.get_selected_objects() - material_type = material.is_a() - if self.assign_as_usage: - material_type += "Usage" core.assign_material( tool.Ifc, tool.Material, - material_type=material_type, + material_type=material.is_a(), objects=objects, material=material, + should_auto_assign_usage=self.should_auto_assign_usage, ) diff --git a/src/bonsai/bonsai/bim/module/material/ui.py b/src/bonsai/bonsai/bim/module/material/ui.py index f743306767..db16755aad 100644 --- a/src/bonsai/bonsai/bim/module/material/ui.py +++ b/src/bonsai/bonsai/bim/module/material/ui.py @@ -118,12 +118,17 @@ class BIM_PT_materials(Panel): row.operator("bim.edit_material", text="Save Material", icon="CHECKMARK").material = ifc_definition_id row.operator("bim.disable_editing_material", text="", icon="CANCEL") elif self.props.editing_material_type == "STYLE": - row = self.layout.row(align=True) - row.prop(self.props, "contexts", text="") - prop_with_search(row, self.props, "styles", text="") - row = self.layout.row(align=True) - row.operator("bim.edit_material_style", text="Assign Style", icon="CHECKMARK") - row.operator("bim.disable_editing_material", text="", icon="CANCEL") + if MaterialsData.data["styles"]: + row = self.layout.row(align=True) + row.prop(self.props, "contexts", text="") + prop_with_search(row, self.props, "styles", text="") + row = self.layout.row(align=True) + row.operator("bim.edit_material_style", text="Assign Style", icon="CHECKMARK") + row.operator("bim.disable_editing_material", text="", icon="CANCEL") + else: + row = self.layout.row(align=True) + row.label(text="No Styles Found") + row.operator("bim.disable_editing_material", text="", icon="CANCEL") class BIM_PT_object_material(Panel): diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index 03d6f8bb36..addd03a504 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -262,7 +262,7 @@ class BIM_PT_object_psets(Panel): row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") - if props.pset_name != "BBIM_BSDD" and not props.pset_name.startswith(tool.Bsdd.identifier_url): + if props.pset_name != "BBIM_BSDD" and not props.pset_name.startswith(tool.Bsdd.identifier_url()): op = row.operator("bim.add_pset", icon="ADD", text="") op.obj = obj.name op.obj_type = "Object" diff --git a/src/bonsai/bonsai/core/material.py b/src/bonsai/bonsai/core/material.py index 65c5955603..4fd254b6f2 100644 --- a/src/bonsai/bonsai/core/material.py +++ b/src/bonsai/bonsai/core/material.py @@ -113,6 +113,7 @@ def assign_material( material_type: Union[str, None], objects: list[bpy.types.Object], material: Optional[ifcopenshell.entity_instance] = None, + should_auto_assign_usage: bool = True, ) -> None: """Assign material to the provided objects. @@ -121,12 +122,18 @@ def assign_material( """ material_type = material_type or material_tool.get_object_ui_material_type() material = material or material_tool.get_object_ui_active_material() + can_be_usage = should_auto_assign_usage and material_type in ("IfcMaterialLayerSet", "IfcMaterialProfileSet") for obj in objects: element = ifc.get_entity(obj) if not element: continue - ifc.run("material.assign_material", products=[element], type=material_type, material=material) + if can_be_usage and not material_tool.is_type_product(element): + element_material_type = material_type + "Usage" + else: + element_material_type = material_type + + ifc.run("material.assign_material", products=[element], type=element_material_type, material=material) assigned_material = material_tool.get_material(element) assert assigned_material # Type checker. @@ -136,7 +143,7 @@ def assign_material( material_tool.add_material_to_set(material_set=material, material=default_material) elif material_tool.is_a_material_set(assigned_material): material_tool.add_material_to_set(material_set=assigned_material, material=material) - material_tool.ensure_material_assigned(elements=[element], material_type=material_type, material=material) + material_tool.ensure_material_assigned(elements=[element], material_type=element_material_type, material=material) def unassign_material(ifc: type[tool.Ifc], material_tool: type[tool.Material], objects: list[bpy.types.Object]) -> None: diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index 234c79c53c..340f9d7a64 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -580,6 +580,7 @@ class Material: def import_material_definitions(cls, material_type: str): pass def is_a_flow_segment(cls, element): pass def is_a_material_set(cls, material): pass + def is_type_product(cls, element): pass def is_editing_materials(cls): pass def is_material_used_in_sets(cls, material): pass def load_material_attributes(cls, material): pass diff --git a/src/bonsai/bonsai/tool/bsdd.py b/src/bonsai/bonsai/tool/bsdd.py index 47c948abcc..74d9926f15 100644 --- a/src/bonsai/bonsai/tool/bsdd.py +++ b/src/bonsai/bonsai/tool/bsdd.py @@ -36,11 +36,23 @@ if TYPE_CHECKING: class Bsdd(bonsai.core.tool.Bsdd): - identifier_url = "https://identifier.buildingsmart.org" + default_identifier_url = "https://identifier.buildingsmart.org" + default_api_url = "https://api.bsdd.buildingsmart.org/api/" client = bsdd.Client() bsdd_classes: dict[str, dict] = {} bsdd_properties: dict[str, dict] = {} + @classmethod + def identifier_url(cls) -> str: + """Derives the identifier base URL from the current client baseurl. + Falls back to the standard bSDD identifier URL when using the default API.""" + if cls.client.baseurl == cls.default_api_url: + return cls.default_identifier_url + from urllib.parse import urlparse + + parsed = urlparse(cls.client.baseurl) + return f"{parsed.scheme}://{parsed.netloc}" + @classmethod def get_bsdd_props(cls) -> BIMBSDDProperties: assert (scene := bpy.context.scene) @@ -269,7 +281,7 @@ class Bsdd(bonsai.core.tool.Bsdd): for obj in tool.Blender.get_selected_objects(include_active=True): if element := tool.Ifc.get_entity(obj): for reference in ifcopenshell.util.classification.get_references(element): - if (uri := reference.Location) and uri.startswith(cls.identifier_url): + if (uri := reference.Location) and uri.startswith(cls.identifier_url()): classes.add((reference[1] or reference[2] or "Unnamed", uri)) dictionary_uris = ( @@ -383,7 +395,7 @@ class Bsdd(bonsai.core.tool.Bsdd): def get_applicable_psets(cls, element: ifcopenshell.entity_instance): uris = set() for reference in ifcopenshell.util.classification.get_references(element): - if (uri := reference.Location) and uri.startswith(cls.identifier_url): + if (uri := reference.Location) and uri.startswith(cls.identifier_url()): uris.add(uri) psets = set() for uri in uris: @@ -399,7 +411,7 @@ class Bsdd(bonsai.core.tool.Bsdd): def is_applicable(cls, pset_uri: str, element: ifcopenshell.entity_instance) -> bool: uris = set() for reference in ifcopenshell.util.classification.get_references(element): - if (uri := reference.Location) and uri.startswith(cls.identifier_url): + if (uri := reference.Location) and uri.startswith(cls.identifier_url()): uris.add(uri) class_uri, pset_name = pset_uri.rsplit("#", 1) return class_uri in uris diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index b8ec26fd37..a78fd67ed2 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -603,6 +603,10 @@ class Drawing(bonsai.core.tool.Drawing): props = tool.Drawing.get_text_props(obj) for literal_props in props.literals: literal_data = bonsai.bim.helper.export_attributes(literal_props.attributes) + alignment = literal_props.align_vertical + "-" + literal_props.align_horizontal + if alignment == "middle-middle": + alignment = "center" + literal_data["BoxAlignment"] = alignment literals.append(literal_data) return literals @@ -1176,22 +1180,26 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def import_text_attributes(cls, obj: bpy.types.Object) -> None: - from bonsai.bim.module.drawing.prop import BOX_ALIGNMENT_POSITIONS - props = cls.get_text_props(obj) props.literals.clear() ifc_literals = cls.get_text_literal(obj, return_list=True) assert isinstance(ifc_literals, list) + + if ifc_literals: + first_alignment = getattr(ifc_literals[0], "BoxAlignment", None) or "bottom-left" + if first_alignment == "center": + first_alignment = "middle-middle" + props.align_vertical, props.align_horizontal = first_alignment.split("-") + for ifc_literal in ifc_literals: literal_props = props.literals.add() bonsai.bim.helper.import_attributes(ifc_literal, literal_props.attributes) - box_alignment_mask = [False] * 9 - position_string = literal_props.attributes["BoxAlignment"].string_value - box_alignment_mask[BOX_ALIGNMENT_POSITIONS.index(position_string)] = True - - literal_props.box_alignment = box_alignment_mask # pyright: ignore[reportAttributeAccessIssue] + alignment = getattr(ifc_literal, "BoxAlignment", None) or "bottom-left" + if alignment == "center": + alignment = "middle-middle" + literal_props.align_vertical, literal_props.align_horizontal = alignment.split("-") literal_props.ifc_definition_id = ifc_literal.id() from bonsai.bim.module.drawing.data import DecoratorData diff --git a/src/bonsai/bonsai/tool/material.py b/src/bonsai/bonsai/tool/material.py index e09462a8c2..4c55123a9c 100644 --- a/src/bonsai/bonsai/tool/material.py +++ b/src/bonsai/bonsai/tool/material.py @@ -226,6 +226,10 @@ class Material(bonsai.core.tool.Material): "IfcMaterialProfileSet", ] + @classmethod + def is_type_product(cls, element: ifcopenshell.entity_instance) -> bool: + return element.is_a("IfcTypeProduct") + @classmethod def add_material_to_set( cls, material_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance diff --git a/src/bonsai/bonsai/tool/polyline.py b/src/bonsai/bonsai/tool/polyline.py index 5a91fc79b8..542bc23005 100644 --- a/src/bonsai/bonsai/tool/polyline.py +++ b/src/bonsai/bonsai/tool/polyline.py @@ -533,9 +533,12 @@ class Polyline(bonsai.core.tool.Polyline): polyline_data = polyline_data[0] polyline_points = polyline_data.polyline_points if polyline_points: - # Avoids creating two points at the same location - for point in polyline_points[1:]: # The first can be repeated to form a wall loop + # Avoids creating two points at the same location. + # The only exception is repeating the first point to close a loop (requires >= 3 existing points). + for i, point in enumerate(polyline_points): if (x, y, z) == (point.x, point.y, point.z): + if i == 0 and len(polyline_points) >= 3: + continue return "Cannot create two points at the same location" # Avoids creating overlapping edges if len(polyline_points) > 1: diff --git a/src/bonsai/test/tool/test_drawing.py b/src/bonsai/test/tool/test_drawing.py index 06091ff8c4..1d6e9e6e0f 100644 --- a/src/bonsai/test/tool/test_drawing.py +++ b/src/bonsai/test/tool/test_drawing.py @@ -666,10 +666,13 @@ class TestImportTextAttributes(NewFile): literal_props = props.literals[0] assert literal_props.ifc_definition_id == item.id() - assert literal_props.box_alignment[:] == tuple([False] * 6 + [True] + [False] * 2) assert literal_props.attributes["Literal"].string_value == "Literal" assert literal_props.attributes["Path"].enum_value == "RIGHT" assert literal_props.attributes["BoxAlignment"].string_value == "bottom-left" + assert literal_props.align_vertical == "bottom" + assert literal_props.align_horizontal == "left" + assert props.align_vertical == "bottom" + assert props.align_horizontal == "left" class TestReplaceTextLiteralVariables(NewFile): diff --git a/src/ifcgeom/Iterator.cpp b/src/ifcgeom/Iterator.cpp index a7f700f4de..4db7db8946 100644 --- a/src/ifcgeom/Iterator.cpp +++ b/src/ifcgeom/Iterator.cpp @@ -620,17 +620,7 @@ const IfcGeom::Element* IfcGeom::Iterator::get_object(int id) { } } catch (const std::exception& e) { Logger::Error(e); - } -#ifdef IFOPSH_WITH_OPENCASCADE - catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error returning product"); - } - } -#endif - catch (...) { + } catch (...) { Logger::Error("Unknown error returning product"); } @@ -645,18 +635,7 @@ const IfcUtil::IfcBaseClass* IfcGeom::Iterator::create() { } catch (const std::exception& e) { Logger::Error(e); had_error_processing_elements_ = true; - } -#ifdef IFOPSH_WITH_OPENCASCADE - catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating geometry"); - } - had_error_processing_elements_ = true; - } -#endif - catch (...) { + } catch (...) { Logger::Error("Unknown error creating geometry"); had_error_processing_elements_ = true; } diff --git a/src/ifcgeom/Iterator.h b/src/ifcgeom/Iterator.h index 3f5f90a350..fc9e542d60 100644 --- a/src/ifcgeom/Iterator.h +++ b/src/ifcgeom/Iterator.h @@ -68,10 +68,6 @@ #include "../ifcgeom/abstract_mapping.h" #include "../ifcgeom/GeometrySerializer.h" -#ifdef IFOPSH_WITH_OPENCASCADE -#include -#endif - #include #include diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.cpp b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.cpp index a5a9d94149..e8b672906e 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.cpp +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.cpp @@ -255,1126 +255,28 @@ bool IfcGeom::OpenCascadeKernel::unify_shapes(const IfcGeom::ConversionResults& } bool IfcGeom::OpenCascadeKernel::convert_impl(const taxonomy::revolve::ptr r, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + gp_Ax1 ax( + convert_xyz(*r->axis_origin), + convert_xyz(*r->direction)); + TopoDS_Shape face; + if (!convert(taxonomy::cast(r->basis), face)) { + return false; + } - gp_Ax1 ax( - convert_xyz(*r->axis_origin), - convert_xyz(*r->direction)); + TopoDS_Shape shape; + if (r->angle) { + shape = BRepPrimAPI_MakeRevol(face, ax, *r->angle); + } else { + shape = BRepPrimAPI_MakeRevol(face, ax); + } - TopoDS_Shape face; - if (!convert(taxonomy::cast(r->basis), face)) { - return false; - } - - TopoDS_Shape shape; - if (r->angle) { - shape = BRepPrimAPI_MakeRevol(face, ax, *r->angle); - } else { - shape = BRepPrimAPI_MakeRevol(face, ax); - } - - results.emplace_back(ConversionResult( - r->instance->as()->id(), - r->matrix, - new OpenCascadeShape(shape), - r->surface_style - )); - return true; + results.emplace_back(ConversionResult( + r->instance->as()->id(), + r->matrix, + new OpenCascadeShape(shape), + r->surface_style)); + return true; + }); } - -// IfcSchema::IfcRelVoidsElement::list::ptr IfcGeom::Kernel::find_openings(IfcSchema::IfcProduct* product) { -// std::vector rs; -// -// if (product->declaration().is(IfcSchema::IfcElement::Class()) && !product->declaration().is(IfcSchema::IfcOpeningElement::Class())) { -// IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)product; -// auto rels = element->HasOpenings(); -// rs.insert(rs.end(), rels->begin(), rels->end()); -// } -// -// // Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements? -// IfcSchema::IfcObjectDefinition* obdef = product->as(); -// for (;;) { -// auto decomposes = obdef->Decomposes(); -// if (decomposes->size() != 1) break; -// IfcSchema::IfcObjectDefinition* rel_obdef = (*decomposes->begin())->RelatingObject(); -// if (rel_obdef->declaration().is(IfcSchema::IfcElement::Class()) && !rel_obdef->declaration().is(IfcSchema::IfcOpeningElement::Class())) { -// IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)rel_obdef; -// auto rels = element->HasOpenings(); -// rs.insert(rs.end(), rels->begin(), rels->end()); -// } -// -// obdef = rel_obdef; -// } -// -// // Filter openings in Reference view, solely marked as Reference. -// IfcSchema::IfcRelVoidsElement::list::ptr openings(new IfcSchema::IfcRelVoidsElement::list); -// std::for_each(rs.begin(), rs.end(), [&openings](IfcSchema::IfcRelVoidsElement* rel) { -// if (rel->RelatedOpeningElement()->ObjectPlacement() && rel->RelatedOpeningElement()->Representation()) { -// auto reps = rel->RelatedOpeningElement()->Representation()->Representations(); -// if (!(reps->size() == 1 && (*reps->begin())->RepresentationIdentifier().get_value_or("") == "Reference")) { -// openings->push(rel); -// } -// } -// }); -// -// return openings; -// } -// -// const IfcSchema::IfcMaterial* IfcGeom::Kernel::get_single_material_association(const IfcSchema::IfcProduct* product) { -// IfcSchema::IfcMaterial* single_material = 0; -// IfcSchema::IfcRelAssociatesMaterial::list::ptr associated_materials = product->HasAssociations()->as(); -// if (associated_materials->size() == 1) { -// IfcSchema::IfcMaterialSelect* associated_material = (*associated_materials->begin())->RelatingMaterial(); -// single_material = associated_material->as(); -// -// // NB: IfcMaterialLayerSets are also considered, regardless of --enable-layerset-slicing. Picking -// // the first material (in accordance with other viewers) when layerset-slicing is disabled. -// if (!single_material && associated_material->as()) { -// IfcSchema::IfcMaterialLayerSet* layerset = associated_material->as()->ForLayerSet(); -// if (getValue(GV_LAYERSET_FIRST) > 0.0 ? layerset->MaterialLayers()->size() >= 1 : layerset->MaterialLayers()->size() == 1) { -// IfcSchema::IfcMaterialLayer* layer = (*layerset->MaterialLayers()->begin()); -// if (layer->Material()) { -// single_material = layer->Material(); -// } -// } -// } -// } -// return single_material; -// } -// -// IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_product( -// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product) -// { -// std::stringstream representation_id_builder; -// -// representation_id_builder << representation->data().id(); -// -// IfcGeom::Representation::BRep* shape; -// IfcGeom::ConversionResults shapes, shapes2; -// -// if (!convert_shapes(representation, shapes)) { -// return 0; -// } -// -// if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { -// TopoDS_Shape merge; -// if (util::flatten_shape_list(shapes, merge, false, getValue(GV_PRECISION))) { -// if (util::count(merge, TopAbs_FACE) > 0) { -// std::vector thickness; -// std::vector layers; -// std::vector< std::vector > folded_layers; -// std::vector> styles; -// if (convert_layerset(product, layers, styles, thickness)) { -// -// IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); -// for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) { -// IfcSchema::IfcRelAssociatesMaterial* associates_material = (**it).as(); -// if (associates_material) { -// unsigned layerset_id = associates_material->RelatingMaterial()->data().id(); -// representation_id_builder << "-layerset-" << layerset_id; -// break; -// } -// } -// -// if (styles.size() > 1) { -// // If there's only a single layer there is no need to manipulate geometries. -// bool success = true; -// if (product->as() && fold_layers(product->as(), shapes, layers, thickness, folded_layers)) { -// if (util::apply_folded_layerset(shapes, folded_layers, styles, shapes2, getValue(GV_PRECISION))) { -// std::swap(shapes, shapes2); -// success = true; -// } -// } else { -// if (util::apply_layerset(shapes, layers, styles, shapes2, getValue(GV_PRECISION))) { -// std::swap(shapes, shapes2); -// success = true; -// } -// } -// -// if (!success) { -// Logger::Error("Failed processing layerset"); -// } -// } -// } -// } -// } -// } -// -// bool material_style_applied = false; -// -// const IfcSchema::IfcMaterial* single_material = get_single_material_association(product); -// if (single_material) { -// auto s = get_style(single_material); -// for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { -// if (!it->hasStyle() && s) { -// it->setStyle(s); -// material_style_applied = true; -// } -// } -// } else { -// bool some_items_without_style = false; -// for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { -// if (!it->hasStyle() && util::count(it->Shape(), TopAbs_FACE)) { -// some_items_without_style = true; -// break; -// } -// } -// if (some_items_without_style) { -// Logger::Warning("No material and surface styles for:", product); -// } -// } -// -// if (material_style_applied) { -// representation_id_builder << "-material-" << single_material->data().id(); -// } -// -// if (settings.force_space_transparency() >= 0. && product->declaration().is("IfcSpace")) { -// for (auto& s : shapes) { -// if (s.hasStyle()) { -// for (auto& p : style_cache) { -// if (p.second == s.StylePtr()) { -// std::const_pointer_cast(p.second)->Transparency() = settings.force_space_transparency(); -// } -// } -// } -// } -// } -// -// int parent_id = -1; -// try { -// IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product); -// if (parent_object && parent_object->as()) { -// parent_id = parent_object->data().id(); -// } -// } catch (const std::exception& e) { -// Logger::Error(e); -// } -// -// const std::string name = product->Name().get_value_or(""); -// const std::string guid = product->GlobalId(); -// -// gp_Trsf trsf; -// try { -// if (product->ObjectPlacement()) { -// convert(product->ObjectPlacement(), trsf); -// } -// } catch (const std::exception& e) { -// Logger::Error(e); -// } catch (...) { -// Logger::Error("Failed to construct placement"); -// } -// -// // Does the IfcElement have any IfcOpenings? -// // Note that openings for IfcOpeningElements are not processed -// IfcSchema::IfcRelVoidsElement::list::ptr openings = find_openings(product); -// -// const std::string product_type = product->declaration().name(); -// ElementSettings element_settings(settings, getValue(GV_LENGTH_UNIT), product_type); -// -// if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { -// representation_id_builder << "-openings"; -// for (IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++it) { -// representation_id_builder << "-" << (*it)->data().id(); -// } -// -// IfcGeom::ConversionResults opened_shapes; -// bool caught_error = false; -// try { -// convert_openings(product, openings, shapes, trsf, opened_shapes); -// } catch (const std::exception& e) { -// Logger::Message(Logger::LOG_ERROR, std::string("Error processing openings for: ") + e.what() + ":", product); -// caught_error = true; -// } catch (...) { -// Logger::Message(Logger::LOG_ERROR, "Error processing openings for:", product); -// } -// -// if (caught_error && opened_shapes.size() < shapes.size()) { -// opened_shapes = shapes; -// } -// -// if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { -// for (IfcGeom::ConversionResults::iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++it) { -// it->prepend(trsf); -// } -// trsf = gp_Trsf(); -// representation_id_builder << "-world-coords"; -// } -// shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), opened_shapes); -// } else if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { -// for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { -// it->prepend(trsf); -// } -// trsf = gp_Trsf(); -// representation_id_builder << "-world-coords"; -// shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), shapes); -// } else { -// shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), shapes); -// } -// -// std::string context_string = ""; -// if (representation->RepresentationIdentifier()) { -// context_string = *representation->RepresentationIdentifier(); -// } else if (representation->ContextOfItems()->ContextType()) { -// context_string = *representation->ContextOfItems()->ContextType(); -// } -// -// auto elem = new BRepElement( -// product->data().id(), -// parent_id, -// name, -// product_type, -// guid, -// context_string, -// trsf, -// boost::shared_ptr(shape), -// product -// ); -// -// if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) { -// auto rels = product->IsDefinedBy(); -// for (auto& rel : *rels) { -// if (rel->as()) { -// auto pdef = rel->as()->RelatingPropertyDefinition(); -// if (pdef->as()) { -// std::string organization_name; -// try { -// // A couple of files are not according to the schema here. -// organization_name = pdef->as()->OwnerHistory()->OwningApplication()->ApplicationDeveloper()->Name(); -// } catch (...) {} -// if (organization_name == "IfcOpenShell") { -// auto qs = pdef->as()->Quantities(); -// for (auto& q : *qs) { -// if (q->as() && q->Name() == "Total Surface Area") { -// double a_calc; -// double a_file = q->as()->AreaValue(); -// if (elem->geometry().calculate_surface_area(a_calc)) { -// double diff = std::abs(a_calc - a_file); -// if (diff / std::sqrt(a_file) > getValue(GV_PRECISION)) { -// Logger::Error("Validation of surface area failed for:", product); -// } else { -// Logger::Notice("Validation of surface area succeeded for:", product); -// } -// } else { -// Logger::Error("Validation of surface area failed for:", product); -// } -// } else if (q->as() && q->Name() == "Volume") { -// double v_calc; -// double v_file = q->as()->VolumeValue(); -// if (elem->geometry().calculate_volume(v_calc)) { -// double diff = std::abs(v_calc - v_file); -// if (diff / std::sqrt(v_file) > getValue(GV_PRECISION)) { -// Logger::Error("Validation of volume failed for:", product); -// } else { -// Logger::Notice("Validation of volume succeeded for:", product); -// } -// } else { -// Logger::Error("Validation of volume failed for:", product); -// } -// } else if (q->as() && q->Name() == "Shape Validation Properties") { -// auto qs2 = q->as()->HasQuantities(); -// bool all_succeeded = qs2->size() > 0; -// for (auto& q2 : *qs2) { -// if (q2->as() && q2->Name() == "Surface Genus" && q2->Description()) { -// int item_id = boost::lexical_cast((*q2->Description()).substr(1)); -// int genus = (int)q2->as()->CountValue(); -// for (auto& part : elem->geometry()) { -// if (part.ItemId() == item_id) { -// if (util::surface_genus(part.Shape()) != genus) { -// all_succeeded = false; -// } -// } -// } -// } -// } -// if (!all_succeeded) { -// Logger::Error("Validation of surface genus failed for:", product); -// } else { -// Logger::Notice("Validation of surface genus succeeded for:", product); -// } -// } -// } -// } -// } -// } -// } -// } -// -// return elem; -// } -// -// IfcSchema::IfcRepresentation* IfcGeom::Kernel::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) { -// IfcSchema::IfcRepresentation* representation_mapped_to = 0; -// try { -// IfcSchema::IfcRepresentationItem::list::ptr items = representation->Items(); -// if (items->size() == 1) { -// IfcSchema::IfcRepresentationptr item = *items->begin(); -// if (item->declaration().is(IfcSchema::IfcMappedItem::Class())) { -// if (item->StyledByItem()->size() == 0) { -// IfcSchema::IfcMappedptr mapped_item = item->as(); -// if (is_identity_transform(mapped_item->MappingTarget())) { -// IfcSchema::IfcRepresentationMap* map = mapped_item->MappingSource(); -// if (is_identity_transform(map->MappingOrigin())) { -// representation_mapped_to = map->MappedRepresentation(); -// } -// } -// } -// } -// } -// } catch (const IfcParse::IfcException& e) { -// Logger::Error(e); -// // @todo reset representation_mapped_to to zero? -// } -// return representation_mapped_to; -// } -// -// IfcSchema::IfcProduct::list::ptr IfcGeom::Kernel::products_represented_by(const IfcSchema::IfcRepresentation* representation) { -// IfcSchema::IfcProduct::list::ptr products(new IfcSchema::IfcProduct::list); -// -// IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation(); -// -// for (IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it) { -// // http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm -// // IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards. -// // It will be changed into an ABSTRACT supertype in future releases of IFC. -// -// // IfcProductRepresentation also lacks the INVERSE relation to IfcProduct -// // Let's find the IfcProducts that reference the IfcProductRepresentation anyway -// products->push((*it)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as()); -// } -// -// IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); -// -// if (products->size() && maps->size()) { -// Logger::Warning("Representation used by IfcRepresentationMap and IfcProductDefinitionShape", representation); -// } -// -// if (prodreps->size() > 1) { -// Logger::Warning("Multiple IfcProductDefinitionShapes for representation", representation); -// } -// -// if (maps->size() > 1) { -// Logger::Warning("Multiple IfcRepresentationMaps for representation", representation); -// } -// -// if (maps->size() == 1) { -// IfcSchema::IfcRepresentationMap* map = *maps->begin(); -// if (is_identity_transform(map->MappingOrigin())) { -// IfcSchema::IfcMappedItem::list::ptr items = map->MapUsage(); -// for (IfcSchema::IfcMappedItem::list::it it = items->begin(); it != items->end(); ++it) { -// IfcSchema::IfcMappedptr item = *it; -// if (item->StyledByItem()->size() != 0) continue; -// -// if (!is_identity_transform(item->MappingTarget())) { -// continue; -// } -// -// IfcSchema::IfcRepresentation::list::ptr reps = item->data().getInverse((&IfcSchema::IfcRepresentation::Class()), -1)->as(); -// for (IfcSchema::IfcRepresentation::list::it jt = reps->begin(); jt != reps->end(); ++jt) { -// IfcSchema::IfcRepresentation* rep = *jt; -// if (rep->Items()->size() != 1) continue; -// IfcSchema::IfcProductRepresentation::list::ptr prodreps_mapped = rep->OfProductRepresentation(); -// for (IfcSchema::IfcProductRepresentation::list::it kt = prodreps_mapped->begin(); kt != prodreps_mapped->end(); ++kt) { -// IfcSchema::IfcProduct::list::ptr ps = (*kt)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as(); -// products->push(ps); -// } -// } -// } -// } -// } -// -// return products; -// } -// -// IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_processed_representation( -// const IteratorSettings& /*settings*/, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, -// IfcGeom::BRepElement* brep) -// { -// int parent_id = -1; -// try { -// IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product); -// if (parent_object && parent_object->as()) { -// parent_id = parent_object->data().id(); -// } -// } catch (const std::exception& e) { -// Logger::Error(e); -// } -// -// const std::string name = product->Name().get_value_or(""); -// const std::string guid = product->GlobalId(); -// -// gp_Trsf trsf; -// try { -// if (product->ObjectPlacement()) { -// convert(product->ObjectPlacement(), trsf); -// } -// } catch (const std::exception& e) { -// Logger::Error(e); -// } catch (...) { -// Logger::Error("Failed to construct placement"); -// } -// -// std::string context_string = ""; -// if (representation->RepresentationIdentifier()) { -// context_string = *representation->RepresentationIdentifier(); -// } else if (representation->ContextOfItems()->ContextType()) { -// context_string = *representation->ContextOfItems()->ContextType(); -// } -// -// const std::string product_type = product->declaration().name(); -// -// return new BRepElement( -// product->data().id(), -// parent_id, -// name, -// product_type, -// guid, -// context_string, -// trsf, -// brep->geometry_pointer(), -// product -// ); -// } -// -// bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std::vector& surfaces, std::vector>& styles, std::vector& thicknesses) { -// -// } -// -// bool IfcGeom::Kernel::find_wall_end_points(const IfcSchema::IfcWall* wall, gp_Pnt& start, gp_Pnt& end) { -// IfcSchema::IfcRepresentation* axis_representation = find_representation(wall, "Axis"); -// if (!axis_representation) { -// return false; -// } -// -// ConversionResults items; -// { -// Kernel temp = *this; -// temp.setValue(GV_DIMENSIONALITY, -1.); -// temp.convert_shapes(axis_representation, items); -// } -// -// TopoDS_Vertex a, b; -// for (ConversionResults::const_iterator it = items.begin(); it != items.end(); ++it) { -// TopExp_Explorer exp(it->Shape(), TopAbs_VERTEX); -// for (; exp.More(); exp.Next()) { -// b = TopoDS::Vertex(exp.Current()); -// if (a.IsNull()) { -// a = b; -// } -// } -// } -// -// if (a.IsNull() || b.IsNull()) { -// return false; -// } -// -// start = BRep_Tool::Pnt(a); -// end = BRep_Tool::Pnt(b); -// -// return true; -// } -// -// bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const ConversionResults& items, const std::vector& surfaces, const std::vector& thicknesses, std::vector< std::vector >& result) { -// /* -// * @todo isn't it easier to do this based on the non-folded surfaces of -// * the connected walls and fold both pairs of layersets simultaneously? -// */ -// -// bool folds_made = false; -// -// IfcSchema::IfcRelConnectsPathElements::list::ptr connections(new IfcSchema::IfcRelConnectsPathElements::list); -// connections->push(wall->ConnectedFrom()->as()); -// connections->push(wall->ConnectedTo()->as()); -// -// typedef std::vector surfaces_t; -// typedef std::pair curve_on_surface; -// typedef std::vector curves_on_surfaces_t; -// typedef std::vector< std::pair< std::pair, const IfcSchema::IfcProduct*> > endpoint_connections_t; -// typedef std::vector< std::vector > result_t; -// endpoint_connections_t endpoint_connections; -// -// // Find the semantic connections to other wall elements when they are not connected 'AT_PATH' because -// // in that latter case no folds need to be made. -// for (IfcSchema::IfcRelConnectsPathElements::list::it it = connections->begin(); it != connections->end(); ++it) { -// IfcSchema::IfcRelConnectsPathElements* connection = *it; -// IfcSchema::IfcConnectionTypeEnum::Value own_type = connection->RelatedElement() == wall -// ? connection->RelatedConnectionType() -// : connection->RelatingConnectionType(); -// IfcSchema::IfcConnectionTypeEnum::Value other_type = connection->RelatedElement() == wall -// ? connection->RelatingConnectionType() -// : connection->RelatedConnectionType(); -// if (other_type != IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATPATH && -// (own_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND || -// own_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART)) { -// IfcSchema::IfcElement* other = connection->RelatedElement() == wall -// ? connection->RelatingElement() -// : connection->RelatedElement(); -// if (other->as()) { -// endpoint_connections.push_back(std::make_pair(std::make_pair(own_type, other_type), other)); -// } -// } -// } -// -// if (endpoint_connections.size() == 0) { -// return false; -// } -// -// // Count how many connections are made AT_START and AT_END respectively -// int connection_type_count[2] = { 0,0 }; -// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) { -// const int idx = it->first.first == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART; -// connection_type_count[idx] ++; -// } -// -// gp_Trsf local; -// if (wall->ObjectPlacement()) { -// if (!convert(wall->ObjectPlacement(), local)) { -// return false; -// } -// } -// local.Invert(); -// -// { -// // Copy the unfolded surfaces -// result.resize(surfaces.size()); -// std::vector< std::vector >::iterator result_it = result.begin() + 1; -// std::vector::const_iterator input_it = surfaces.begin() + 1; -// for (; input_it != surfaces.end() - 1; ++result_it, ++input_it) { -// result_it->push_back(*input_it); -// } -// } -// -// const double total_thickness = std::accumulate(thicknesses.begin(), thicknesses.end(), 0.); -// -// gp_Pnt own_axis_start, own_axis_end; -// find_wall_end_points(wall, own_axis_start, own_axis_end); -// -// // Sometimes duplicate IfcRelConnectsPathElements exist. These are detected -// // and the counts of connections are decremented accordingly. -// for (int idx = 0; idx < 2; ++idx) { -// if (connection_type_count[idx] <= 1) { -// continue; -// } -// -// /* -// IfcSchema::IfcConnectionTypeEnum::Value connection_type = idx == 1 -// ? IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART -// : IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND; -// */ -// -// std::set others; -// endpoint_connections_t::iterator it = endpoint_connections.begin(); -// while (it != endpoint_connections.end()) { -// const IfcSchema::IfcProduct* other = it->second; -// if (others.find(other) != others.end()) { -// it = endpoint_connections.erase(it); -// --connection_type_count[idx]; -// } else { -// others.insert(other); -// ++it; -// } -// } -// } -// -// // Check whether the end points are of the wall are really ~1 LayerThickness away from each other -// /* -// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) { -// IfcSchema::IfcConnectionTypeEnum::Value own_type = it->first.first; -// IfcSchema::IfcConnectionTypeEnum::Value other_type = it->first.second; -// -// gp_Pnt other_axis_start, other_axis_end; -// find_wall_end_points(it->second->as(), other_axis_start, other_axis_end); -// -// gp_Trsf other; -// if (!convert(it->second->ObjectPlacement(), other)) { -// continue; -// } -// -// other.Transforms(other_axis_start.ChangeCoord()); -// local.Transforms(other_axis_start.ChangeCoord()); -// other.Transforms(other_axis_end.ChangeCoord()); -// local.Transforms(other_axis_end.ChangeCoord()); -// -// const gp_Pnt& a = own_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART -// ? own_axis_start -// : own_axis_end; -// -// const gp_Pnt& b = other_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART -// ? other_axis_start -// : other_axis_end; -// -// const double d = a.Distance(b); -// } -// */ -// -// const double length_required = endpoint_connections.size() * total_thickness; -// // @todo this is not precisely the distance in case of curved walls. Also, it's safer -// // to first reproject the body onto the axis to get the precise curve parametrization -// // range. It's only a safeguard though, so can probably be approximated. -// const double axis_length = own_axis_start.Distance(own_axis_end); -// if (length_required > axis_length) { -// Logger::Warning("The wall axis is not long enough to accommodate the fold points"); -// return false; -// } -// -// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) { -// IfcSchema::IfcConnectionTypeEnum::Value connection_type = it->first.first; -// -// // If more than one wall connects to this start/end -point assume layers do not need to be folded -// const int idx = connection_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART; -// if (connection_type_count[idx] > 1) continue; -// -// // Pick the corresponding point from the axis -// const gp_Pnt& own_end_point = connection_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND -// ? own_axis_end -// : own_axis_start; -// const IfcSchema::IfcProduct* other_wall = it->second; -// -// gp_Trsf other; -// if (other_wall->ObjectPlacement()) { -// if (!convert(other_wall->ObjectPlacement(), other)) { -// Logger::Error("Failed to convert placement", other_wall); -// continue; -// } -// } -// -// IfcSchema::IfcRepresentation* axis_representation = find_representation(other_wall, "Axis"); -// -// if (!axis_representation) { -// Logger::Warning("Joined wall has no axis representation", other_wall); -// continue; -// } -// -// ConversionResults axis_items; -// { -// Kernel temp = *this; -// temp.setValue(GV_DIMENSIONALITY, -1.); -// temp.convert_shapes(axis_representation, axis_items); -// } -// -// TopoDS_Shape axis_shape; -// util::flatten_shape_list(axis_items, axis_shape, false, getValue(GV_PRECISION)); -// -// // local and other are IfcLocalPlacements and therefore have a unit -// // scale factor that can be applied by means of TopoDS_Shape::Move() -// axis_shape.Move(other); -// axis_shape.Move(local); -// -// TopoDS_Shape body_shape; -// util::flatten_shape_list(items, body_shape, false, getValue(GV_PRECISION)); -// -// // Create a single paremetric range over a single curve -// // that represents the entire 1d domain of the other wall -// // Sometimes there are multiple edges in the Axis shape -// // but it is assumed these are colinear. -// Handle_Geom_Curve other_axis_curve; -// double axis_u1, axis_u2; -// { -// TopExp_Explorer exp(axis_shape, TopAbs_EDGE); -// if (!exp.More()) { -// return false; -// } -// -// TopoDS_Edge axis_edge = TopoDS::Edge(exp.Current()); -// other_axis_curve = BRep_Tool::Curve(axis_edge, axis_u1, axis_u2); -// -// gp_Pnt other_a_1, other_a_2; -// other_axis_curve->D0(axis_u1, other_a_1); -// other_axis_curve->D0(axis_u2, other_a_2); -// -// if (axis_u2 < axis_u1) { -// std::swap(axis_u1, axis_u2); -// } -// exp.Next(); -// -// for (; exp.More(); exp.Next()) { -// TopoDS_Edge axis_edge2 = TopoDS::Edge(exp.Current()); -// TopExp_Explorer exp2(axis_edge2, TopAbs_VERTEX); -// for (; exp2.More(); exp2.Next()) { -// gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(exp2.Current())); -// gp_Pnt pp; -// double u, d; -// if (util::project(other_axis_curve, p, pp, u, d)) { -// if (u < axis_u1) axis_u1 = u; -// if (u > axis_u2) axis_u2 = u; -// } -// } -// } -// } -// -// double layer_offset = 0; -// -// std::vector::const_iterator thickness = thicknesses.begin(); -// result_t::iterator result_vector = result.begin() + 1; -// -// // nb The first layer is never folded, because it corresponds -// // to one of the longitudinal faces of the wall. Hence the +1 -// for (surfaces_t::const_iterator jt = surfaces.begin() + 1; jt != surfaces.end() - 1; ++jt, ++result_vector) { -// layer_offset += *thickness++; -// -// bool found_intersection = false, parallel = false; -// boost::optional point_outside_param_range; -// -// const Handle_Geom_Surface& surface = *jt; -// -// // Find the intersection point between the layerset surface -// // and the other axis curve. If it's within the parametric -// // range of the other wall it means the walls are connected -// // with an angle. -// GeomAPI_IntCS intersections(other_axis_curve, surface); -// if (intersections.IsDone() && intersections.NbPoints() == 1) { -// const gp_Pnt& p = intersections.Point(1); -// -// double u, v, w; -// intersections.Parameters(1, u, v, w); -// -// gp_Pnt Pc, Ps; -// gp_Vec Vc, Vs1, Vs2; -// other_axis_curve->D1(w, Pc, Vc); -// surface->D1(u, v, Ps, Vs1, Vs2); -// Vs1.Cross(Vs2); -// -// if (Vs1.IsNormal(Vc, 1.e-5)) { -// Logger::Warning("Connected walls are parallel"); -// parallel = true; -// } else if (w < axis_u1 || w > axis_u2) { -// point_outside_param_range = p; -// } else { -// // Found an intersection. Layer end point is covered by connecting wall -// found_intersection = true; -// break; -// } -// } -// -// if (!parallel && !found_intersection && point_outside_param_range) { -// -// /* -// Is there a bug in Open Cascade related to the intersection -// of offset surfaces constructed from linear extrusions? -// Handle_Geom_Surface xy = new Geom_Plane(gp::Origin(), gp::DZ()); -// // Handle_Geom_Surface yz = new Geom_Plane(gp::Origin(), gp::DX()); -// // Handle_Geom_Surface yz2 = new Geom_OffsetSurface(yz, 1.); -// Handle_Geom_Curve ln = new Geom_Line(gp::Origin(), gp::DX()); -// Handle_Geom_Surface yz = new Geom_SurfaceOfLinearExtrusion(ln, gp::DZ()); -// Handle_Geom_Surface yz2 = new Geom_OffsetSurface(yz, 1.); -// intersect(xy, yz2); -// */ -// -// Handle_Geom_Surface plane = new Geom_Plane(*point_outside_param_range, gp::DZ()); -// -// // vertical edges at wall end point face. -// curves_on_surfaces_t layer_ends; -// util::intersect(surface, body_shape, layer_ends); -// -// Handle_Geom_Curve layer_body_intersection; -// Handle_Geom_Surface body_surface; -// double mind = std::numeric_limits::infinity(); -// for (curves_on_surfaces_t::const_iterator kt = layer_ends.begin(); kt != layer_ends.end(); ++kt) { -// gp_Pnt p; -// gp_Vec v; -// double u, d; -// kt->second->D1(0., p, v); -// if (ALMOST_THE_SAME(0., v.Dot(gp::DZ()))) { -// // Filter horizontal curves -// continue; -// } -// // Find vertical wall end point edge closest to end point associated with semantic connection -// if (util::project(kt->second, own_end_point, p, u, d)) { -// // In addition to closest, there is a length threshold based on thickness. -// // @todo ideally, first, the point closest to end-point is selected, and -// // after that the parallel check is performed. But threshold probably -// // functions good enough. -// if (d < total_thickness * 3 && d < mind) { -// GeomAdaptor_Curve GAC(other_axis_curve); -// GeomAdaptor_Surface GAS(kt->first); -// -// Extrema_ExtCS x(GAC, GAS, getValue(GV_PRECISION), getValue(GV_PRECISION)); -// -// if (x.IsParallel()) { -// body_surface = kt->first; -// layer_body_intersection = kt->second; -// mind = d; -// } -// } -// } -// } -// -// if (body_surface.IsNull()) { -// continue; -// } -// -// // Intersect vertical edge with ground plane for point. -// GeomAPI_IntCS intersection2(layer_body_intersection, plane); -// if (intersection2.IsDone() && intersection2.NbPoints() == 1) { -// const gp_Pnt& layer_end_point = intersection2.Point(1); -// -// // Intersect layerset surface with ground plane -// GeomAPI_IntSS intersection3(surface, plane, 1.e-7); -// if (intersection3.IsDone() && intersection3.NbLines() == 1) { -// Handle_Geom_Curve layer_line = intersection3.Line(1); -// GeomAdaptor_Curve layer_line_adaptor(layer_line); -// ShapeAnalysis_Curve sac; -// gp_Pnt layer_end_point_projected; double layer_end_point_param; -// sac.Project(layer_line, layer_end_point, 1e-3, layer_end_point_projected, layer_end_point_param, false); -// -// // Move point inwards by distance from other layerset -// GCPnts_AbscissaPoint dst(layer_line_adaptor, layer_offset, layer_end_point_param); -// if (dst.IsDone()) { -// // Convert parameter to point -// gp_Pnt layer_fold_point; -// layer_line->D0(dst.Parameter(), layer_fold_point); -// -// GeomAPI_IntSS intersection4(body_surface, plane, 1.e-7); -// if (intersection4.IsDone() && intersection4.NbLines() == 1) { -// Handle_Geom_Curve body_trim_curve = intersection4.Line(1); -// ShapeAnalysis_Curve sac2; -// gp_Pnt layer_fold_point_projected; double layer_fold_point_param; -// sac2.Project(body_trim_curve, layer_fold_point, 1.e-7, layer_fold_point_projected, layer_fold_point_param, false); -// Handle_Geom_Curve fold_curve = new Geom_OffsetCurve(body_trim_curve->Reversed(), layer_fold_point_projected.Distance(layer_fold_point), gp::DZ()); -// -// Handle_Geom_Surface fold_surface = new Geom_SurfaceOfLinearExtrusion(fold_curve, gp::DZ()); -// result_vector->push_back(fold_surface); -// folds_made = true; -// } -// } -// } -// } -// -// } -// -// } -// } -// -// return folds_made; -// } -// -// IfcSchema::IfcRepresentation* IfcGeom::Kernel::find_representation(const IfcSchema::IfcProduct* product, const std::string& identifier) { -// if (!product->Representation()) return 0; -// IfcSchema::IfcProductRepresentation* prod_rep = product->Representation(); -// IfcSchema::IfcRepresentation::list::ptr reps = prod_rep->Representations(); -// for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) { -// if ((**it).RepresentationIdentifier() && (*(**it).RepresentationIdentifier()) == identifier) { -// return *it; -// } -// } -// return 0; -// } -// -// const IfcSchema::IfcRepresentationptr IfcGeom::Kernel::find_item_carrying_style(const IfcSchema::IfcRepresentationptr item) { -// if (item->StyledByItem()->size()) { -// return item; -// } -// -// while (item->declaration().is(IfcSchema::IfcBooleanResult::Class())) { -// // All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of -// // IfcGeometricRepresentationItem -// item = item->as()->FirstOperand()->as(); -// if (item && item->StyledByItem()->size()) { -// return item; -// } -// } -// -// // TODO: Ideally this would be done for other entities (such as IfcCsgSolid) as well. -// // But neither are these very prevalent, nor does the current IfcOpenShell style -// // mechanism enable to conveniently style subshapes, which would be necessary for -// // distinctly styled union operands. -// -// return item; -// } -// -// bool IfcGeom::Kernel::is_identity_transform(IfcUtil::IfcBaseInterface* l) { -// IfcSchema::IfcAxis2Placement2D* ax2d; -// IfcSchema::IfcAxis2Placement3D* ax3d; -// -// IfcSchema::IfcCartesianTransformationOperator2D* op2d; -// IfcSchema::IfcCartesianTransformationOperator3D* op3d; -// IfcSchema::IfcCartesianTransformationOperator2DnonUniform* op2dnonu; -// IfcSchema::IfcCartesianTransformationOperator3DnonUniform* op3dnonu; -// -// if ((op2dnonu = l->as()) != 0) { -// gp_GTrsf2d gtrsf2d; -// convert(op2dnonu, gtrsf2d); -// return gtrsf2d.Form() == gp_Identity; -// } else if ((op2d = l->as()) != 0) { -// gp_Trsf2d trsf2d; -// convert(op2d, trsf2d); -// return trsf2d.Form() == gp_Identity; -// } else if ((op3dnonu = l->as()) != 0) { -// gp_GTrsf gtrsf; -// convert(op3dnonu, gtrsf); -// return gtrsf.Form() == gp_Identity; -// } else if ((op3d = l->as()) != 0) { -// gp_Trsf trsf; -// convert(op3d, trsf); -// return trsf.Form() == gp_Identity; -// } else if ((ax2d = l->as()) != 0) { -// gp_Trsf2d trsf2d; -// convert(ax2d, trsf2d); -// return trsf2d.Form() == gp_Identity; -// } else if ((ax3d = l->as()) != 0) { -// gp_Trsf trsf; -// convert(ax3d, trsf); -// return trsf.Form() == gp_Identity; -// } else { -// throw IfcParse::IfcException("Invalid valuation for IfcAxis2Placement / IfcCartesianTransformationOperator"); -// } -// } -// -// void IfcGeom::Kernel::set_conversion_placement_rel_to_type(const IfcParse::declaration* type) { -// placement_rel_to_type_ = type; -// } -// -// void IfcGeom::Kernel::set_conversion_placement_rel_to_instance(const IfcUtil::IfcBaseEntity* instance) { -// placement_rel_to_instance_ = instance; -// } -// -// -// namespace { -// -// bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) { -// if (colour != 0) { -// rgb[0] = colour->Red(); -// rgb[1] = colour->Green(); -// rgb[2] = colour->Blue(); -// } -// return colour != 0; -// } -// -// bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) { -// if (factor != 0) { -// const double f = *factor; -// rgb[0] = rgb[1] = rgb[2] = f; -// } -// return factor != 0; -// } -// -// bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) { -// if (colour_or_factor == 0) { -// return false; -// } else if (colour_or_factor->declaration().is(IfcSchema::IfcColourRgb::Class())) { -// return process_colour(static_cast(colour_or_factor), rgb); -// } else if (colour_or_factor->declaration().is(IfcSchema::IfcNormalisedRatioMeasure::Class())) { -// return process_colour(static_cast(colour_or_factor), rgb); -// } else { -// return false; -// } -// } -// -// } -// -// #define Kernel POSTFIX_SCHEMA(Kernel) -// -// std::shared_ptr IfcGeom::Kernel::internalize_surface_style(const std::pair& shading_styles) { -// if (shading_styles.second == 0) { -// return 0; -// } -// int surface_style_id = shading_styles.first->data().id(); -// auto it = style_cache.find(surface_style_id); -// if (it != style_cache.end()) { -// return it->second; -// } -// -// -// IfcSchema::IfcSurfaceStyle* style = shading_styles.first->as(); -// IfcSchema::IfcSurfaceStyleShading* shading = shading_styles.second->as(); -// -// std::shared_ptr surface_style_ptr; -// -// if (style->Name()) { -// surface_style_ptr.reset(new SurfaceStyle(surface_style_id, *style->Name())); -// } else { -// surface_style_ptr.reset(new SurfaceStyle(surface_style_id)); -// } -// -// std::shared_ptr surface_style_ptr_const = std::const_pointer_cast(surface_style_ptr); -// SurfaceStyle& surface_style = *surface_style_ptr; -// -// double rgb[3]; -// if (process_colour(shading->SurfaceColour(), rgb)) { -// surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2])); -// } -// if (shading_styles.second->declaration().is(IfcSchema::IfcSurfaceStyleRendering::Class())) { -// IfcSchema::IfcSurfaceStyleRendering* rendering_style = static_cast(shading_styles.second); -// if (rendering_style->DiffuseColour() && process_colour(rendering_style->DiffuseColour(), rgb)) { -// SurfaceStyle::ColorComponent diffuse = surface_style.Diffuse().get_value_or(SurfaceStyle::ColorComponent(1, 1, 1)); -// surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(diffuse.R() * rgb[0], diffuse.G() * rgb[1], diffuse.B() * rgb[2])); -// } -// if (rendering_style->DiffuseTransmissionColour()) { -// // Not supported -// } -// if (rendering_style->ReflectionColour()) { -// // Not supported -// } -// if (rendering_style->SpecularColour() && process_colour(rendering_style->SpecularColour(), rgb)) { -// surface_style.Specular().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2])); -// } -// if (rendering_style->SpecularHighlight()) { -// IfcSchema::IfcSpecularHighlightSelect* highlight = rendering_style->SpecularHighlight(); -// if (highlight->declaration().is(IfcSchema::IfcSpecularRoughness::Class())) { -// double roughness = *((IfcSchema::IfcSpecularRoughness*)highlight); -// if (roughness >= 1e-9) { -// surface_style.Specularity().reset(1.0 / roughness); -// } -// } else if (highlight->declaration().is(IfcSchema::IfcSpecularExponent::Class())) { -// surface_style.Specularity().reset(*((IfcSchema::IfcSpecularExponent*)highlight)); -// } -// } -// if (rendering_style->TransmissionColour()) { -// // Not supported -// } -// if (rendering_style->Transparency()) { -// const double d = *rendering_style->Transparency(); -// surface_style.Transparency().reset(d); -// } -// } -// return style_cache[surface_style_id] = surface_style_ptr_const; -// } -// -// std::shared_ptr IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationptr item) { -// return internalize_surface_style(get_surface_style(item)); -// } -// -// std::shared_ptr IfcGeom::Kernel::get_style(const IfcSchema::IfcMaterial* material) { -// IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation(); -// for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) { -// IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations(); -// IfcSchema::IfcStyledItem::list::ptr styles(new IfcSchema::IfcStyledItem::list); -// for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) { -// styles->push((**it).Items()->as()); -// } -// for (IfcSchema::IfcStyledItem::list::it it = styles->begin(); it != styles->end(); ++it) { -// const std::pair ss = get_surface_style(*it); -// if (ss.second) { -// return internalize_surface_style(ss); -// } -// } -// } -// auto material_style = std::make_shared(material->data().id(), material->Name()); -// return style_cache[material->data().id()] = material_style; -// } -// -// void IfcGeom::Kernel::apply_layerset(IfcGeom::ConversionResults& r, const ifcopenshell::geometry::layerset_information& info) { -// convert(info.layers); -// -// if (info.layers.empty()) { -// return; -// } -// -// if (axis_curve->DynamicType() == STANDARD_TYPE(Geom_Line)) { -// Handle_Geom_Line axis_line = Handle_Geom_Line::DownCast(axis_curve); -// // @todo note that this creates an offset into the wrong order, the cross product arguments should be -// // reversed. This causes some inversions later on, e.g. if(positive) { reverse(); } -// reference_surface = new Geom_Plane(axis_line->Lin().Location(), axis_line->Lin().Direction() ^ gp::DZ()); -// } else if (axis_curve->DynamicType() == STANDARD_TYPE(Geom_Circle)) { -// // @todo note that in this branch this inversion does not seem to take place. -// Handle_Geom_Circle axis_line = Handle_Geom_Circle::DownCast(axis_curve); -// reference_surface = new Geom_CylindricalSurface(axis_li->Position(), axis_line->Radius()); -// } else { -// Logger::Message(Logger::LOG_ERROR, "Unsupported underlying curve of Axis representation:", product); -// return false; -// } -// -// IfcGeom::ConversionResults r2; -// if (IfcGeom::util::apply_layerset(r, const std::vector&, ConversionResults& r2, double tol)) { -// std::swap(r, r2) -// } -// } \ No newline at end of file diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h index 61b1a2c548..add0c15a25 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -56,6 +56,21 @@ #include "../../../ifcgeom/taxonomy.h" #include "../../../ifcgeom/ConversionSettings.h" +namespace { +template +bool handle_occt_exception(Fn&& fn) { + try { + return std::forward(fn)(); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + throw std::runtime_error(e.GetMessageString()); + } else { + throw std::runtime_error("Unknown error creating geometry"); + } + } +} +} + namespace IfcGeom { class IFC_GEOMLIBRARY_API OpenCascadeKernel : public ifcopenshell::geometry::kernels::AbstractKernel { diff --git a/src/ifcgeom/kernels/opencascade/boolean_result.cpp b/src/ifcgeom/kernels/opencascade/boolean_result.cpp index 6750108a90..64b65f5f2a 100644 --- a/src/ifcgeom/kernels/opencascade/boolean_result.cpp +++ b/src/ifcgeom/kernels/opencascade/boolean_result.cpp @@ -84,6 +84,7 @@ namespace { } bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result::ptr br, ConversionResults& results) { + return handle_occt_exception([&]() -> bool { bool valid_result = false; bool first = true; const double tol = settings_.get().get(); @@ -196,4 +197,5 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result::ptr br, Con )); return true; + }); } diff --git a/src/ifcgeom/kernels/opencascade/extrusion.cpp b/src/ifcgeom/kernels/opencascade/extrusion.cpp index b1152a4158..631c382bf8 100644 --- a/src/ifcgeom/kernels/opencascade/extrusion.cpp +++ b/src/ifcgeom/kernels/opencascade/extrusion.cpp @@ -72,6 +72,8 @@ bool OpenCascadeKernel::convert(const taxonomy::extrusion::ptr extrusion, TopoDS } bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion::ptr extrusion, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; if (!convert(extrusion, shape)) { return false; @@ -84,4 +86,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion::ptr extrusion, I extrusion->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/face.cpp b/src/ifcgeom/kernels/opencascade/face.cpp index b2313487c1..661b725c1d 100644 --- a/src/ifcgeom/kernels/opencascade/face.cpp +++ b/src/ifcgeom/kernels/opencascade/face.cpp @@ -599,6 +599,8 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re } bool OpenCascadeKernel::convert_impl(const taxonomy::face::ptr face, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; if (!convert(face, shape)) { return false; @@ -609,4 +611,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::face::ptr face, IfcGeom::Co face->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/loft.cpp b/src/ifcgeom/kernels/opencascade/loft.cpp index b055b7743b..719e089278 100644 --- a/src/ifcgeom/kernels/opencascade/loft.cpp +++ b/src/ifcgeom/kernels/opencascade/loft.cpp @@ -427,6 +427,8 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re } bool OpenCascadeKernel::convert_impl(const taxonomy::loft::ptr loft, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; if (!convert(loft, shape)) { return false; @@ -438,4 +440,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::loft::ptr loft, IfcGeom::Co loft->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/loop.cpp b/src/ifcgeom/kernels/opencascade/loop.cpp index 581cbf8567..5d8a538716 100644 --- a/src/ifcgeom/kernels/opencascade/loop.cpp +++ b/src/ifcgeom/kernels/opencascade/loop.cpp @@ -378,6 +378,8 @@ bool OpenCascadeKernel::convert(const taxonomy::loop::ptr loop, TopoDS_Wire& wir } bool OpenCascadeKernel::convert_impl(const taxonomy::loop::ptr loop, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Wire shape; if (!convert(loop, shape)) { return false; @@ -389,9 +391,13 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::loop::ptr loop, IfcGeom::Co loop->surface_style )); return true; + + }); } bool OpenCascadeKernel::convert_impl(const taxonomy::edge::ptr edge, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Wire shape = boost::get(convert_curve(edge)); results.emplace_back(ConversionResult( @@ -400,4 +406,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::edge::ptr edge, IfcGeom::Co edge->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/shell.cpp b/src/ifcgeom/kernels/opencascade/shell.cpp index eacc87e12b..4456895b12 100644 --- a/src/ifcgeom/kernels/opencascade/shell.cpp +++ b/src/ifcgeom/kernels/opencascade/shell.cpp @@ -107,6 +107,8 @@ bool OpenCascadeKernel::convert(const taxonomy::shell::ptr l, TopoDS_Shape& shap } bool OpenCascadeKernel::convert_impl(const taxonomy::shell::ptr shell, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; if (!convert(shell, shape)) { return false; @@ -118,4 +120,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::shell::ptr shell, IfcGeom:: shell->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/solid.cpp b/src/ifcgeom/kernels/opencascade/solid.cpp index 4f308b0b06..fb38c29789 100644 --- a/src/ifcgeom/kernels/opencascade/solid.cpp +++ b/src/ifcgeom/kernels/opencascade/solid.cpp @@ -102,6 +102,8 @@ bool OpenCascadeKernel::convert(const taxonomy::solid::ptr solid, TopoDS_Shape& } bool OpenCascadeKernel::convert_impl(const taxonomy::solid::ptr solid, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; if (!convert(solid, shape)) { return false; @@ -113,4 +115,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::solid::ptr solid, IfcGeom:: solid->surface_style )); return true; + + }); } diff --git a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp index c971291868..510c8f182d 100644 --- a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp +++ b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp @@ -308,6 +308,8 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo } bool OpenCascadeKernel::convert_impl(const taxonomy::sweep_along_curve::ptr scs, IfcGeom::ConversionResults& results) { + return handle_occt_exception([&]() -> bool { + TopoDS_Shape shape; // For tiny radii occt will fail building the sweep, in which case we enlarge the inputs to occt, and add a scale matrix to the output bool enlarged = false; @@ -352,4 +354,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::sweep_along_curve::ptr scs, scs->surface_style )); return true; + + }); }