diff --git a/src/bonsai/bonsai/bim/module/light/list.py b/src/bonsai/bonsai/bim/module/light/list.py index b0dbd141cb..0536284e7a 100644 --- a/src/bonsai/bonsai/bim/module/light/list.py +++ b/src/bonsai/bonsai/bim/module/light/list.py @@ -30,6 +30,7 @@ class MATERIAL_UL_radiance_materials(bpy.types.UIList): if self.layout_type in {"DEFAULT", "COMPACT"}: row = layout.row(align=True) + # Adding color preview or glass icon if item.category == "Glass": row.label(icon="SHADING_TEXTURE") else: diff --git a/src/bonsai/bonsai/bim/module/light/operator.py b/src/bonsai/bonsai/bim/module/light/operator.py index 3806185a4d..aaf7f2fc57 100644 --- a/src/bonsai/bonsai/bim/module/light/operator.py +++ b/src/bonsai/bonsai/bim/module/light/operator.py @@ -386,7 +386,6 @@ ground_glow source ground self.report({"INFO"}, "Exported Scene file to: {}".format(scene_file)) print("Setting up Radiance scene...") - scene = pr.Scene("ascene") material_path = os.path.join(output_dir, "materials.rad") diff --git a/src/bonsai/bonsai/bim/module/spatial/operator.py b/src/bonsai/bonsai/bim/module/spatial/operator.py index 8d59dfa223..932b105acf 100644 --- a/src/bonsai/bonsai/bim/module/spatial/operator.py +++ b/src/bonsai/bonsai/bim/module/spatial/operator.py @@ -247,10 +247,17 @@ class ToggleContainerElement(bpy.types.Operator): bl_idname = "bim.toggle_container_element" bl_label = "Toggle Container Element" bl_options = {"REGISTER", "UNDO"} + bl_description = "Toggle children\nALT+CLICK to recursively toggle children" element_index: bpy.props.IntProperty() + is_recursive: bpy.props.BoolProperty(name="Is Recursive", default=False, options={"SKIP_SAVE"}) + + def invoke(self, context, event): + if event.type == "LEFTMOUSE" and event.alt: + self.is_recursive = True + return self.execute(context) def execute(self, context): - core.toggle_container_element(tool.Spatial, element_index=self.element_index) + core.toggle_container_element(tool.Spatial, element_index=self.element_index, is_recursive=self.is_recursive) return {"FINISHED"} diff --git a/src/bonsai/bonsai/core/spatial.py b/src/bonsai/bonsai/core/spatial.py index 9a200626e4..ae05fdef08 100644 --- a/src/bonsai/bonsai/core/spatial.py +++ b/src/bonsai/bonsai/core/spatial.py @@ -141,8 +141,8 @@ def delete_container( spatial.import_spatial_decomposition() -def toggle_container_element(spatial: tool.Spatial, element_index: int) -> None: - spatial.toggle_container_element(element_index) +def toggle_container_element(spatial: tool.Spatial, element_index: int, is_recursive: bool) -> None: + spatial.toggle_container_element(element_index, is_recursive=is_recursive) spatial.load_contained_elements() diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index 9b8cdbf46c..d9a6a75a10 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -199,14 +199,8 @@ class Spatial(bonsai.core.tool.Spatial): src_obj.location.xy = destination_obj.location.xy @classmethod - def load_contained_elements(cls) -> None: + def get_grouped_elements_in_container(cls, container: ifcopenshell.entity_instance) -> dict: props = bpy.context.scene.BIMSpatialDecompositionProperties - props.elements.clear() - if not (container := props.active_container): - return - - container = tool.Ifc.get().by_id(container.ifc_definition_id) - results: defaultdict[str, dict[int, Any]] = defaultdict(dict) if props.should_include_children: elements = ifcopenshell.util.element.get_decomposition(container, is_recursive=True) @@ -222,25 +216,39 @@ class Spatial(bonsai.core.tool.Spatial): class_data = results.setdefault(ifc_class, {}) type_data = class_data.setdefault(ifc_definition_id, {"type_name": type_name, "elements": []}) type_data["elements"].append(element) + return results + + @classmethod + def load_contained_elements(cls) -> None: + props = bpy.context.scene.BIMSpatialDecompositionProperties + props.elements.clear() + if not (container := props.active_container): + return + + container = tool.Ifc.get().by_id(container.ifc_definition_id) + results = cls.get_grouped_elements_in_container(container) expanded_elements = json.loads(props.expanded_elements) expanded_classes = expanded_elements.get("CLASS", []) expanded_ifc_ids = expanded_elements.get("IFC_ID", []) expanded_untyped = expanded_elements.get("UNTYPED_CLASSES", []) + expanded_classes_r = expanded_elements.get("CLASS_R", []) + total_elements = 0 for ifc_class in sorted(results.keys()): new = props.elements.add() new.name = ifc_class new.type = "CLASS" class_is_expanded = ifc_class in expanded_classes - new.is_expanded = class_is_expanded + class_is_expanded_r = ifc_class in expanded_classes_r + new.is_expanded = class_is_expanded or class_is_expanded_r total = 0 for ifc_definition_id in sorted( results[ifc_class].keys(), key=lambda x: results[ifc_class][x]["type_name"] ): type_data = results[ifc_class][ifc_definition_id] total2 = len(type_data["elements"]) - if class_is_expanded: + if new.is_expanded: new2 = props.elements.add() new2.type = "TYPE" new2.name = type_data["type_name"] @@ -251,9 +259,9 @@ class Spatial(bonsai.core.tool.Spatial): type_is_expanded = ifc_class in expanded_untyped else: type_is_expanded = ifc_definition_id in expanded_ifc_ids - new2.is_expanded = type_is_expanded + new2.is_expanded = type_is_expanded or class_is_expanded_r - if type_is_expanded: + if new2.is_expanded: for element in type_data["elements"]: occurrence = props.elements.add() occurrence.name = element.Name or "Unnamed" @@ -363,25 +371,53 @@ class Spatial(bonsai.core.tool.Spatial): props.contracted_containers = json.dumps(contracted_containers) @classmethod - def toggle_container_element(cls, element_index: int) -> None: + def toggle_container_element(cls, element_index: int, is_recursive: bool) -> None: props = bpy.context.scene.BIMSpatialDecompositionProperties + expanded_elements: dict[str, list[Union[str, int]]] = json.loads(props.expanded_elements) + element = props.elements[element_index] if element.type == "CLASS": element_type = "CLASS" filtered_item = element.name - else: + elif element.type == "TYPE": if element.ifc_definition_id == 0: element_type = "UNTYPED_CLASSES" filtered_item = element.ifc_class else: element_type = "IFC_ID" filtered_item = element.ifc_definition_id + else: + return + expanded_elements_list: list[Union[str, int]] = expanded_elements.setdefault(element_type, []) if filtered_item in expanded_elements_list: expanded_elements_list.remove(filtered_item) + should_expand = False else: expanded_elements_list.append(filtered_item) + should_expand = True + + if is_recursive and element.type == "CLASS": + container = tool.Ifc.get().by_id(props.active_container.ifc_definition_id) + results = cls.get_grouped_elements_in_container(container) + for ifc_class in results.keys(): + if ifc_class != element.name: + continue + for ifc_definition_id in results[ifc_class].keys(): + if ifc_definition_id == 0: + element_type = "UNTYPED_CLASSES" + filtered_item = ifc_class + else: + element_type = "IFC_ID" + filtered_item = ifc_definition_id + + expanded_elements_list = expanded_elements.setdefault(element_type, []) + if should_expand is False and filtered_item in expanded_elements_list: + expanded_elements_list.remove(filtered_item) + elif should_expand is True and filtered_item not in expanded_elements_list: + expanded_elements_list.append(filtered_item) + props.expanded_elements = json.dumps(expanded_elements) # HERE STARTS SPATIAL TOOL diff --git a/src/bonsai/docs/quickstart/explore_model.rst b/src/bonsai/docs/quickstart/explore_model.rst index 26851df24b..3ccc73830c 100644 --- a/src/bonsai/docs/quickstart/explore_model.rst +++ b/src/bonsai/docs/quickstart/explore_model.rst @@ -121,49 +121,34 @@ pressing the button on the top right of the **Viewport** panel. The hotkey is Overview of all objects ----------------------- -The **Outliner** panel on the top right shows a hierarchy of all the currently -loaded physical **Objects** in your IFC project. These **Objects** correlate to -what you can see in the **Viewport** panel. +All physical **Elements** are organised in a hierarchy of **Spatial +Containers**. By default, this hierarchy represents a breakdown of spaces, from +large spaces such as a site and a building, down to smaller spaces like +building storeys and room spaces. The hierarchy will always begin with an +**IfcProject**. -Every **Object** in the **Outliner** represents an IFC **Element**. These -**Objects** have a name with the pattern ``Class/Name``. The class prefix -represents the type of object, and the name is the name of the object. Examples -of classes you will see are ``IfcBuilding``, or ``IfcWall``. This naming -convention makes it easy to quickly spot types of objects. +The :menuselection:`Properties --> Project Overview --> Spatial Decomposition` +panel shows this hierarchy. Below, it shows a list of all **Elements** +contained inside the actively selected **Spatial Container**. -.. image:: images/outliner.png +.. image:: images/spatial-tree.png -Objects are organised in a hierarchy. By default, this hierarchy represents a -breakdown of spaces, from large spaces such as a site and a building, down to -smaller spaces like building storeys and room spaces. The hierarchy will always -begin with an **IfcProject** object. You can click on the triangle to toggle the -hierarchy. +With a **Spatial Container** selected, use the **Isolate** button or **Hide / +Show Icons** to quickly focus or control visibility. Use the search filters at +the bottom of the **Container** or **Element** lists to quickly find objects, +and use the **Select Icon** to select them. + +.. image:: images/spatial-tree-features.png + +**Elements** are grouped into IFC **Classes**, such as Wall, Slab, or Door. +Within that, **Elements** are grouped into **Construction Types**. You'll see a +count of how many objects of that type exist. In the example above, there is +only one stair contained in the building storey. .. tip:: - In large projects with deep hierarchies, you can ``Shift-LMB`` click the - triangle to recursively toggle the hierarchy. You can also click and drag the - ``MMB`` to pan left and right. - -When there are lots of objects, you can type a name in the filter box to quickly -identify objects by name or type. - -.. image:: images/outliner-filter.png - -Clicking on an object in the **Outliner** panel also selects the corresponding -object in the **Viewport** panel. A good strategy to find objects is to then use -``View > Frame Selected`` to zoom to it in the **Viewport**. - -The **Outliner** panel is also great for isolating portions of your project. You -can include and exclude portions by clicking on the **Tick Icon** next to -collections of objects in the hierarchy. - -Let's isolate a single building storey. Start by disabling the **Tick Icon** -next to the **IfcProject** collection. This will hide everything in the project. -Then navigate through the hierarchy and enable the **Tick Icon** next to an -**IfcBuildingStory**. - -.. image:: images/outliner-isolate.png + Hold :kbd:`Alt` when clicking on triangles in the hierarchy to show / hide + children recursively. Viewing element classes ----------------------- @@ -185,16 +170,15 @@ properties and relationships it is allowed to have. For example, a Wall worry about memorising all the available **Classes**, you'll get a feel for them as you explore more. -To view an object's class, click on an object in the **Viewport** or **Outliner** -panel, then switch to the **Object Information** tab in the **Properties** panel. -You can see the **Class** name in the **Object Metadata** subpanel. +To view an object's class, click on an object in the :menuselection:`3D +Viewport`, then go to :menuselection:`Properties --> Object Information --> +Object Metadata` to see the **Class** name. .. image:: images/element-class.png -In this case, the **Class** of our roof is an **IfcSlab**. You'll notice this is -the same **Class** name used as a prefix for the object name in the **Outliner** -panel. You can also see the name of the actively selected object in the top left -of the **Viewport** panel. +In this case, the **Class** of our roof is an **IfcSlab**. You can also see the +name of the actively selected object in the top left of the :menuselection:`3D +Viewport`. .. warning:: @@ -219,24 +203,26 @@ not see it all the time. classes and predefined types you should see. Press the **Select Icon** to select all objects that are of the same -**IfcSlab** **Class**. Then, you can isolate these elements by going to ``Object -> Show/Hide > Hide Unselected`` (hotkey ``Shift-H``). To show all elements again, you can use -``Object > Show/Hide > Show Hidden Objects`` (hotkey ``Alt-H``). If you want to -hide elements instead, you can use ``Object > Show/Hidden > Hide Selected`` -(hotkey ``H``). +**IfcSlab** **Class**. Then, you can isolate these elements by going to +:menuselection:`3D Viewport --> Object --> Show/Hide --> Hide Unselected` +(:kbd:`Shift-H`). To show all elements again, you can use :menuselection:`3D +Viewport --> Object --> Show/Hide --> Show Hidden Objects` (:kbd:`Alt-H`). If +you want to hide elements instead, you can use :menuselection:`3D Viewport --> +Object --> Show/Hidden --> Hide Selected` (:kbd:`H`). .. image:: images/element-class-select.png .. note:: Remember that Blender's hotkeys are context sensitive. Make sure your mouse - is hovering over the **Viewport** panel when you press a hotkey or no cake - for you. + is hovering over the :menuselection:`Viewport` panel when you press a hotkey + or no cake for you. You can also see statistics about the number of selected objects. If you right -click on the bottom right status bar and enable **Scene Statistics** you will -see information like **Objects 4/4**, which means that 4 objects are selected -out of 4 available objects. This is a great way of counting objects like toilets. +click on the bottom right :menuselection:`Status Bar` and enable **Scene +Statistics** you will see information like **Objects 4/4**, which means that 4 +objects are selected out of 4 available objects. This is a great way of +counting objects like toilets. .. image:: images/scene-statistics.png @@ -244,7 +230,7 @@ Viewing attributes and properties --------------------------------- You can view the **Attributes**, **Properties**, and **Quantities** of the -selected object in the **Object Properties** tab. +selected object in the :menuselection:`Properties --> Object Information` tab. Let's focus on **Attributes** first. Scroll down to the **Attributes** subpanel. **Attributes** are a limited set of fundamental data (usually less @@ -279,7 +265,8 @@ have different **Properties** depending on what information they want to store. Each **Property** has a name and a value, and are grouped into **Property Sets**. Each **Property Set** also has a name. -You can find **Properties** in the **Object Property Sets** subpanel. +You can find **Properties** in the :menuselection:`Properties --> Object +Information --> Property Sets` panel. .. image:: images/psets.png @@ -303,7 +290,8 @@ value, and are grouped into **Quantity Sets**. Similarly, there are also common quantities defined as part of the international standard, denoted by the prefix ``Qto_``. This prefix is short for "Quantity Take-Off". -You can find **Quantities** in the **Object Quantity Sets** subpanel. +You can find **Quantities** in the :menuselection:`Properties --> Object +Information --> Quantity Sets` panel. .. image:: images/qtos.png @@ -313,16 +301,17 @@ Finding the location of objects Every object in the built environment has a location in the world. For example, a chair will be located in a space, and a wall is typically located in a building storey. You've already seen this hierarchy of spaces in the -**Outliner** panel, where an IFC project is broken down into site, building, -storeys, and spaces. +:menuselection:`Properties --> Project Overview --> Spatial Decomposition` +panel, where an IFC project is broken down into site, building, storeys, and +spaces. Sometimes, objects may have multiple relevant locations, such as a multi-storey column which can be related to multiple building storeys. Even in these cases, IFC enforces one location to be its primary location, known as its **Spatial Container**. -If you click on any object, you can see its location in the **Spatial -Container** subpanel in the **Object Information** tab. +If you click on any object, you can see its location in the +:menuselection:`Properties --> Object Information --> Spatial Container` panel. Press the **Select Icon** to select all objects that are in the same location. @@ -334,20 +323,22 @@ Checking construction types Almost everything in the built environment will have a **Construction Type**. For example, an architect will specify a door type for every door in a project. -You can see a list of **Construction Types** in the **Outliner** panel in the -**Types** collection. For example, if the architect has a wall types schedule -with the wall type names of ``WT01``, ``WT02``, and ``WT03``, you should see -three **IfcWallType** objects with those same names in the **Outliner**. +You can see a list of **Construction Types** in the :menuselection:`Outliner` +in the **Types** collection. For example, if the architect has a wall types +schedule with the wall type names of ``WT01``, ``WT02``, and ``WT03``, you +should see three **IfcWallType** objects with those same names in the +**Outliner**. You can click on these types to see more details about them in the -**Properties** panel. +:menuselection:`Properties` panel. .. image:: images/outliner-types.png -When selecting an object, you can also see its construction type in **Object -Information** under the **Type** subpanel. You can press the **Select Icon** to -select all objects that are of the same **Construction Type**. You can use the -hide and isolate hotkeys to quickly view them in the model. +When selecting an object, you can also see its construction type in +:menuselection:`Properties --> Object Information --> Type`. You can press the +**Select Icon** to select all objects that are of the same **Construction +Type**. You can use the hide and isolate hotkeys to quickly view them in the +model. .. image:: images/properties-types.png @@ -361,10 +352,10 @@ geometry of the pump, so all occurrences of that pump will have the same geometry. You can visually inspect types in isolation to the rest of the model. Types are -hidden by default, so first enable the visibility of the **Types** collection in -the **Outliner** by pressing the **Visibility Icon**. Then, select a type, and -click on ``View > Local View > Toggle Local View`` (hotkey ``/``) in the -**Viewport**. Toggle the view to see the entire model again. +hidden by default, so first enable the visibility of the **Types** collection +in the **Outliner** by pressing the **Visibility Icon**. Then, select a type, +and click on :menuselection:`3D Viewport --> View --> Local View --> Toggle +Local View` (:kbd:`/`). Toggle the view to see the entire model again. .. image:: images/type-local-view.png @@ -383,8 +374,8 @@ resources. For example, a **Material** might be blockwork. Another **Material** might be in-situ concrete. **Materials** are grouped into categories like steel, concrete, brick, block, and so on. -We can see a list of **Materials** used in the project in the **Materials** -subpanel in the **Geometry and Materials** tab. +We can see a list of **Materials** used in the project in the +:menuselection:`Properties --> Geometry and Materials --> Materials` panel. Press the **Select Icon** to select all objects that are of the selected material. @@ -395,18 +386,20 @@ Taking simple measurements -------------------------- The simplest form of measurement is the one that's already taken for you. The -**Viewing attributes and properties** section describes how to view -pre-calculated **Quantities**. +:ref:`quickstart/explore_model:Viewing attributes and properties` section +describes how to view pre-calculated **Quantities**. Sometimes, you may wish to take manual measurements yourself. You can view the overall X, Y, and Z dimensions of the currently selected object in the -**Derived Coordinates** subpanel in the **Geometry and Materials** tab. +:menuselection:`Properties --> Geometry and Materials --> Placement --> Derived +Coordinates` panel. .. image:: images/dimensions.png Another way to manually measure from two points is to use the **Measure** tool. First, press the **Snap Icon** to enable snapping. Then choose snap targets in -the **Snap Menu** in the top middle section of the **Viewport** panel. +the **Snap Menu** in the top middle section of the :menuselection:`3D +Viewport`. .. image:: images/snap-targets.png @@ -418,12 +411,12 @@ the **Snap Menu** in the top middle section of the **Viewport** panel. measurements will automatically snap to the nearest object's surface. Now that you have configured snapping, press the **Measure Tool Icon** on the -left of the **Viewport** panel. **Click** and **Drag** in the 3D viewport to -take a measurement. A circle will appear guiding the first point of your -measurement. While **Dragging**, press the ``X`` key to lock the measurement -line along the X axis. Alternatively, press the ``Y`` or ``Z`` key to lock the -measurement line along the Y or Z axis. Let go of the mouse to finish your -measurement. +left of the :menuselection:`3D Viewport`. **Click** and **Drag** in the 3D +viewport to take a measurement. A circle will appear guiding the first point of +your measurement. While **Dragging**, press the ``X`` key to lock the +measurement line along the X axis. Alternatively, press the ``Y`` or ``Z`` key +to lock the measurement line along the Y or Z axis. Let go of the mouse to +finish your measurement. .. image:: images/measure-tool.png diff --git a/src/bonsai/docs/quickstart/images/outliner-filter.png b/src/bonsai/docs/quickstart/images/outliner-filter.png deleted file mode 100644 index 08cafb6632..0000000000 Binary files a/src/bonsai/docs/quickstart/images/outliner-filter.png and /dev/null differ diff --git a/src/bonsai/docs/quickstart/images/outliner-isolate.png b/src/bonsai/docs/quickstart/images/outliner-isolate.png deleted file mode 100644 index ee6cc3ff2a..0000000000 Binary files a/src/bonsai/docs/quickstart/images/outliner-isolate.png and /dev/null differ diff --git a/src/bonsai/docs/quickstart/images/outliner.png b/src/bonsai/docs/quickstart/images/outliner.png deleted file mode 100644 index d568f9ed07..0000000000 Binary files a/src/bonsai/docs/quickstart/images/outliner.png and /dev/null differ diff --git a/src/bonsai/docs/quickstart/images/spatial-tree-features.png b/src/bonsai/docs/quickstart/images/spatial-tree-features.png new file mode 100644 index 0000000000..058272b4c3 Binary files /dev/null and b/src/bonsai/docs/quickstart/images/spatial-tree-features.png differ diff --git a/src/bonsai/docs/quickstart/images/spatial-tree.png b/src/bonsai/docs/quickstart/images/spatial-tree.png new file mode 100644 index 0000000000..923c4dcc01 Binary files /dev/null and b/src/bonsai/docs/quickstart/images/spatial-tree.png differ diff --git a/src/ifcopenshell-python/test/test_entity_instance.py b/src/ifcopenshell-python/test/test_entity_instance.py new file mode 100644 index 0000000000..a07c9cdade --- /dev/null +++ b/src/ifcopenshell-python/test/test_entity_instance.py @@ -0,0 +1,60 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Thomas Krijnen +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import pytest +import test.bootstrap +import ifcopenshell +import ifcopenshell.api +import ifcopenshell.util.element + + +class TestGetInfo2(test.bootstrap.IFC4): + def test_instance_attribute(self): + brep = self.file.create_entity("IfcFacetedBrep") + shell = self.file.create_entity("IfcClosedShell") + brep.Outer = shell + assert brep.get_info_2(recursive=True) == { + "Outer": {"CfsFaces": None, "id": 2, "type": "IfcClosedShell"}, + "id": 1, + "type": "IfcFacetedBrep", + } + + def test_aggregate_of_instance_attribute(self): + shell = self.file.create_entity("IfcClosedShell") + faces = [self.file.create_entity("IfcFace") for i in range(3)] + shell.CfsFaces = faces + assert shell.get_info_2(recursive=True)["CfsFaces"] == ( + {"Bounds": None, "id": 2, "type": "IfcFace"}, + {"Bounds": None, "id": 3, "type": "IfcFace"}, + {"Bounds": None, "id": 4, "type": "IfcFace"}, + ) + + def test_aggregate_of_aggregate_of_instance_attribute(self): + surface = self.file.create_entity("IfcBSplineSurfaceWithKnots") + pp = [self.file.create_entity("IfcCartesianPoint", [float(i)]) for i in range(4)] + surface.ControlPointsList = [pp[:2], pp[2:]] + assert surface.get_info_2(recursive=True)["ControlPointsList"] == ( + ( + {"Coordinates": (0.0,), "id": 2, "type": "IfcCartesianPoint"}, + {"Coordinates": (1.0,), "id": 3, "type": "IfcCartesianPoint"}, + ), + ( + {"Coordinates": (2.0,), "id": 4, "type": "IfcCartesianPoint"}, + {"Coordinates": (3.0,), "id": 5, "type": "IfcCartesianPoint"}, + ), + ) diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index cff9922693..3a30620670 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -773,6 +773,25 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas Py_INCREF(Py_None); return static_cast(Py_None); } + } else if constexpr (std::is_same_v) { + return get_info_cpp(v); + } else if constexpr (std::is_same_v) { + auto r = PyTuple_New(v->size()); + for (unsigned i = 0; i < v->size(); ++i) { + PyTuple_SetItem(r, i, get_info_cpp((*v)[i])); + } + return r; + } else if constexpr (std::is_same_v) { + auto rs = PyTuple_New(v->size()); + for (auto it = v->begin(); it != v->end(); ++it) { + auto v_i = it; + auto r = PyTuple_New(v_i->size()); + for (unsigned i = 0; i < v_i->size(); ++i) { + PyTuple_SetItem(r, i, get_info_cpp((*v_i)[i])); + } + PyTuple_SetItem(rs, std::distance(v->begin(), it), r); + } + return rs; } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { Py_INCREF(Py_None); return static_cast(Py_None);