diff --git a/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md b/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md new file mode 100644 index 0000000000..e2e2ebeb35 --- /dev/null +++ b/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md @@ -0,0 +1 @@ +SVG [mustache](https://mustache.github.io/) templates that will be used for sheets and fill be filled with infromation from the sheet's IfcDocumentInformation attributes (e.g. Identification, Name, Revision, etc). diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 1f4360eb6a..57b1981e2c 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -1734,7 +1734,7 @@ class OpenLayout(bpy.types.Operator, tool.Ifc.Operator): class SelectAllSheets(bpy.types.Operator): bl_idname = "bim.select_all_sheets" - bl_label = "Select All Sheetss" + bl_label = "Select All Sheets" view: bpy.props.StringProperty() bl_description = "Select all sheets in the sheet list.\n\n" + "SHIFT+CLICK to deselect all sheets" select_all: bpy.props.BoolProperty(name="Open All", default=True, options={"SKIP_SAVE"}) diff --git a/src/bonsai/bonsai/bim/module/drawing/sheeter.py b/src/bonsai/bonsai/bim/module/drawing/sheeter.py index f73b71b64f..009ce5bcf5 100644 --- a/src/bonsai/bonsai/bim/module/drawing/sheeter.py +++ b/src/bonsai/bonsai/bim/module/drawing/sheeter.py @@ -61,8 +61,8 @@ class SheetBuilder: shutil.copy(ootb_titleblock_path, titleblock_path) view_root = ET.parse(titleblock_path).getroot() - view_width = self.convert_to_mm(view_root.attrib.get("width")) - view_height = self.convert_to_mm(view_root.attrib.get("height")) + view_width = self.convert_to_mm(view_root.attrib["width"]) + view_height = self.convert_to_mm(view_root.attrib["height"]) view = ET.SubElement(root, "g") view.attrib["data-type"] = "titleblock" titleblock = ET.SubElement(view, "image") @@ -87,6 +87,7 @@ class SheetBuilder: ) -> None: filename = drawing.Name layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") + assert layout_path layout_dir = os.path.dirname(layout_path) drawing_path = tool.Drawing.get_document_uri(tool.Drawing.get_drawing_reference(drawing)) @@ -110,8 +111,8 @@ class SheetBuilder: view.attrib["data-type"] = "drawing" view.attrib["data-id"] = str(reference.id()) view.attrib["data-drawing"] = drawing.GlobalId - view_width = self.convert_to_mm(view_root.attrib.get("width")) - view_height = self.convert_to_mm(view_root.attrib.get("height")) + view_width = self.convert_to_mm(view_root.attrib["width"]) + view_height = self.convert_to_mm(view_root.attrib["height"]) x, y = self.next_drawing_location(layout_root, view_width) @@ -134,16 +135,16 @@ class SheetBuilder: # how wide is the title block frame try: - titleblock_width = self.convert_to_mm(titleblocks[0][0].attrib.get("width")) + titleblock_width = self.convert_to_mm(titleblocks[0][0].attrib["width"]) except (IndexError, AttributeError): titleblock_width = 840.0 # where does the last drawing finish try: last = drawings[-1][0] - last_width = self.convert_to_mm(last.attrib.get("width")) - last_x = self.convert_to_mm(last.attrib.get("x")) - last_y = self.convert_to_mm(last.attrib.get("y")) + last_width = self.convert_to_mm(last.attrib["width"]) + last_x = self.convert_to_mm(last.attrib["x"]) + last_y = self.convert_to_mm(last.attrib["y"]) except (IndexError, AttributeError): return [DEFAULT_POSITION.x, DEFAULT_POSITION.y] @@ -155,8 +156,8 @@ class SheetBuilder: for drawing in drawings: for image in drawing: try: - image_y = self.convert_to_mm(image.attrib.get("y")) - image_height = self.convert_to_mm(image.attrib.get("height")) + image_y = self.convert_to_mm(image.attrib["y"]) + image_height = self.convert_to_mm(image.attrib["height"]) except AttributeError: return [DEFAULT_POSITION.x, DEFAULT_POSITION.y] if image_y + image_height + DRAWING_PADDING > last_y: @@ -167,6 +168,7 @@ class SheetBuilder: ET.register_namespace("", "http://www.w3.org/2000/svg") layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") + assert layout_path layout_tree = ET.parse(layout_path) layout_root = layout_tree.getroot() ifc_file = tool.Ifc.get() @@ -210,12 +212,13 @@ class SheetBuilder: ET.register_namespace("", "http://www.w3.org/2000/svg") layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") + assert layout_path if not os.path.exists(layout_path): return layout_tree = ET.parse(layout_path) layout_root = layout_tree.getroot() - for g in layout_root.findall("{http://www.w3.org/2000/svg}g"): + for g in layout_root.findall(f"{SVG}g"): if g.attrib.get("data-id") == str(reference.id()): layout_root.remove(g) break @@ -233,6 +236,7 @@ class SheetBuilder: tool.Drawing.create_svg_document(document) document_name = os.path.splitext(os.path.basename(view_path))[0] layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") + assert layout_path layout_dir = os.path.dirname(layout_path) ET.register_namespace("", "http://www.w3.org/2000/svg") @@ -278,13 +282,14 @@ class SheetBuilder: title.attrib["xlink:href"] = os.path.relpath(title_path, layout_dir) title.attrib["x"] = str(x) title.attrib["y"] = str(y) - title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width"))) - title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height"))) + title.attrib["width"] = str(self.convert_to_mm(title_root.attrib["width"])) + title.attrib["height"] = str(self.convert_to_mm(title_root.attrib["height"])) def build(self, sheet: ifcopenshell.entity_instance) -> dict: self.references = {"SHEET": None, "RASTER": []} layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") + assert layout_path self.layout_dir = os.path.dirname(layout_path) sheet_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_sheet_path(sheet[0], sheet.Name)) @@ -313,14 +318,14 @@ class SheetBuilder: return self.references def build_titleblock(self, root: ET.Element, sheet: ifcopenshell.entity_instance) -> None: - titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0] - image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0] + titleblock = root.findall(f'{SVG}g[@data-type="titleblock"]')[0] + image = titleblock.findall(f"{SVG}image")[0] g = self.parse_embedded_svg(image, sheet.get_info()) grid_north = ifcopenshell.util.geolocation.get_grid_north(tool.Ifc.get()) * -1 true_north = ifcopenshell.util.geolocation.get_true_north(tool.Ifc.get()) * -1 - for north in g.iterfind('.//{http://www.w3.org/2000/svg}g[@data-type="grid-north"]'): + for north in g.iterfind(f'.//{SVG}g[@data-type="grid-north"]'): north.attrib["transform"] = f"rotate({grid_north})" - for north in g.iterfind('.//{http://www.w3.org/2000/svg}g[@data-type="true-north"]'): + for north in g.iterfind(f'.//{SVG}g[@data-type="true-north"]'): north.attrib["transform"] = f"rotate({true_north})" titleblock.append(g) titleblock.remove(image) @@ -333,7 +338,9 @@ class SheetBuilder: # add .prefix class to all css selectors style = svg.find(f"{SVG}defs/{SVG}style") + assert style style_data = style.text + assert style_data is not None text = "" brackets_level = 0 for l in style_data: @@ -374,6 +381,8 @@ class SheetBuilder: if "filter" in attrib: # example use "#fill-background" filter attrib["filter"] = replace_urls(attrib["filter"]) + if "style" in attrib: + attrib["style"] = replace_urls(attrib["style"]) if svg_element.tag == f"{SVG}use": href_attrib = f"{XLINK}href" if href_attrib in attrib: @@ -384,16 +393,16 @@ class SheetBuilder: return svg def build_drawings(self, root: ET.Element, sheet: ifcopenshell.entity_instance): - for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'): + for view in root.findall(f'{SVG}g[@data-type="drawing"]'): drawing_id = int(view.attrib["data-id"]) try: reference = tool.Ifc.get().by_id(int(view.attrib["data-id"])) - drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"]) - except: + drawing = tool.Ifc.get().by_guid(view.attrib["data-drawing"]) + except RuntimeError: # Perhaps the SVG has outdated content or is edited externally which we cannot control. continue - images = view.findall("{http://www.w3.org/2000/svg}image") + images = view.findall(f"{SVG}image") foreground = None view_title = None @@ -410,6 +419,7 @@ class SheetBuilder: view.append(svg) if view_title is not None: + assert foreground foreground_path = self.get_href(foreground) data = reference.get_info() data.update({"Sheet" + k: v for k, v in sheet.get_info().items()}) @@ -434,8 +444,8 @@ class SheetBuilder: view.remove(image) def build_documents(self, root: ET.Element, sheet: ifcopenshell.entity_instance) -> None: - schedules = root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]') - references = root.findall('{http://www.w3.org/2000/svg}g[@data-type="reference"]') + schedules = root.findall(f'{SVG}g[@data-type="schedule"]') + references = root.findall(f'{SVG}g[@data-type="reference"]') documents = schedules + references for view in documents: try: @@ -445,7 +455,7 @@ class SheetBuilder: # Perhaps the SVG has outdated content or is edited externally which we cannot control. continue - images = view.findall("{http://www.w3.org/2000/svg}image") + images = view.findall(f"{SVG}image") table = None view_title = None @@ -471,13 +481,12 @@ class SheetBuilder: view.remove(image) def get_href(self, element: ET.Element) -> str: - return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href")).replace("\\", "/") + return urllib.parse.unquote(element.attrib[f"{XLINK}href"]).replace("\\", "/") def parse_embedded_svg(self, image: ET.Element, data: dict) -> ET.Element: group = ET.Element("g") - group.attrib["transform"] = "translate({},{})".format( - self.convert_to_mm(image.attrib.get("x")), self.convert_to_mm(image.attrib.get("y")) - ) + x, y = self.convert_to_mm(image.attrib["x"]), self.convert_to_mm(image.attrib["y"]) + group.attrib["transform"] = f"translate({x},{y})" # Convert viewBox into a clip path clip_id = str(uuid.uuid4()) @@ -487,8 +496,8 @@ class SheetBuilder: rect = ET.Element("rect") rect.attrib["x"] = "0" rect.attrib["y"] = "0" - rect.attrib["width"] = str(self.convert_to_mm(image.attrib.get("width"))) - rect.attrib["height"] = str(self.convert_to_mm(image.attrib.get("height"))) + rect.attrib["width"] = str(self.convert_to_mm(image.attrib["width"])) + rect.attrib["height"] = str(self.convert_to_mm(image.attrib["height"])) clip_path.append(rect) self.defs.append(clip_path) @@ -499,16 +508,16 @@ class SheetBuilder: embedded.attrib["viewBox"] = "" # TODO: This should not be in this function self.scale = embedded.attrib.get("data-scale") - images = embedded.findall("{http://www.w3.org/2000/svg}image") + images = embedded.findall(f"{SVG}image") for image in images: - old_href = Path(image.attrib.get("{http://www.w3.org/1999/xlink}href")) + old_href = Path(image.attrib[f"{XLINK}href"]) if not os.path.isabs(old_href): template_dir = Path(os.path.join(self.layout_dir, svg_path)).resolve().parent old_href = Path(os.path.join(template_dir, old_href)) old_href = old_href.absolute().resolve().as_posix() new_href = Path(os.path.join(self.sheets_dir, Path(old_href).name)).absolute().resolve().as_posix() shutil.copy(old_href, new_href) - image.attrib["{http://www.w3.org/1999/xlink}href"] = Path(old_href).name + image.attrib[f"{XLINK}href"] = Path(old_href).name for child in embedded: if "namedview" in child.tag: continue @@ -539,9 +548,9 @@ class SheetBuilder: sheet_tree = ET.parse(sheet_path) root = sheet_tree.getroot() - titleblock = sheet_tree.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0] - image = titleblock.findall("{http://www.w3.org/2000/svg}image[@{http://www.w3.org/1999/xlink}href]")[0] - image.attrib["{http://www.w3.org/1999/xlink}href"] = os.path.relpath(titleblock_path, sheet_dir) + titleblock = sheet_tree.findall(f'{SVG}g[@data-type="titleblock"]')[0] + image = titleblock.findall(f"{SVG}image[@{XLINK}href]")[0] + image.attrib[f"{XLINK}href"] = os.path.relpath(titleblock_path, sheet_dir) image.attrib["width"] = str(view_width) image.attrib["height"] = str(view_height) diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index 742b8734a1..8f39c4aedf 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -1132,21 +1132,24 @@ class DumbWallJoiner: relating_element = None connections = element1.ConnectedTo for conn in connections: - if conn.RelatingConnectionType == "ATEND": + if conn.is_a("IfcRelConnectsPathElements") and conn.RelatingConnectionType == "ATEND": relating_element = conn.RelatedElement + relating_connection = conn.RelatedConnectionType description = conn.Description + bonsai.core.geometry.remove_connection(tool.Geometry, connection=conn) connections = element1.ConnectedFrom for conn in connections: - if conn.RelatedConnectionType == "ATEND": + if conn.is_a("IfcRelConnectsPathElements") and conn.RelatedConnectionType == "ATEND": relating_element = conn.RelatingElement + relating_connection = conn.RelatingConnectionType description = conn.Description - + bonsai.core.geometry.remove_connection(tool.Geometry, connection=conn) if relating_element: ifcopenshell.api.geometry.connect_path( tool.Ifc.get(), relating_element=relating_element, related_element=element2, - relating_connection="ATSTART", + relating_connection=relating_connection, related_connection="ATEND", description=description, ) diff --git a/src/bonsai/bonsai/bim/module/profile/prop.py b/src/bonsai/bonsai/bim/module/profile/prop.py index e2da08091f..f69f6a6cd9 100644 --- a/src/bonsai/bonsai/bim/module/profile/prop.py +++ b/src/bonsai/bonsai/bim/module/profile/prop.py @@ -82,7 +82,7 @@ class BIMProfileProperties(PropertyGroup): name="Filter Material Profiles", default=False, description="Check to only show IfcProfileDefs attached to IfcMaterialProfiles", - update=lambda self, context: bpy.ops.bim.load_profiles(), + update=lambda self, context: (None, bpy.ops.bim.load_profiles())[0], ) object_to_profile: PointerProperty( name="Object to profile", diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index f58d117258..e1a120dafe 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -63,24 +63,34 @@ from bonsai.bim.module.project.decorator import ProjectDecorator, ClippingPlaneD from bonsai.bim.module.project.prop import BreadcrumbType from bonsai.bim.module.model.decorator import PolylineDecorator, FaceAreaDecorator from bonsai.bim.module.model.polyline import PolylineOperator -from typing import Union, TYPE_CHECKING, get_args +from typing import Union, TYPE_CHECKING, get_args, Literal if TYPE_CHECKING: + import bpy.stub_internal.rna_enums as rna_enums from bonsai.bim.module.project.prop import Link +PresetType = Literal["metric_m", "metric_mm", "imperial_ft", "demo", "wizard"] + + class NewProject(bpy.types.Operator): bl_idname = "bim.new_project" bl_label = "New Project" bl_options = {"REGISTER", "UNDO"} bl_description = "Start a new IFC project in a fresh session" - preset: bpy.props.StringProperty() + preset: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration] + items=[(i, i, "") for i in get_args(PresetType)] + ) - def execute(self, context): + if TYPE_CHECKING: + preset: PresetType + + def execute(self, context) -> set["rna_enums.OperatorReturnItems"]: bpy.ops.wm.read_homefile() pprops = tool.Project.get_project_props() bim_props = tool.Blender.get_bim_props() + assert bpy.context.scene if self.preset == "metric_m": pprops.export_schema = "IFC4" bpy.context.scene.unit_settings.system = "METRIC" diff --git a/src/bonsai/bonsai/bim/module/unit/operator.py b/src/bonsai/bonsai/bim/module/unit/operator.py index 19620a89dc..f7c129aba1 100644 --- a/src/bonsai/bonsai/bim/module/unit/operator.py +++ b/src/bonsai/bonsai/bim/module/unit/operator.py @@ -21,15 +21,21 @@ import ifcopenshell.api import ifcopenshell.util.unit import bonsai.tool as tool import bonsai.core.unit as core +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from bpy.stub_internal import rna_enums class AssignSceneUnits(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_scene_units" bl_label = "Assign Scene Units" + bl_description = "Add new units based on the current Blender scene units and assign them as project default." bl_options = {"REGISTER", "UNDO"} def _execute(self, context): core.assign_scene_units(tool.Ifc, tool.Unit) + core.load_units(tool.Unit) class AssignUnit(bpy.types.Operator, tool.Ifc.Operator): @@ -54,24 +60,26 @@ class UnassignUnit(bpy.types.Operator, tool.Ifc.Operator): core.unassign_unit(tool.Ifc, tool.Unit, unit=tool.Ifc.get().by_id(self.unit)) -class LoadUnits(bpy.types.Operator, tool.Ifc.Operator): +class LoadUnits(bpy.types.Operator): bl_idname = "bim.load_units" bl_label = "Load Units" bl_options = {"REGISTER", "UNDO"} bl_description = "Open the loaded units" - def _execute(self, context): + def execute(self, context) -> set["rna_enums.OperatorReturnItems"]: core.load_units(tool.Unit) + return {"FINISHED"} -class DisableUnitEditingUI(bpy.types.Operator, tool.Ifc.Operator): +class DisableUnitEditingUI(bpy.types.Operator): bl_idname = "bim.disable_unit_editing_ui" bl_label = "Disable Unit Editing UI" bl_options = {"REGISTER", "UNDO"} bl_description = "Close the editing units mode" - def _execute(self, context): + def execute(self, context) -> set["rna_enums.OperatorReturnItems"]: core.disable_unit_editing_ui(tool.Unit) + return {"FINISHED"} class RemoveUnit(bpy.types.Operator, tool.Ifc.Operator): @@ -124,23 +132,25 @@ class AddContextDependentUnit(bpy.types.Operator, tool.Ifc.Operator): core.add_context_dependent_unit(tool.Ifc, tool.Unit, unit_type=self.unit_type, name=self.name) -class EnableEditingUnit(bpy.types.Operator, tool.Ifc.Operator): +class EnableEditingUnit(bpy.types.Operator): bl_idname = "bim.enable_editing_unit" bl_label = "Enable Editing Unit" bl_options = {"REGISTER", "UNDO"} unit: bpy.props.IntProperty() - def _execute(self, context): + def execute(self, context) -> set["rna_enums.OperatorReturnItems"]: core.enable_editing_unit(tool.Unit, unit=tool.Ifc.get().by_id(self.unit)) + return {"FINISHED"} -class DisableEditingUnit(bpy.types.Operator, tool.Ifc.Operator): +class DisableEditingUnit(bpy.types.Operator): bl_idname = "bim.disable_editing_unit" bl_label = "Disable Editing Unit" bl_options = {"REGISTER", "UNDO"} - def _execute(self, context): + def execute(self, context) -> set["rna_enums.OperatorReturnItems"]: core.disable_editing_unit(tool.Unit) + return {"FINISHED"} class EditUnit(bpy.types.Operator, tool.Ifc.Operator): diff --git a/src/bonsai/bonsai/bim/module/unit/ui.py b/src/bonsai/bonsai/bim/module/unit/ui.py index d132a142d9..9407ef01ac 100644 --- a/src/bonsai/bonsai/bim/module/unit/ui.py +++ b/src/bonsai/bonsai/bim/module/unit/ui.py @@ -48,10 +48,12 @@ class BIM_PT_units(Panel): UnitsData.load() self.props = tool.Unit.get_unit_props() + assert self.layout row = self.layout.row(align=True) row.label(text="{} Units Found".format(UnitsData.data["total_units"]), icon="SNAP_GRID") if self.props.is_editing: + row.operator("bim.assign_scene_units", text="", icon="TOOL_SETTINGS") row.operator("bim.disable_unit_editing_ui", text="", icon="CANCEL") else: row.operator("bim.load_units", text="", icon="GREASEPENCIL") diff --git a/src/bonsai/bonsai/core/attribute.py b/src/bonsai/bonsai/core/attribute.py index 9d7cafdf1a..fd41beda7a 100644 --- a/src/bonsai/bonsai/core/attribute.py +++ b/src/bonsai/bonsai/core/attribute.py @@ -26,7 +26,12 @@ if TYPE_CHECKING: def copy_attribute_to_selection( - ifc: tool.Ifc, blender: tool.Blender, root: tool.Root, spatial: tool.Spatial, name: str, value: Union[str, None] + ifc: type[tool.Ifc], + blender: type[tool.Blender], + root: type[tool.Root], + spatial: type[tool.Spatial], + name: str, + value: Union[str, None], ) -> int: total_changed = 0 has_edited_spatial_name = False diff --git a/src/bonsai/bonsai/core/brick.py b/src/bonsai/bonsai/core/brick.py index 27b82d43f8..b285377d86 100644 --- a/src/bonsai/bonsai/core/brick.py +++ b/src/bonsai/bonsai/core/brick.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: import bonsai.tool as tool -def load_brick_project(brick: tool.Brick, filepath: str, brick_root: str) -> None: +def load_brick_project(brick: type[tool.Brick], filepath: str, brick_root: str) -> None: brick.load_brick_file(filepath) brick.import_brick_classes(brick_root) brick.import_brick_classes(brick_root, split_screen=True) @@ -33,7 +33,7 @@ def load_brick_project(brick: tool.Brick, filepath: str, brick_root: str) -> Non brick.set_active_brick_class(brick_root, split_screen=True) -def new_brick_file(brick: tool.Brick, brick_root: str) -> None: +def new_brick_file(brick: type[tool.Brick], brick_root: str) -> None: brick.new_brick_file() brick.import_brick_classes(brick_root) brick.import_brick_classes(brick_root, split_screen=True) @@ -41,7 +41,7 @@ def new_brick_file(brick: tool.Brick, brick_root: str) -> None: brick.set_active_brick_class(brick_root, split_screen=True) -def view_brick_class(brick: tool.Brick, brick_class: str, split_screen: bool = False) -> None: +def view_brick_class(brick: type[tool.Brick], brick_class: str, split_screen: bool = False) -> None: brick.add_brick_breadcrumb(split_screen=split_screen) brick.clear_brick_browser(split_screen=split_screen) brick.import_brick_classes(brick_class, split_screen=split_screen) @@ -49,13 +49,13 @@ def view_brick_class(brick: tool.Brick, brick_class: str, split_screen: bool = F brick.set_active_brick_class(brick_class, split_screen=split_screen) -def view_brick_item(brick: tool.Brick, item: str, split_screen: bool = False) -> None: +def view_brick_item(brick: type[tool.Brick], item: str, split_screen: bool = False) -> None: brick_class = brick.get_item_class(item) brick.run_view_brick_class(brick_class=brick_class, split_screen=split_screen) brick.select_browser_item(item, split_screen=split_screen) -def rewind_brick_class(brick: tool.Brick, split_screen: bool = False) -> None: +def rewind_brick_class(brick: type[tool.Brick], split_screen: bool = False) -> None: previous_class = brick.pop_brick_breadcrumb(split_screen=split_screen) brick.clear_brick_browser(split_screen=split_screen) brick.import_brick_classes(previous_class, split_screen=split_screen) @@ -63,7 +63,7 @@ def rewind_brick_class(brick: tool.Brick, split_screen: bool = False) -> None: brick.set_active_brick_class(previous_class, split_screen=split_screen) -def close_brick_project(brick: tool.Brick) -> None: +def close_brick_project(brick: type[tool.Brick]) -> None: brick.clear_project() brick.clear_brick_browser() brick.clear_brick_browser(split_screen=True) @@ -71,15 +71,15 @@ def close_brick_project(brick: tool.Brick) -> None: brick.clear_breadcrumbs(split_screen=True) -def convert_brick_project(ifc: tool.Ifc, brick: tool.Brick) -> None: +def convert_brick_project(ifc: type[tool.Ifc], brick: type[tool.Brick]) -> None: library = ifc.run("library.add_library", name=brick.get_brick_path_name()) if ifc.get_schema() != "IFC2X3": ifc.run("library.edit_library", library=library, attributes={"Location": brick.get_brick_path()}) def assign_brick_reference( - ifc: tool.Ifc, - brick: tool.Brick, + ifc: type[tool.Ifc], + brick: type[tool.Brick], element: ifcopenshell.entity_instance, library: ifcopenshell.entity_instance, brick_uri: str, @@ -96,8 +96,8 @@ def assign_brick_reference( def add_brick( - ifc: tool.Ifc, - brick: tool.Brick, + ifc: type[tool.Ifc], + brick: type[tool.Brick], element: Union[ifcopenshell.entity_instance, None], namespace: str, brick_class: str, @@ -113,12 +113,14 @@ def add_brick( brick.run_refresh_brick_viewer() -def add_brick_relation(brick: tool.Brick, brick_uri: str, predicate: str, object: str) -> None: +def add_brick_relation(brick: type[tool.Brick], brick_uri: str, predicate: str, object: str) -> None: brick.add_relation(brick_uri, predicate, object) brick.run_refresh_brick_viewer() -def convert_ifc_to_brick(brick: tool.Brick, namespace: str, library: Union[ifcopenshell.entity_instance, None]) -> None: +def convert_ifc_to_brick( + brick: type[tool.Brick], namespace: str, library: Union[ifcopenshell.entity_instance, None] +) -> None: # convert spaces to brick spaces = brick.get_convertable_brick_spaces() space_uris = {} @@ -163,14 +165,16 @@ def convert_ifc_to_brick(brick: tool.Brick, namespace: str, library: Union[ifcop brick.run_refresh_brick_viewer() -def refresh_brick_viewer(brick: tool.Brick) -> None: +def refresh_brick_viewer(brick: type[tool.Brick]) -> None: brick.run_view_brick_class(brick_class=brick.get_active_brick_class()) brick.pop_brick_breadcrumb() brick.run_view_brick_class(brick_class=brick.get_active_brick_class(split_screen=True), split_screen=True) brick.pop_brick_breadcrumb(split_screen=True) -def remove_brick(ifc: tool.Ifc, brick: tool.Brick, library: ifcopenshell.entity_instance, brick_uri: str) -> None: +def remove_brick( + ifc: type[tool.Ifc], brick: type[tool.Brick], library: ifcopenshell.entity_instance, brick_uri: str +) -> None: if library: reference = brick.get_library_brick_reference(library, brick_uri) if reference: @@ -179,18 +183,18 @@ def remove_brick(ifc: tool.Ifc, brick: tool.Brick, library: ifcopenshell.entity_ brick.run_refresh_brick_viewer() -def serialize_brick(brick: tool.Brick) -> None: +def serialize_brick(brick: type[tool.Brick]) -> None: brick.serialize_brick() -def add_brick_namespace(brick: tool.Brick, alias: str, uri: str) -> None: +def add_brick_namespace(brick: type[tool.Brick], alias: str, uri: str) -> None: brick.add_namespace(alias, uri) -def set_brick_list_root(brick: tool.Brick, brick_root: str, split_screen: bool = False) -> None: +def set_brick_list_root(brick: type[tool.Brick], brick_root: str, split_screen: bool = False) -> None: brick.run_view_brick_class(brick_class=brick_root, split_screen=split_screen) brick.clear_breadcrumbs(split_screen=split_screen) -def remove_brick_relation(brick: tool.Brick, brick_uri: str, predicate: str, object: str) -> None: +def remove_brick_relation(brick: type[tool.Brick], brick_uri: str, predicate: str, object: str) -> None: brick.remove_relation(brick_uri, predicate, object) diff --git a/src/bonsai/bonsai/core/context.py b/src/bonsai/bonsai/core/context.py index c251a263c4..626720fb33 100644 --- a/src/bonsai/bonsai/core/context.py +++ b/src/bonsai/bonsai/core/context.py @@ -26,7 +26,7 @@ if TYPE_CHECKING: def add_context( - ifc: tool.Ifc, + ifc: type[tool.Ifc], context_type: Optional[str] = None, context_identifier: Optional[str] = None, target_view: Optional[str] = None, @@ -41,19 +41,19 @@ def add_context( ) -def remove_context(ifc: tool.Ifc, context: ifcopenshell.entity_instance) -> None: +def remove_context(ifc: type[tool.Ifc], context: ifcopenshell.entity_instance) -> None: ifc.run("context.remove_context", context=context) -def enable_editing_context(context_tool: tool.Context, context: ifcopenshell.entity_instance) -> None: +def enable_editing_context(context_tool: type[tool.Context], context: ifcopenshell.entity_instance) -> None: context_tool.set_context(context) context_tool.import_attributes() -def disable_editing_context(context: tool.Context) -> None: +def disable_editing_context(context: type[tool.Context]) -> None: context.clear_context() -def edit_context(ifc: tool.Ifc, context: tool.Context) -> None: +def edit_context(ifc: type[tool.Ifc], context: type[tool.Context]) -> None: ifc.run("context.edit_context", context=context.get_context(), attributes=context.export_attributes()) disable_editing_context(context) diff --git a/src/bonsai/bonsai/core/cost.py b/src/bonsai/bonsai/core/cost.py index 5da4a826ac..f6c26a5ac7 100644 --- a/src/bonsai/bonsai/core/cost.py +++ b/src/bonsai/bonsai/core/cost.py @@ -458,7 +458,5 @@ def add_currency(ifc: type[tool.Ifc], cost: type[tool.Cost]) -> ifcopenshell.ent return unit -def generate_cost_schedule_browser( - cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance -) -> bpy.types.Panel: +def generate_cost_schedule_browser(cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance) -> None: return cost.generate_cost_schedule_browser(cost_schedule) diff --git a/src/bonsai/bonsai/core/covering.py b/src/bonsai/bonsai/core/covering.py index d26780c0c5..a6b9912505 100644 --- a/src/bonsai/bonsai/core/covering.py +++ b/src/bonsai/bonsai/core/covering.py @@ -25,7 +25,9 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_instance_flooring_covering_from_cursor(ifc: tool.Ifc, root: tool.Root, spatial: tool.Spatial) -> None: +def add_instance_flooring_covering_from_cursor( + ifc: type[tool.Ifc], root: type[tool.Root], spatial: type[tool.Spatial] +) -> None: if not root.get_default_container(): raise NoDefaultContainer() @@ -68,7 +70,7 @@ def add_instance_flooring_covering_from_cursor(ifc: tool.Ifc, root: tool.Root, s def add_instance_ceiling_covering_from_cursor( - ifc: tool.Ifc, root: tool.Root, covering: tool.Covering, spatial: tool.Spatial + ifc: type[tool.Ifc], root: type[tool.Root], covering: type[tool.Covering], spatial: type[tool.Spatial] ) -> None: if not root.get_default_container(): raise NoDefaultContainer() @@ -111,7 +113,7 @@ def add_instance_ceiling_covering_from_cursor( spatial.regen_obj_representation(obj, body) -def regen_selected_covering_object(root: tool.Root, spatial: tool.Spatial) -> None: +def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spatial]) -> None: if not root.get_default_container(): raise NoDefaultContainer() @@ -142,7 +144,7 @@ def regen_selected_covering_object(root: tool.Root, spatial: tool.Spatial) -> No # TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS -def add_instance_flooring_coverings_from_walls(root: tool.Root, spatial: tool.Spatial) -> None: +def add_instance_flooring_coverings_from_walls(root: type[tool.Root], spatial: type[tool.Spatial]) -> None: if not root.get_default_container(): raise NoDefaultContainer() @@ -168,7 +170,9 @@ def add_instance_flooring_coverings_from_walls(root: tool.Root, spatial: tool.Sp spatial.regen_obj_representation(obj, body) -def add_instance_ceiling_coverings_from_walls(root: tool.Root, spatial: tool.Spatial, covering: tool.Covering) -> None: +def add_instance_ceiling_coverings_from_walls( + root: type[tool.Root], spatial: type[tool.Spatial], covering: type[tool.Covering] +) -> None: if not root.get_default_container(): raise NoDefaultContainer() diff --git a/src/bonsai/bonsai/core/georeference.py b/src/bonsai/bonsai/core/georeference.py index 4abbe4658d..419f4218ba 100644 --- a/src/bonsai/bonsai/core/georeference.py +++ b/src/bonsai/bonsai/core/georeference.py @@ -25,25 +25,25 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_georeferencing(georeference: tool.Georeference) -> None: +def add_georeferencing(georeference: type[tool.Georeference]) -> None: georeference.add_georeferencing() -def enable_editing_georeferencing(georeference: tool.Georeference) -> None: +def enable_editing_georeferencing(georeference: type[tool.Georeference]) -> None: georeference.import_projected_crs() georeference.import_coordinate_operation() georeference.enable_editing() -def remove_georeferencing(ifc: tool.Ifc) -> None: +def remove_georeferencing(ifc: type[tool.Ifc]) -> None: ifc.run("georeference.remove_georeferencing") -def disable_editing_georeferencing(georeference: tool.Georeference) -> None: +def disable_editing_georeferencing(georeference: type[tool.Georeference]) -> None: georeference.disable_editing() -def edit_georeferencing(ifc: tool.Ifc, georeference: tool.Georeference) -> None: +def edit_georeferencing(ifc: type[tool.Ifc], georeference: type[tool.Georeference]) -> None: ifc.run( "georeference.edit_georeferencing", projected_crs=georeference.export_projected_crs(), @@ -53,7 +53,7 @@ def edit_georeferencing(ifc: tool.Ifc, georeference: tool.Georeference) -> None: georeference.set_model_origin() -def get_cursor_location(georeference: tool.Georeference) -> None: +def get_cursor_location(georeference: type[tool.Georeference]) -> None: location = georeference.get_cursor_location() if georeference.has_blender_offset(): georeference.set_coordinates("blender", location) @@ -61,39 +61,39 @@ def get_cursor_location(georeference: tool.Georeference) -> None: georeference.set_coordinates("local", location) -def import_plot(georeference: tool.Georeference, filepath: str) -> None: +def import_plot(georeference: type[tool.Georeference], filepath: str) -> None: georeference.import_plot(filepath) -def enable_editing_wcs(georeference: tool.Georeference) -> None: +def enable_editing_wcs(georeference: type[tool.Georeference]) -> None: georeference.import_wcs() georeference.enable_editing_wcs() -def disable_editing_wcs(georeference: tool.Georeference) -> None: +def disable_editing_wcs(georeference: type[tool.Georeference]) -> None: georeference.disable_editing_wcs() -def edit_wcs(georeference: tool.Georeference) -> None: +def edit_wcs(georeference: type[tool.Georeference]) -> None: wcs = georeference.export_wcs() georeference.set_wcs(wcs) georeference.disable_editing_wcs() georeference.set_model_origin() -def enable_editing_true_north(georeference: tool.Georeference) -> None: +def enable_editing_true_north(georeference: type[tool.Georeference]) -> None: georeference.import_true_north() georeference.enable_editing_true_north() -def disable_editing_true_north(georeference: tool.Georeference) -> None: +def disable_editing_true_north(georeference: type[tool.Georeference]) -> None: georeference.disable_editing_true_north() -def edit_true_north(ifc: tool.Ifc, georeference: tool.Georeference) -> None: +def edit_true_north(ifc: type[tool.Ifc], georeference: type[tool.Georeference]) -> None: ifc.run("georeference.edit_true_north", true_north=georeference.get_true_north_attributes()) georeference.disable_editing_true_north() -def remove_true_north(ifc: tool.Ifc) -> None: +def remove_true_north(ifc: type[tool.Ifc]) -> None: ifc.run("georeference.edit_true_north", true_north=None) diff --git a/src/bonsai/bonsai/core/library.py b/src/bonsai/bonsai/core/library.py index 4b08f771bb..53f5f8ce8c 100644 --- a/src/bonsai/bonsai/core/library.py +++ b/src/bonsai/bonsai/core/library.py @@ -25,35 +25,35 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_library(ifc: tool.Ifc) -> ifcopenshell.entity_instance: +def add_library(ifc: type[tool.Ifc]) -> ifcopenshell.entity_instance: return ifc.run("library.add_library", name="Unnamed") -def remove_library(ifc: tool.Ifc, library: ifcopenshell.entity_instance) -> None: +def remove_library(ifc: type[tool.Ifc], library: ifcopenshell.entity_instance) -> None: ifc.run("library.remove_library", library=library) -def enable_editing_library_references(library_tool: tool.Library, library: ifcopenshell.entity_instance) -> None: +def enable_editing_library_references(library_tool: type[tool.Library], library: ifcopenshell.entity_instance) -> None: library_tool.set_editing_mode("REFERENCES") library_tool.set_active_library(library) library_tool.import_references(library) -def disable_editing_library_references(library: tool.Library) -> None: +def disable_editing_library_references(library: type[tool.Library]) -> None: library.clear_editing_mode() library.set_active_library(None) -def enable_editing_library(library: tool.Library) -> None: +def enable_editing_library(library: type[tool.Library]) -> None: library.set_editing_mode("LIBRARY") library.import_library_attributes(library.get_active_library()) -def disable_editing_library(library: tool.Library) -> None: +def disable_editing_library(library: type[tool.Library]) -> None: library.set_editing_mode("REFERENCES") -def edit_library(ifc: tool.Ifc, library: tool.Library) -> None: +def edit_library(ifc: type[tool.Ifc], library: type[tool.Library]) -> None: library.set_editing_mode("REFERENCES") active_library = library.get_active_library() attributes = library.export_library_attributes() @@ -61,29 +61,31 @@ def edit_library(ifc: tool.Ifc, library: tool.Library) -> None: library.import_references(active_library) -def add_library_reference(ifc: tool.Ifc, library: tool.Library) -> ifcopenshell.entity_instance: +def add_library_reference(ifc: type[tool.Ifc], library: type[tool.Library]) -> ifcopenshell.entity_instance: active_library = library.get_active_library() reference = ifc.run("library.add_reference", library=active_library) library.import_references(active_library) return reference -def remove_library_reference(ifc: tool.Ifc, library: tool.Library, reference: ifcopenshell.entity_instance) -> None: +def remove_library_reference( + ifc: type[tool.Ifc], library: type[tool.Library], reference: ifcopenshell.entity_instance +) -> None: ifc.run("library.remove_reference", reference=reference) library.import_references(library.get_active_library()) -def enable_editing_library_reference(library: tool.Library, reference: ifcopenshell.entity_instance) -> None: +def enable_editing_library_reference(library: type[tool.Library], reference: ifcopenshell.entity_instance) -> None: library.set_editing_mode("REFERENCE") library.set_active_reference(reference) library.import_reference_attributes(reference) -def disable_editing_library_reference(library: tool.Library) -> None: +def disable_editing_library_reference(library: type[tool.Library]) -> None: library.set_editing_mode("REFERENCES") -def edit_library_reference(ifc: tool.Ifc, library: tool.Library) -> None: +def edit_library_reference(ifc: type[tool.Ifc], library: type[tool.Library]) -> None: library.set_editing_mode("REFERENCES") active_reference = library.get_active_reference() attributes = library.export_reference_attributes() @@ -91,9 +93,13 @@ def edit_library_reference(ifc: tool.Ifc, library: tool.Library) -> None: library.import_references(library.get_active_library()) -def assign_library_reference(ifc: tool.Ifc, obj: bpy.types.Object, reference: ifcopenshell.entity_instance) -> None: +def assign_library_reference( + ifc: type[tool.Ifc], obj: bpy.types.Object, reference: ifcopenshell.entity_instance +) -> None: ifc.run("library.assign_reference", products=[ifc.get_entity(obj)], reference=reference) -def unassign_library_reference(ifc: tool.Ifc, obj: bpy.types.Object, reference: ifcopenshell.entity_instance) -> None: +def unassign_library_reference( + ifc: type[tool.Ifc], obj: bpy.types.Object, reference: ifcopenshell.entity_instance +) -> None: ifc.run("library.unassign_reference", products=[ifc.get_entity(obj)], reference=reference) diff --git a/src/bonsai/bonsai/core/nest.py b/src/bonsai/bonsai/core/nest.py index 18fe5c5831..645efa0eb6 100644 --- a/src/bonsai/bonsai/core/nest.py +++ b/src/bonsai/bonsai/core/nest.py @@ -25,18 +25,18 @@ if TYPE_CHECKING: import bonsai.tool as tool -def enable_editing_nest(nest: tool.Nest, obj: bpy.types.Object) -> None: +def enable_editing_nest(nest: type[tool.Nest], obj: bpy.types.Object) -> None: nest.enable_editing(obj) -def disable_editing_nest(nest: tool.Nest, obj: bpy.types.Object) -> None: +def disable_editing_nest(nest: type[tool.Nest], obj: bpy.types.Object) -> None: nest.disable_editing(obj) def assign_object( - ifc: tool.Ifc, - nest: tool.Nest, - collector: tool.Collector, + ifc: type[tool.Ifc], + nest: type[tool.Nest], + collector: type[tool.Collector], relating_obj: bpy.types.Object, related_obj: bpy.types.Object, ) -> Union[ifcopenshell.entity_instance, None]: @@ -54,9 +54,9 @@ def assign_object( def unassign_object( - ifc: tool.Ifc, - nest: tool.Nest, - collector: tool.Collector, + ifc: type[tool.Ifc], + nest: type[tool.Nest], + collector: type[tool.Collector], relating_obj: bpy.types.Object, related_obj: bpy.types.Object, ) -> Union[ifcopenshell.entity_instance, None]: @@ -77,10 +77,10 @@ def unassign_object( def add_part_to_object( - ifc: tool.Ifc, - nest: tool.Nest, - collector: tool.Collector, - blender: tool.Blender, + ifc: type[tool.Ifc], + nest: type[tool.Nest], + collector: type[tool.Collector], + blender: type[tool.Blender], obj: bpy.types.Object, part_class: str, part_name: str, @@ -90,7 +90,7 @@ def add_part_to_object( def enable_nest_mode( - nest: tool.Nest, + nest: type[tool.Nest], obj: bpy.types.Object, ) -> None: if nest.get_nest_mode(): @@ -99,5 +99,5 @@ def enable_nest_mode( nest.enable_nest_mode(obj) -def disable_nest_mode(nest: tool.Nest) -> None: +def disable_nest_mode(nest: type[tool.Nest]) -> None: nest.disable_nest_mode() diff --git a/src/bonsai/bonsai/core/patch.py b/src/bonsai/bonsai/core/patch.py index 0a7a83d632..a715f11f74 100644 --- a/src/bonsai/bonsai/core/patch.py +++ b/src/bonsai/bonsai/core/patch.py @@ -26,5 +26,5 @@ if TYPE_CHECKING: import bonsai.tool as tool -def run_migrate_patch(patch: tool.Patch, infile: str, outfile: str, schema: str) -> None: +def run_migrate_patch(patch: type[tool.Patch], infile: str, outfile: str, schema: str) -> None: patch.run_migrate_patch(infile, outfile, schema) diff --git a/src/bonsai/bonsai/core/project.py b/src/bonsai/bonsai/core/project.py index c222696ad7..f699c8bfe9 100644 --- a/src/bonsai/bonsai/core/project.py +++ b/src/bonsai/bonsai/core/project.py @@ -26,10 +26,10 @@ if TYPE_CHECKING: def create_project( - ifc: tool.Ifc, - georeference: tool.Georeference, - project: tool.Project, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + georeference: type[tool.Georeference], + project: type[tool.Project], + spatial: type[tool.Spatial], schema: str, template: Optional[str] = None, ) -> None: diff --git a/src/bonsai/bonsai/core/pset.py b/src/bonsai/bonsai/core/pset.py index 477f023cc2..6bcf098e52 100644 --- a/src/bonsai/bonsai/core/pset.py +++ b/src/bonsai/bonsai/core/pset.py @@ -27,8 +27,8 @@ if TYPE_CHECKING: def copy_property_to_selection( - ifc: tool.Ifc, - pset: tool.Pset, + ifc: type[tool.Ifc], + pset: type[tool.Pset], obj: bpy.types.Object, pset_name: str, prop_name: str, @@ -48,7 +48,11 @@ def copy_property_to_selection( def add_pset( - ifc: tool.Ifc, pset: tool.Pset, blender: tool.Blender, obj_name: str, obj_type: tool.Ifc.OBJECT_TYPE + ifc: type[tool.Ifc], + pset: type[tool.Pset], + blender: type[tool.Blender], + obj_name: str, + obj_type: type[tool.Ifc].OBJECT_TYPE, ) -> None: pset_name = pset.get_pset_name(obj_name, obj_type, pset_type="PSET") if obj_type == "Object": @@ -64,12 +68,12 @@ def add_pset( def enable_pset_editing( - pset_tool: tool.Pset, + pset_tool: type[tool.Pset], pset: Union[ifcopenshell.entity_instance, None], pset_name: str, - pset_type: tool.Pset.PSET_TYPE, + pset_type: type[tool.Pset].PSET_TYPE, obj_name: str, - obj_type: tool.Ifc.OBJECT_TYPE, + obj_type: type[tool.Ifc].OBJECT_TYPE, ) -> None: props = pset_tool.get_pset_props(obj_name, obj_type) pset_tool.clear_blender_pset_properties(props) @@ -90,7 +94,7 @@ def enable_pset_editing( def add_proposed_prop( - pset: tool.Pset, obj_name: str, obj_type: tool.Ifc.OBJECT_TYPE, name: str, value: Any + pset: type[tool.Pset], obj_name: str, obj_type: type[tool.Ifc].OBJECT_TYPE, name: str, value: Any ) -> Union[None, str]: props = pset.get_pset_props(obj_name, obj_type) res = pset.add_proposed_property(name, pset.cast_string_to_primitive(value), props) @@ -99,7 +103,7 @@ def add_proposed_prop( def unshare_pset( - ifc: tool.Ifc, pset_tool: tool.Pset, obj_type: tool.Ifc.OBJECT_TYPE, obj_name: str, pset_id: int + ifc: type[tool.Ifc], pset_tool: type[tool.Pset], obj_type: type[tool.Ifc].OBJECT_TYPE, obj_name: str, pset_id: int ) -> None: elements: list[ifcopenshell.entity_instance] pset = ifc.get_entity_by_id(pset_id) diff --git a/src/bonsai/bonsai/core/qto.py b/src/bonsai/bonsai/core/qto.py index 85999684d9..d56130962b 100644 --- a/src/bonsai/bonsai/core/qto.py +++ b/src/bonsai/bonsai/core/qto.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: import bonsai.tool as tool -def calculate_circle_radius(qto: tool.Qto, obj: bpy.types.Object) -> float: +def calculate_circle_radius(qto: type[tool.Qto], obj: bpy.types.Object) -> float: result = qto.get_radius_of_selected_vertices(obj) qto.set_qto_result(result) return result diff --git a/src/bonsai/bonsai/core/search.py b/src/bonsai/bonsai/core/search.py index 0fe60fdb65..5e9d279df9 100644 --- a/src/bonsai/bonsai/core/search.py +++ b/src/bonsai/bonsai/core/search.py @@ -1,2 +1,28 @@ -def show_scene_elements(spatial): +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . + +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import bpy + import bonsai.tool as tool + + +def show_scene_elements(spatial: type[tool.Spatial]) -> None: spatial.show_scene_objects() diff --git a/src/bonsai/bonsai/core/sequence.py b/src/bonsai/bonsai/core/sequence.py index 558bf9556b..9581f2595e 100644 --- a/src/bonsai/bonsai/core/sequence.py +++ b/src/bonsai/bonsai/core/sequence.py @@ -25,106 +25,110 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_work_plan(ifc: tool.Ifc) -> ifcopenshell.entity_instance: +def add_work_plan(ifc: type[tool.Ifc]) -> ifcopenshell.entity_instance: return ifc.run("sequence.add_work_plan") -def remove_work_plan(ifc: tool.Ifc, work_plan: ifcopenshell.entity_instance) -> None: +def remove_work_plan(ifc: type[tool.Ifc], work_plan: ifcopenshell.entity_instance) -> None: ifc.run("sequence.remove_work_plan", work_plan=work_plan) -def enable_editing_work_plan(sequence: tool.Sequence, work_plan: ifcopenshell.entity_instance) -> None: +def enable_editing_work_plan(sequence: type[tool.Sequence], work_plan: ifcopenshell.entity_instance) -> None: sequence.load_work_plan_attributes(work_plan) sequence.enable_editing_work_plan(work_plan) -def disable_editing_work_plan(sequence: tool.Sequence) -> None: +def disable_editing_work_plan(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_work_plan() -def edit_work_plan(ifc: tool.Ifc, sequence: tool.Sequence, work_plan: ifcopenshell.entity_instance) -> None: +def edit_work_plan(ifc: type[tool.Ifc], sequence: type[tool.Sequence], work_plan: ifcopenshell.entity_instance) -> None: attributes = sequence.get_work_plan_attributes() ifc.run("sequence.edit_work_plan", work_plan=work_plan, attributes=attributes) sequence.disable_editing_work_plan() -def edit_work_schedule(ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def edit_work_schedule( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance +) -> None: attributes = sequence.get_work_schedule_attributes() ifc.run("sequence.edit_work_schedule", work_schedule=work_schedule, attributes=attributes) sequence.disable_editing_work_schedule() def enable_editing_work_plan_schedules( - sequence: tool.Sequence, work_plan: Optional[ifcopenshell.entity_instance] = None + sequence: type[tool.Sequence], work_plan: Optional[ifcopenshell.entity_instance] = None ) -> None: sequence.enable_editing_work_plan_schedules(work_plan) -def add_work_schedule(ifc: tool.Ifc, sequence: tool.Sequence, name: str) -> ifcopenshell.entity_instance: +def add_work_schedule(ifc: type[tool.Ifc], sequence: type[tool.Sequence], name: str) -> ifcopenshell.entity_instance: predefined_type, object_type = sequence.get_user_predefined_type() return ifc.run("sequence.add_work_schedule", name=name, predefined_type=predefined_type, object_type=object_type) -def remove_work_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None: +def remove_work_schedule(ifc: type[tool.Ifc], work_schedule: ifcopenshell.entity_instance) -> None: ifc.run("sequence.remove_work_schedule", work_schedule=work_schedule) def assign_work_schedule( - ifc: tool.Ifc, work_plan: ifcopenshell.entity_instance, work_schedule: ifcopenshell.entity_instance + ifc: type[tool.Ifc], work_plan: ifcopenshell.entity_instance, work_schedule: ifcopenshell.entity_instance ) -> Union[ifcopenshell.entity_instance, None]: if work_schedule: return ifc.run("aggregate.assign_object", relating_object=work_plan, products=[work_schedule]) -def unassign_work_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None: +def unassign_work_schedule(ifc: type[tool.Ifc], work_schedule: ifcopenshell.entity_instance) -> None: ifc.run("aggregate.unassign_object", products=[work_schedule]) -def enable_editing_work_schedule(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def enable_editing_work_schedule(sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance) -> None: sequence.load_work_schedule_attributes(work_schedule) sequence.enable_editing_work_schedule(work_schedule) -def enable_editing_work_schedule_tasks(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def enable_editing_work_schedule_tasks( + sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance +) -> None: sequence.enable_editing_work_schedule_tasks(work_schedule) sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def load_task_tree(sequence: tool.Sequence, work_schedule) -> None: +def load_task_tree(sequence: type[tool.Sequence], work_schedule) -> None: sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def expand_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def expand_task(sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: sequence.expand_task(task) work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def expand_all_tasks(sequence: tool.Sequence) -> None: +def expand_all_tasks(sequence: type[tool.Sequence]) -> None: sequence.expand_all_tasks() work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def contract_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def contract_task(sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: sequence.contract_task(task) work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def contract_all_tasks(sequence: tool.Sequence) -> None: +def contract_all_tasks(sequence: type[tool.Sequence]) -> None: sequence.contract_all_tasks() work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def remove_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def remove_task(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: ifc.run("sequence.remove_task", task=task) work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) @@ -132,22 +136,24 @@ def remove_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entit sequence.disable_selecting_deleted_task() -def load_task_properties(sequence: tool.Sequence) -> None: +def load_task_properties(sequence: type[tool.Sequence]) -> None: sequence.load_task_properties() -def disable_editing_work_schedule(sequence: tool.Sequence) -> None: +def disable_editing_work_schedule(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_work_schedule() -def add_summary_task(ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def add_summary_task( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance +) -> None: ifc.run("sequence.add_task", work_schedule=work_schedule) sequence.load_task_tree(work_schedule) sequence.load_task_properties() def add_task( - ifc: tool.Ifc, sequence: tool.Sequence, parent_task: Optional[ifcopenshell.entity_instance] = None + ifc: type[tool.Ifc], sequence: type[tool.Sequence], parent_task: Optional[ifcopenshell.entity_instance] = None ) -> None: ifc.run("sequence.add_task", parent_task=parent_task) work_schedule = sequence.get_active_work_schedule() @@ -155,19 +161,19 @@ def add_task( sequence.load_task_properties() -def enable_editing_task_attributes(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def enable_editing_task_attributes(sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: sequence.load_task_attributes(task) sequence.enable_editing_task_attributes(task) -def edit_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def edit_task(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: attributes = sequence.get_task_attributes() ifc.run("sequence.edit_task", task=task, attributes=attributes) sequence.load_task_properties(task=task) sequence.disable_editing_task() -def copy_task_attribute(ifc: tool.Ifc, sequence: tool.Sequence, attribute_name: str) -> None: +def copy_task_attribute(ifc: type[tool.Ifc], sequence: type[tool.Sequence], attribute_name: str) -> None: for task in sequence.get_checked_tasks(): ifc.run( "sequence.edit_task", @@ -177,18 +183,20 @@ def copy_task_attribute(ifc: tool.Ifc, sequence: tool.Sequence, attribute_name: sequence.load_task_properties(task) -def duplicate_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def duplicate_task(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: ifc.run("sequence.duplicate_task", task=task) work_schedule = sequence.get_active_work_schedule() sequence.load_task_tree(work_schedule) sequence.load_task_properties() -def disable_editing_task(sequence: tool.Sequence) -> None: +def disable_editing_task(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_task() -def enable_editing_task_time(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def enable_editing_task_time( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance +) -> None: task_time = sequence.get_task_time(task) if task_time is None: task_time = ifc.run("sequence.add_task_time", task=task) @@ -196,7 +204,9 @@ def enable_editing_task_time(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcop sequence.enable_editing_task_time(task) -def edit_task_time(ifc: tool.Ifc, sequence: tool.Sequence, resource, task_time: ifcopenshell.entity_instance) -> None: +def edit_task_time( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], resource, task_time: ifcopenshell.entity_instance +) -> None: attributes = sequence.get_task_time_attributes() # TODO: nasty loop goes on when calendar props are messed up ifc.run("sequence.edit_task_time", task_time=task_time, attributes=attributes) @@ -206,34 +216,36 @@ def edit_task_time(ifc: tool.Ifc, sequence: tool.Sequence, resource, task_time: resource.load_resource_properties() -def assign_predecessor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def assign_predecessor(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: predecessor_task = sequence.get_highlighted_task() ifc.run("sequence.assign_sequence", relating_process=task, related_process=predecessor_task) sequence.load_task_properties() -def unassign_predecessor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def unassign_predecessor( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance +) -> None: predecessor_task = sequence.get_highlighted_task() ifc.run("sequence.unassign_sequence", relating_process=task, related_process=predecessor_task) sequence.load_task_properties() -def assign_successor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def assign_successor(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: successor_task = sequence.get_highlighted_task() ifc.run("sequence.assign_sequence", relating_process=successor_task, related_process=task) sequence.load_task_properties() -def unassign_successor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def unassign_successor(ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: successor_task = sequence.get_highlighted_task() ifc.run("sequence.unassign_sequence", relating_process=successor_task, related_process=task) sequence.load_task_properties() def assign_products( - ifc: tool.Ifc, - sequence: tool.Sequence, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], + spatial: type[tool.Spatial], task: ifcopenshell.entity_instance, products: Optional[list[ifcopenshell.entity_instance]] = None, ) -> None: @@ -244,9 +256,9 @@ def assign_products( def unassign_products( - ifc: tool.Ifc, - sequence: tool.Sequence, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], + spatial: type[tool.Spatial], task: ifcopenshell.entity_instance, products: Optional[list[ifcopenshell.entity_instance]] = None, ) -> None: @@ -257,9 +269,9 @@ def unassign_products( def assign_input_products( - ifc: tool.Ifc, - sequence: tool.Sequence, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], + spatial: type[tool.Spatial], task: ifcopenshell.entity_instance, products: Optional[list[ifcopenshell.entity_instance]] = None, ) -> None: @@ -270,9 +282,9 @@ def assign_input_products( def unassign_input_products( - ifc: tool.Ifc, - sequence: tool.Sequence, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], + spatial: type[tool.Spatial], task: ifcopenshell.entity_instance, products: Optional[list[ifcopenshell.entity_instance]] = None, ) -> None: @@ -282,7 +294,9 @@ def unassign_input_products( sequence.load_task_inputs(inputs) -def assign_resource(ifc: tool.Ifc, sequence: tool.Sequence, resource_tool, task: ifcopenshell.entity_instance) -> None: +def assign_resource( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], resource_tool, task: ifcopenshell.entity_instance +) -> None: resource = resource_tool.get_highlighted_resource() sub_resource = ifc.run( "resource.add_resource", @@ -296,8 +310,8 @@ def assign_resource(ifc: tool.Ifc, sequence: tool.Sequence, resource_tool, task: def unassign_resource( - ifc: tool.Ifc, - sequence: tool.Sequence, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], resource_tool, task: ifcopenshell.entity_instance, resource: ifcopenshell.entity_instance, @@ -308,54 +322,58 @@ def unassign_resource( resource_tool.load_resources() -def remove_work_calendar(ifc: tool.Ifc, work_calendar: ifcopenshell.entity_instance) -> None: +def remove_work_calendar(ifc: type[tool.Ifc], work_calendar: ifcopenshell.entity_instance) -> None: ifc.run("sequence.remove_work_calendar", work_calendar=work_calendar) -def add_work_calendar(ifc: tool.Ifc) -> ifcopenshell.entity_instance: +def add_work_calendar(ifc: type[tool.Ifc]) -> ifcopenshell.entity_instance: return ifc.run("sequence.add_work_calendar") -def edit_work_calendar(ifc: tool.Ifc, sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None: +def edit_work_calendar( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], work_calendar: ifcopenshell.entity_instance +) -> None: attributes = sequence.get_work_calendar_attributes() ifc.run("sequence.edit_work_calendar", work_calendar=work_calendar, attributes=attributes) sequence.disable_editing_work_calendar() sequence.load_task_properties() -def enable_editing_work_calendar(sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None: +def enable_editing_work_calendar(sequence: type[tool.Sequence], work_calendar: ifcopenshell.entity_instance) -> None: sequence.load_work_calendar_attributes(work_calendar) sequence.enable_editing_work_calendar(work_calendar) -def disable_editing_work_calendar(sequence: tool.Sequence) -> None: +def disable_editing_work_calendar(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_work_calendar() -def enable_editing_work_calendar_times(sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None: +def enable_editing_work_calendar_times( + sequence: type[tool.Sequence], work_calendar: ifcopenshell.entity_instance +) -> None: sequence.enable_editing_work_calendar_times(work_calendar) def add_work_time( - ifc: tool.Ifc, work_calendar: ifcopenshell.entity_instance, time_type: ifcopenshell.entity_instance + ifc: type[tool.Ifc], work_calendar: ifcopenshell.entity_instance, time_type: ifcopenshell.entity_instance ) -> ifcopenshell.entity_instance: return ifc.run("sequence.add_work_time", work_calendar=work_calendar, time_type=time_type) -def enable_editing_work_time(sequence: tool.Sequence, work_time: ifcopenshell.entity_instance) -> None: +def enable_editing_work_time(sequence: type[tool.Sequence], work_time: ifcopenshell.entity_instance) -> None: sequence.load_work_time_attributes(work_time) sequence.enable_editing_work_time(work_time) -def disable_editing_work_time(sequence: tool.Sequence) -> None: +def disable_editing_work_time(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_work_time() -def remove_work_time(ifc: tool.Ifc, work_time=None) -> None: +def remove_work_time(ifc: type[tool.Ifc], work_time=None) -> None: ifc.run("sequence.remove_work_time", work_time=work_time) -def edit_work_time(ifc: tool.Ifc, sequence: tool.Sequence) -> None: +def edit_work_time(ifc: type[tool.Ifc], sequence: type[tool.Sequence]) -> None: work_time = sequence.get_active_work_time() ifc.run("sequence.edit_work_time", work_time=work_time, attributes=sequence.get_work_time_attributes()) recurrence_pattern = work_time.RecurrencePattern @@ -369,32 +387,34 @@ def edit_work_time(ifc: tool.Ifc, sequence: tool.Sequence) -> None: def assign_recurrence_pattern( - ifc: tool.Ifc, work_time: ifcopenshell.entity_instance, recurrence_type: ifcopenshell.entity_instance + ifc: type[tool.Ifc], work_time: ifcopenshell.entity_instance, recurrence_type: ifcopenshell.entity_instance ) -> ifcopenshell.entity_instance: return ifc.run("sequence.assign_recurrence_pattern", parent=work_time, recurrence_type=recurrence_type) -def unassign_recurrence_pattern(ifc: tool.Ifc, recurrence_pattern: ifcopenshell.entity_instance) -> None: +def unassign_recurrence_pattern(ifc: type[tool.Ifc], recurrence_pattern: ifcopenshell.entity_instance) -> None: ifc.run("sequence.unassign_recurrence_pattern", recurrence_pattern=recurrence_pattern) -def add_time_period(ifc: tool.Ifc, sequence: tool.Sequence, recurrence_pattern: ifcopenshell.entity_instance) -> None: +def add_time_period( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], recurrence_pattern: ifcopenshell.entity_instance +) -> None: start_time, end_time = sequence.get_recurrence_pattern_times() ifc.run("sequence.add_time_period", recurrence_pattern=recurrence_pattern, start_time=start_time, end_time=end_time) sequence.reset_time_period() -def remove_time_period(ifc: tool.Ifc, time_period: ifcopenshell.entity_instance) -> None: +def remove_time_period(ifc: type[tool.Ifc], time_period: ifcopenshell.entity_instance) -> None: ifc.run("sequence.remove_time_period", time_period=time_period) -def enable_editing_task_calendar(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def enable_editing_task_calendar(sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> None: sequence.enable_editing_task_calendar(task) def edit_task_calendar( - ifc: tool.Ifc, - sequence: tool.Sequence, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], task: ifcopenshell.entity_instance, work_calendar: ifcopenshell.entity_instance, ) -> None: @@ -404,8 +424,8 @@ def edit_task_calendar( def remove_task_calendar( - ifc: tool.Ifc, - sequence: tool.Sequence, + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], task: ifcopenshell.entity_instance, work_calendar: ifcopenshell.entity_instance, ) -> None: @@ -414,38 +434,42 @@ def remove_task_calendar( sequence.load_task_properties() -def enable_editing_task_sequence(sequence: tool.Sequence) -> None: +def enable_editing_task_sequence(sequence: type[tool.Sequence]) -> None: sequence.enable_editing_task_sequence() sequence.load_task_properties() -def disable_editing_task_time(sequence: tool.Sequence) -> None: +def disable_editing_task_time(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_task_time() -def enable_editing_sequence_attributes(sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance) -> None: +def enable_editing_sequence_attributes( + sequence: type[tool.Sequence], rel_sequence: ifcopenshell.entity_instance +) -> None: sequence.enable_editing_rel_sequence_attributes(rel_sequence) sequence.load_rel_sequence_attributes(rel_sequence) def enable_editing_sequence_lag_time( - sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance, lag_time: ifcopenshell.entity_instance + sequence: type[tool.Sequence], rel_sequence: ifcopenshell.entity_instance, lag_time: ifcopenshell.entity_instance ) -> None: sequence.load_lag_time_attributes(lag_time) sequence.enable_editing_sequence_lag_time(rel_sequence) -def unassign_lag_time(ifc: tool.Ifc, sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance) -> None: +def unassign_lag_time( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], rel_sequence: ifcopenshell.entity_instance +) -> None: ifc.run("sequence.unassign_lag_time", rel_sequence=rel_sequence) sequence.load_task_properties() -def assign_lag_time(ifc: tool.Ifc, rel_sequence: ifcopenshell.entity_instance) -> None: +def assign_lag_time(ifc: type[tool.Ifc], rel_sequence: ifcopenshell.entity_instance) -> None: ifc.run("sequence.assign_lag_time", rel_sequence=rel_sequence, lag_value="P1D") def edit_sequence_attributes( - ifc: tool.Ifc, sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance + ifc: type[tool.Ifc], sequence: type[tool.Sequence], rel_sequence: ifcopenshell.entity_instance ) -> None: attributes = sequence.get_rel_sequence_attributes() ifc.run("sequence.edit_sequence", rel_sequence=rel_sequence, attributes=attributes) @@ -453,33 +477,41 @@ def edit_sequence_attributes( sequence.load_task_properties() -def edit_sequence_lag_time(ifc: tool.Ifc, sequence: tool.Sequence, lag_time: ifcopenshell.entity_instance) -> None: +def edit_sequence_lag_time( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], lag_time: ifcopenshell.entity_instance +) -> None: attributes = sequence.get_lag_time_attributes() ifc.run("sequence.edit_lag_time", lag_time=lag_time, attributes=attributes) sequence.disable_editing_rel_sequence() sequence.load_task_properties() -def disable_editing_rel_sequence(sequence: tool.Sequence) -> None: +def disable_editing_rel_sequence(sequence: type[tool.Sequence]) -> None: sequence.disable_editing_rel_sequence() -def select_task_outputs(sequence: tool.Sequence, spatial: tool.Spatial, task: ifcopenshell.entity_instance) -> None: +def select_task_outputs( + sequence: type[tool.Sequence], spatial: type[tool.Spatial], task: ifcopenshell.entity_instance +) -> None: spatial.select_products(products=sequence.get_task_outputs(task)) -def select_task_inputs(sequence: tool.Sequence, spatial: tool.Spatial, task: ifcopenshell.entity_instance) -> None: +def select_task_inputs( + sequence: type[tool.Sequence], spatial: type[tool.Spatial], task: ifcopenshell.entity_instance +) -> None: spatial.select_products(products=sequence.get_task_inputs(task)) def select_work_schedule_products( - sequence: tool.Sequence, spatial: tool.Spatial, work_schedule: ifcopenshell.entity_instance + sequence: type[tool.Sequence], spatial: type[tool.Spatial], work_schedule: ifcopenshell.entity_instance ) -> None: products = sequence.get_work_schedule_products(work_schedule) spatial.select_products(products) -def select_unassigned_work_schedule_products(ifc: tool.Ifc, sequence: tool.Sequence, spatial: tool.Spatial) -> None: +def select_unassigned_work_schedule_products( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], spatial: type[tool.Spatial] +) -> None: spatial.deselect_objects() products = ifc.get().by_type("IfcElement") work_schedule = sequence.get_active_work_schedule() @@ -488,11 +520,11 @@ def select_unassigned_work_schedule_products(ifc: tool.Ifc, sequence: tool.Seque spatial.select_products(selection) -def recalculate_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None: +def recalculate_schedule(ifc: type[tool.Ifc], work_schedule: ifcopenshell.entity_instance) -> None: ifc.run("sequence.recalculate_schedule", work_schedule=work_schedule) -def add_task_column(sequence: tool.Sequence, column_type: str, name: str, data_type: str) -> None: +def add_task_column(sequence: type[tool.Sequence], column_type: str, name: str, data_type: str) -> None: sequence.add_task_column(column_type, name, data_type) work_schedule = sequence.get_active_work_schedule() if work_schedule: @@ -500,11 +532,11 @@ def add_task_column(sequence: tool.Sequence, column_type: str, name: str, data_t sequence.load_task_properties() -def remove_task_column(sequence: tool.Sequence, name: str) -> None: +def remove_task_column(sequence: type[tool.Sequence], name: str) -> None: sequence.remove_task_column(name) -def set_task_sort_column(sequence: tool.Sequence, column: str) -> None: +def set_task_sort_column(sequence: type[tool.Sequence], column: str) -> None: sequence.set_task_sort_column(column) work_schedule = sequence.get_active_work_schedule() if work_schedule: @@ -512,7 +544,9 @@ def set_task_sort_column(sequence: tool.Sequence, column: str) -> None: sequence.load_task_properties() -def calculate_task_duration(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None: +def calculate_task_duration( + ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance +) -> None: ifc.run("sequence.calculate_task_duration", task=task) work_schedule = sequence.get_active_work_schedule() if work_schedule: @@ -520,11 +554,13 @@ def calculate_task_duration(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcope sequence.load_task_properties() -def load_animation_color_scheme(sequence: tool.Sequence, scheme: Union[ifcopenshell.entity_instance, None]) -> None: +def load_animation_color_scheme( + sequence: type[tool.Sequence], scheme: Union[ifcopenshell.entity_instance, None] +) -> None: sequence.load_animation_color_scheme(scheme) -def go_to_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> Union[None, str]: +def go_to_task(sequence: type[tool.Sequence], task: ifcopenshell.entity_instance) -> Union[None, str]: work_schedule = sequence.get_work_schedule(task) is_work_schedule_active = sequence.is_work_schedule_active(work_schedule) if is_work_schedule_active: @@ -533,26 +569,28 @@ def go_to_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> U return "Work schedule is not active" -def guess_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def guess_date_range(sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance) -> None: start, finish = sequence.guess_date_range(work_schedule) sequence.update_visualisation_date(start, finish) -def setup_default_task_columns(sequence: tool.Sequence) -> None: +def setup_default_task_columns(sequence: type[tool.Sequence]) -> None: sequence.setup_default_task_columns() -def add_task_bars(sequence: tool.Sequence) -> None: +def add_task_bars(sequence: type[tool.Sequence]) -> None: tasks = sequence.get_animation_bar_tasks() if tasks: sequence.create_bars(tasks) -def load_default_animation_color_scheme(sequence: tool.Sequence) -> None: +def load_default_animation_color_scheme(sequence: type[tool.Sequence]) -> None: sequence.load_default_animation_color_scheme() -def visualise_work_schedule_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def visualise_work_schedule_date_range( + sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance +) -> None: sequence.clear_objects_animation(include_blender_objects=False) settings = sequence.get_animation_settings() if settings: @@ -566,7 +604,7 @@ def visualise_work_schedule_date_range(sequence: tool.Sequence, work_schedule: i sequence.set_object_shading() -def visualise_work_schedule_date(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def visualise_work_schedule_date(sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance) -> None: sequence.clear_objects_animation(include_blender_objects=False) start_date = sequence.get_start_date() product_states = sequence.process_construction_state(work_schedule, start_date) @@ -574,13 +612,13 @@ def visualise_work_schedule_date(sequence: tool.Sequence, work_schedule: ifcopen sequence.set_object_shading() -def generate_gantt_chart(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None: +def generate_gantt_chart(sequence: type[tool.Sequence], work_schedule: ifcopenshell.entity_instance) -> None: json = sequence.create_tasks_json(work_schedule) sequence.generate_gantt_browser_chart(json, work_schedule) def load_product_related_tasks( - sequence: tool.Sequence, product: ifcopenshell.entity_instance + sequence: type[tool.Sequence], product: ifcopenshell.entity_instance ) -> Union[list[ifcopenshell.entity_instance], str]: filter_by_schedule = sequence.is_filter_by_active_schedule() if filter_by_schedule: @@ -596,7 +634,7 @@ def load_product_related_tasks( def reorder_task_nesting( - ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance, new_index: int + ifc: type[tool.Ifc], sequence: type[tool.Sequence], task: ifcopenshell.entity_instance, new_index: int ) -> Union[None, str]: is_sorting_enabled = sequence.is_sorting_enabled() is_sort_reversed = sequence.is_sort_reversed() @@ -610,18 +648,21 @@ def reorder_task_nesting( def create_baseline( - ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance, name: Optional[str] = None + ifc: type[tool.Ifc], + sequence: type[tool.Sequence], + work_schedule: ifcopenshell.entity_instance, + name: Optional[str] = None, ) -> None: ifc.run("sequence.create_baseline", work_schedule=work_schedule, name=name) -def clear_previous_animation(sequence: tool.Sequence) -> None: +def clear_previous_animation(sequence: type[tool.Sequence]) -> None: sequence.clear_objects_animation(include_blender_objects=False) -def add_animation_camera(sequence: tool.Sequence) -> None: +def add_animation_camera(sequence: type[tool.Sequence]) -> None: sequence.add_animation_camera() -def save_animation_color_scheme(sequence: tool.Sequence, name: str) -> None: +def save_animation_color_scheme(sequence: type[tool.Sequence], name: str) -> None: sequence.save_animation_color_scheme(name) diff --git a/src/bonsai/bonsai/core/spatial.py b/src/bonsai/bonsai/core/spatial.py index c70f835034..782395d04b 100644 --- a/src/bonsai/bonsai/core/spatial.py +++ b/src/bonsai/bonsai/core/spatial.py @@ -26,8 +26,8 @@ if TYPE_CHECKING: def reference_structure( - ifc: tool.Ifc, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + spatial: type[tool.Spatial], structure: Optional[ifcopenshell.entity_instance] = None, element: Optional[ifcopenshell.entity_instance] = None, ) -> Union[ifcopenshell.entity_instance, None]: @@ -36,8 +36,8 @@ def reference_structure( def dereference_structure( - ifc: tool.Ifc, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + spatial: type[tool.Spatial], structure: Optional[ifcopenshell.entity_instance] = None, element: Optional[ifcopenshell.entity_instance] = None, ) -> None: @@ -46,9 +46,9 @@ def dereference_structure( def assign_container( - ifc: tool.Ifc, - collector: tool.Collector, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + collector: type[tool.Collector], + spatial: type[tool.Spatial], container: ifcopenshell.entity_instance, element_obj: Optional[bpy.types.Object] = None, ) -> Union[ifcopenshell.entity_instance, None]: @@ -61,24 +61,24 @@ def assign_container( return rel -def enable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None: +def enable_editing_container(spatial: type[tool.Spatial], obj: bpy.types.Object) -> None: spatial.set_target_container_as_default() spatial.enable_editing(obj) -def disable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None: +def disable_editing_container(spatial: type[tool.Spatial], obj: bpy.types.Object) -> None: spatial.disable_editing(obj) -def remove_container(ifc: tool.Ifc, collector: tool.Collector, obj: bpy.types.Object) -> None: +def remove_container(ifc: type[tool.Ifc], collector: type[tool.Collector], obj: bpy.types.Object) -> None: ifc.run("spatial.unassign_container", products=[ifc.get_entity(obj)]) collector.assign(obj) def copy_to_container( - ifc: tool.Ifc, - collector: tool.Collector, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + collector: type[tool.Collector], + spatial: type[tool.Spatial], obj: bpy.types.Object, containers: list[ifcopenshell.entity_instance], ) -> list[ifcopenshell.entity_instance]: @@ -102,57 +102,71 @@ def copy_to_container( def select_container( - ifc: tool.Ifc, spatial: tool.Spatial, container: ifcopenshell.entity_instance, selection_mode: str = "ADD" + ifc: type[tool.Ifc], + spatial: type[tool.Spatial], + container: ifcopenshell.entity_instance, + selection_mode: str = "ADD", ) -> None: spatial.set_active_object(ifc.get_object(container), selection_mode=selection_mode) -def select_similar_container(ifc: tool.Ifc, spatial: tool.Spatial, obj: bpy.types.Object) -> None: +def select_similar_container(ifc: type[tool.Ifc], spatial: type[tool.Spatial], obj: bpy.types.Object) -> None: element = ifc.get_entity(obj) if element: spatial.select_products(spatial.get_decomposed_elements(spatial.get_container(element))) -def select_product(spatial: tool.Spatial, product: ifcopenshell.entity_instance) -> None: +def select_product(spatial: type[tool.Spatial], product: ifcopenshell.entity_instance) -> None: spatial.select_products([product]) -def import_spatial_decomposition(spatial: tool.Spatial) -> None: +def import_spatial_decomposition(spatial: type[tool.Spatial]) -> None: spatial.import_spatial_decomposition() -def set_orientation_slot(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None: +def set_orientation_slot(spatial: type[tool.Spatial], container: ifcopenshell.entity_instance) -> None: spatial.create_orientation_slot(container) -def contract_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance, is_recursive: bool) -> None: +def contract_container( + spatial: type[tool.Spatial], container: ifcopenshell.entity_instance, is_recursive: bool +) -> None: spatial.contract_container(container, is_recursive=is_recursive) spatial.import_spatial_decomposition() -def expand_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance, is_recursive: bool) -> None: +def expand_container(spatial: type[tool.Spatial], container: ifcopenshell.entity_instance, is_recursive: bool) -> None: spatial.expand_container(container, is_recursive=is_recursive) spatial.import_spatial_decomposition() def delete_container( - ifc: tool.Ifc, spatial: tool.Spatial, geometry: tool.Geometry, container: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + spatial: type[tool.Spatial], + geometry: type[tool.Geometry], + container: ifcopenshell.entity_instance, ) -> None: geometry.delete_ifc_object(ifc.get_object(container)) spatial.import_spatial_decomposition() -def toggle_container_element(spatial: tool.Spatial, element_index: int, is_recursive: bool) -> None: +def toggle_container_element(spatial: type[tool.Spatial], element_index: int, is_recursive: bool) -> None: spatial.toggle_container_element(element_index, is_recursive=is_recursive) spatial.load_contained_elements() -def select_decomposed_element(ifc: tool.Ifc, spatial: tool.Spatial, element: ifcopenshell.entity_instance) -> None: +def select_decomposed_element( + ifc: type[tool.Ifc], spatial: type[tool.Spatial], element: ifcopenshell.entity_instance +) -> None: spatial.set_active_object(ifc.get_object(element)) def generate_space( - ifc: tool.Ifc, model: tool.Model, root: tool.Root, spatial: tool.Spatial, type: tool.Type + ifc: type[tool.Ifc], + model: type[tool.Model], + root: type[tool.Root], + spatial: type[tool.Spatial], + type: type[tool.Type], ) -> Union[None, str]: """ :return: None if successful, error message string if not. @@ -217,7 +231,9 @@ def generate_space( spatial.import_spatial_decomposition() -def generate_spaces_from_walls(ifc: tool.Ifc, spatial: tool.Spatial, collector: tool.Collector) -> None: +def generate_spaces_from_walls( + ifc: type[tool.Ifc], spatial: type[tool.Spatial], collector: type[tool.Collector] +) -> None: z = spatial.get_active_obj_z() h = spatial.get_active_obj_height() @@ -237,7 +253,7 @@ def generate_spaces_from_walls(ifc: tool.Ifc, spatial: tool.Spatial, collector: spatial.assign_ifcspace_class_to_obj(obj) -def toggle_space_visibility(ifc: tool.Ifc, spatial: tool.Spatial) -> None: +def toggle_space_visibility(ifc: type[tool.Ifc], spatial: type[tool.Spatial]) -> None: model = ifc.get() spaces = model.by_type("IfcSpace") if not spaces: @@ -245,7 +261,7 @@ def toggle_space_visibility(ifc: tool.Ifc, spatial: tool.Spatial) -> None: spatial.toggle_spaces_visibility_wired_and_textured(spaces) -def toggle_hide_spaces(ifc: tool.Ifc, spatial: tool.Spatial) -> None: +def toggle_hide_spaces(ifc: type[tool.Ifc], spatial: type[tool.Spatial]) -> None: model = ifc.get() spaces = model.by_type("IfcSpace") if not spaces: @@ -253,7 +269,7 @@ def toggle_hide_spaces(ifc: tool.Ifc, spatial: tool.Spatial) -> None: spatial.toggle_hide_spaces(spaces) -def set_default_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None: +def set_default_container(spatial: type[tool.Spatial], container: ifcopenshell.entity_instance) -> None: spatial.set_default_container(container) diff --git a/src/bonsai/bonsai/core/structural.py b/src/bonsai/bonsai/core/structural.py index f9570a8d15..4c31883fe5 100644 --- a/src/bonsai/bonsai/core/structural.py +++ b/src/bonsai/bonsai/core/structural.py @@ -25,7 +25,9 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural) -> ifcopenshell.entity_instance: +def add_structural_analysis_model( + ifc: type[tool.Ifc], structural: type[tool.Structural] +) -> ifcopenshell.entity_instance: result = ifc.run("structural.add_structural_analysis_model") structural.load_structural_analysis_models() structural.ensure_representation_contexts() @@ -33,7 +35,7 @@ def add_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural) -> def assign_structural_analysis_model( - ifc: tool.Ifc, + ifc: type[tool.Ifc], products: list[ifcopenshell.entity_instance], structural_analysis_model: ifcopenshell.entity_instance, ) -> None: @@ -44,15 +46,15 @@ def assign_structural_analysis_model( ) -def disable_editing_structural_analysis_model(structural: tool.Structural) -> None: +def disable_editing_structural_analysis_model(structural: type[tool.Structural]) -> None: structural.disable_editing_structural_analysis_model() -def disable_structural_analysis_model_editing_ui(structural: tool.Structural) -> None: +def disable_structural_analysis_model_editing_ui(structural: type[tool.Structural]) -> None: structural.disable_structural_analysis_model_editing_ui() -def edit_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural) -> None: +def edit_structural_analysis_model(ifc: type[tool.Ifc], structural: type[tool.Structural]) -> None: attributes = structural.get_structural_analysis_model_attributes() ifc.run( "structural.edit_structural_analysis_model", @@ -63,28 +65,28 @@ def edit_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural) - structural.disable_editing_structural_analysis_model() -def enable_editing_structural_analysis_model(structural: tool.Structural, model: Union[int, None]) -> None: +def enable_editing_structural_analysis_model(structural: type[tool.Structural], model: Union[int, None]) -> None: structural.enable_editing_structural_analysis_model(model) -def enable_structural_analysis_model_editing_ui(structural: tool.Structural) -> None: +def enable_structural_analysis_model_editing_ui(structural: type[tool.Structural]) -> None: structural.enable_structural_analysis_model_editing_ui() -def load_structural_analysis_model_attributes(structural: tool.Structural, model: Union[int, None]) -> None: +def load_structural_analysis_model_attributes(structural: type[tool.Structural], model: Union[int, None]) -> None: data = structural.get_ifc_structural_analysis_model_attributes(model) if data is None: return structural.load_structural_analysis_model_attributes(data) -def load_structural_analysis_models(structural: tool.Structural) -> None: +def load_structural_analysis_models(structural: type[tool.Structural]) -> None: structural.load_structural_analysis_models() structural.enable_structural_analysis_model_editing_ui() # structural.disable_editing_structural_analysis_model() -def remove_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural, model: int) -> None: +def remove_structural_analysis_model(ifc: type[tool.Ifc], structural: type[tool.Structural], model: int) -> None: ifc.run( "structural.remove_structural_analysis_model", structural_analysis_model=ifc.get().by_id(model), @@ -93,7 +95,7 @@ def remove_structural_analysis_model(ifc: tool.Ifc, structural: tool.Structural, def unassign_structural_analysis_model( - ifc: tool.Ifc, + ifc: type[tool.Ifc], products: list[ifcopenshell.entity_instance], structural_analysis_model: ifcopenshell.entity_instance, ) -> None: diff --git a/src/bonsai/bonsai/core/system.py b/src/bonsai/bonsai/core/system.py index 8b2edb2e41..9707c5f6bc 100644 --- a/src/bonsai/bonsai/core/system.py +++ b/src/bonsai/bonsai/core/system.py @@ -25,62 +25,62 @@ if TYPE_CHECKING: import bonsai.tool as tool -def load_systems(system: tool.System) -> None: +def load_systems(system: type[tool.System]) -> None: system.import_systems() system.enable_system_editing_ui() system.disable_editing_system() -def disable_system_editing_ui(system: tool.System) -> None: +def disable_system_editing_ui(system: type[tool.System]) -> None: system.disable_editing_system() system.disable_system_editing_ui() -def add_system(ifc: tool.Ifc, system: tool.System, ifc_class: str) -> None: +def add_system(ifc: type[tool.Ifc], system: type[tool.System], ifc_class: str) -> None: ifc.run("system.add_system", ifc_class=ifc_class) system.import_systems() -def edit_system(ifc: tool.Ifc, system_tool: tool.System, system: ifcopenshell.entity_instance) -> None: +def edit_system(ifc: type[tool.Ifc], system_tool: type[tool.System], system: ifcopenshell.entity_instance) -> None: attributes = system_tool.export_system_attributes() ifc.run("system.edit_system", system=system, attributes=attributes) system_tool.disable_editing_system() system_tool.import_systems() -def remove_system(ifc: tool.Ifc, system_tool: tool.System, system: ifcopenshell.entity_instance) -> None: +def remove_system(ifc: type[tool.Ifc], system_tool: type[tool.System], system: ifcopenshell.entity_instance) -> None: ifc.run("system.remove_system", system=system) system_tool.import_systems() -def enable_editing_system(system_tool: tool.System, system: ifcopenshell.entity_instance) -> None: +def enable_editing_system(system_tool: type[tool.System], system: ifcopenshell.entity_instance) -> None: system_tool.import_system_attributes(system) system_tool.set_active_edited_system(system) -def disable_editing_system(system: tool.System) -> None: +def disable_editing_system(system: type[tool.System]) -> None: system.disable_editing_system() def assign_system( - ifc: tool.Ifc, system: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance] + ifc: type[tool.Ifc], system: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance] ) -> None: ifc.run("system.assign_system", products=products, system=system) def unassign_system( - ifc: tool.Ifc, system: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance] + ifc: type[tool.Ifc], system: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance] ) -> None: ifc.run("system.unassign_system", products=products, system=system) -def select_system_products(system_tool: tool.System, system: ifcopenshell.entity_instance) -> None: +def select_system_products(system_tool: type[tool.System], system: ifcopenshell.entity_instance) -> None: system_tool.select_system_products(system) system_tool.set_active_system(system) def show_ports( - ifc: tool.Ifc, system: tool.System, spatial: tool.Spatial, element: ifcopenshell.entity_instance + ifc: type[tool.Ifc], system: type[tool.System], spatial: type[tool.Spatial], element: ifcopenshell.entity_instance ) -> None: obj = ifc.get_object(element) if obj and ifc.is_moved(obj): @@ -91,7 +91,7 @@ def show_ports( spatial.select_products(ports) -def hide_ports(ifc: tool.Ifc, system: tool.System, element: ifcopenshell.entity_instance) -> None: +def hide_ports(ifc: type[tool.Ifc], system: type[tool.System], element: ifcopenshell.entity_instance) -> None: obj = ifc.get_object(element) if obj and ifc.is_moved(obj): system.run_geometry_edit_object_placement(obj=obj) @@ -105,27 +105,29 @@ def hide_ports(ifc: tool.Ifc, system: tool.System, element: ifcopenshell.entity_ system.delete_element_objects(ports) -def add_port(ifc: tool.Ifc, system: tool.System, element: ifcopenshell.entity_instance) -> None: +def add_port(ifc: type[tool.Ifc], system: type[tool.System], element: ifcopenshell.entity_instance) -> None: system.load_ports(element, system.get_ports(element)) obj = system.create_empty_at_cursor_with_element_orientation(element) port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort", should_add_representation=False) ifc.run("system.assign_port", element=element, port=port) -def remove_port(ifc: tool.Ifc, system: tool.System, port: ifcopenshell.entity_instance) -> None: +def remove_port(ifc: type[tool.Ifc], system: type[tool.System], port: ifcopenshell.entity_instance) -> None: system.delete_element_objects([port]) ifc.run("root.remove_product", product=port) -def connect_port(ifc: tool.Ifc, port1: ifcopenshell.entity_instance, port2: ifcopenshell.entity_instance) -> None: +def connect_port(ifc: type[tool.Ifc], port1: ifcopenshell.entity_instance, port2: ifcopenshell.entity_instance) -> None: ifc.run("system.connect_port", port1=port1, port2=port2) -def disconnect_port(ifc: tool.Ifc, port: ifcopenshell.entity_instance) -> None: +def disconnect_port(ifc: type[tool.Ifc], port: ifcopenshell.entity_instance) -> None: ifc.run("system.disconnect_port", port=port) -def set_flow_direction(ifc: tool.Ifc, system: tool.System, port: ifcopenshell.entity_instance, direction: str) -> None: +def set_flow_direction( + ifc: type[tool.Ifc], system: type[tool.System], port: ifcopenshell.entity_instance, direction: str +) -> None: port2 = system.get_connected_port(port) if not port2: return diff --git a/src/bonsai/bonsai/core/web.py b/src/bonsai/bonsai/core/web.py index e5a405da2a..187d479b6b 100644 --- a/src/bonsai/bonsai/core/web.py +++ b/src/bonsai/bonsai/core/web.py @@ -1,8 +1,35 @@ -def generate_port_number(web): +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . + +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Union + +if TYPE_CHECKING: + import bpy + import ifcopenshell + import bonsai.tool as tool + + +def generate_port_number(web: type[tool.Web]) -> int: return web.generate_port_number() -def connect_websocket_server(web, port, page): +def connect_websocket_server(web: type[tool.Web], port: int, page: str) -> None: # check if port already has a server listening to it if web.is_port_available(port): web.start_websocket_server(port) @@ -15,13 +42,13 @@ def connect_websocket_server(web, port, page): web.connect_websocket_server(port) -def disconnect_websocket_server(web): +def disconnect_websocket_server(web: type[tool.Web]) -> None: web.disconnect_websocket_server() -def kill_websocket_server(web): +def kill_websocket_server(web: type[tool.Web]) -> None: web.kill_websocket_server() -def open_web_browser(web, port, page): +def open_web_browser(web: type[tool.Web], port: int, page: str) -> None: web.open_web_browser(port, page) diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index 860771c3e2..623a3e6dae 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -159,7 +159,6 @@ class Project(bonsai.core.tool.Project): @classmethod def set_default_modeling_dimensions(cls) -> None: props = tool.Model.get_model_props() - unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) props.extrusion_depth = 3 props.length = 1 props.rl1 = 0 diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index 2ba09f70bb..b1718cf099 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -355,6 +355,16 @@ class Snap(bonsai.core.tool.Snap): # Objects objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox) + # Wireframes + # For wireframe we have to get all the objects so we can further calculate edge intersection + for snap_obj in objs_to_raycast: + if snap_obj.type in {"EMPTY", "CURVE"} or (snap_obj.type == "MESH" and len(snap_obj.data.polygons) == 0): + snap_points = tool.Raycast.ray_cast_by_proximity(context, event, snap_obj) + if snap_points: + for point in snap_points: + point["group"] = "Wireframe" + detected_snaps.append(point) + if (space.shading.type == "SOLID" and space.shading.show_xray) or ( space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe ): @@ -374,12 +384,7 @@ class Snap(bonsai.core.tool.Snap): if snap_obj.type in {"EMPTY", "CURVE"} or ( snap_obj.type == "MESH" and len(snap_obj.data.polygons) == 0 ): - snap_points = tool.Raycast.ray_cast_by_proximity(context, event, snap_obj) - if snap_points: - for point in snap_points: - point["group"] = "Wireframe" - detected_snaps.append(point) - + continue # Meshes else: # Add face snap diff --git a/src/bonsai/bonsai/tool/unit.py b/src/bonsai/bonsai/tool/unit.py index 311a288eca..dc330e479b 100644 --- a/src/bonsai/bonsai/tool/unit.py +++ b/src/bonsai/bonsai/tool/unit.py @@ -69,6 +69,7 @@ class Unit(bonsai.core.tool.Unit): def get_scene_unit_name(cls, unit_type: UNIT_TYPE) -> str: bim_props = tool.Blender.get_bim_props() if unit_type == "LENGTHUNIT": + assert bpy.context.scene props = bpy.context.scene.unit_settings if props.length_unit == "MILES": return "mile" @@ -84,12 +85,13 @@ class Unit(bonsai.core.tool.Unit): elif unit_type == "VOLUMEUNIT": return bim_props.volume_unit else: - assert_never() + assert_never(unit_type) @classmethod def get_scene_unit_si_prefix(cls, unit_type: UNIT_TYPE) -> Union[str, None]: bim_props = tool.Blender.get_bim_props() if unit_type == "LENGTHUNIT": + assert bpy.context.scene props = bpy.context.scene.unit_settings if props.length_unit == "ADAPTIVE" or props.length_unit == "METERS": return @@ -163,6 +165,7 @@ class Unit(bonsai.core.tool.Unit): @classmethod def is_scene_unit_metric(cls) -> bool: + assert bpy.context.scene return bpy.context.scene.unit_settings.system in ["METRIC", "NONE"] @classmethod @@ -189,6 +192,7 @@ class Unit(bonsai.core.tool.Unit): @classmethod def blender_format_unit(cls, value: float) -> str: + assert bpy.context.scene return bpy.utils.units.to_string( bpy.context.scene.unit_settings.system, "LENGTH", diff --git a/src/ifcgeom/Iterator.cpp b/src/ifcgeom/Iterator.cpp new file mode 100644 index 0000000000..0e82e2a678 --- /dev/null +++ b/src/ifcgeom/Iterator.cpp @@ -0,0 +1,652 @@ +#include "Iterator.h" + +/** +* @return Returns true if the iterator is initialized with any elements, false otherwise. +* +* @note +* - A true return value does not guarantee successful initialization of all elements. +* Some elements may have failed to initialize. Check had_error_processing_elements() +* to see whether there were errors during the initialization. +* +* - For non-concurrent iterators, a false return may occur if initialization of the first +* element fails, even if subsequent elements could be initialized successfully. +*/ +bool IfcGeom::Iterator::initialize() { + using std::chrono::high_resolution_clock; + + if (initialization_outcome_) { + return *initialization_outcome_; + } + + time_points[0] = high_resolution_clock::now(); + std::vector reps; + if (num_threads_ != 1) { + // @todo this shouldn't be necessary with properly immutable taxonomy items + converter_->mapping()->use_caching() = false; + } + try { + converter_->mapping()->get_representations(reps, filters_); + } catch (const std::exception& e) { + Logger::Error(e); + } + time_points[1] = high_resolution_clock::now(); + + for (auto& task : reps) { + geometry_conversion_result res; + res.index = task.index; + if (!settings_.get().get()) { + res.representation = task.representation; + res.products_2 = task.products; + } else { + res.item = converter_->mapping()->map(task.representation); + if (!res.item) { + continue; + } + std::transform(task.products->begin(), task.products->end(), std::back_inserter(res.products), [this, &res](IfcUtil::IfcBaseClass* prod) { + auto prod_item = converter_->mapping()->map(prod); + return std::make_pair(prod->as(), ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix); + }); + } + tasks_.push_back(res); + } + + if (settings_.get().get() && settings_.get().get()) { + std::unordered_map< + ifcopenshell::geometry::taxonomy::item::ptr, + std::vector>> folded; + + for (auto& r : tasks_) { + auto i = r.item; + + Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity(); + + while (auto col = std::dynamic_pointer_cast(i)) { + if (col->children.size() == 1) { + if (col->matrix) { + m4 *= col->matrix->ccomponents(); + } + i = col->children[0]; + } else { + break; + } + } + + for (auto& p : r.products) { + auto pl = ifcopenshell::geometry::taxonomy::matrix4::ptr(p.second->clone_()); + pl->components() *= m4; + folded[i].push_back( + { p.first, pl } + ); + } + } + + if (folded.size() < tasks_.size()) { + auto old_size = tasks_.size(); + tasks_.clear(); + size_t i = 0; + for (auto& p : folded) { + tasks_.emplace_back(); + tasks_.back().index = i++; + tasks_.back().item = p.first; + tasks_.back().products = p.second; + } + Logger::Notice("Merged " + std::to_string(old_size) + " tasks into " + std::to_string(tasks_.size()) + " tasks due to permissive shape reuse"); + } + } + + size_t num_products = 0; + for (auto& r : tasks_) { + num_products += !settings_.get().get() ? r.products_2->size() : r.products.size(); + } + + time_points[2] = high_resolution_clock::now(); + + /* + // What to do, map representation and product individually? + // There needs to be two options, mapped item respecting (does that still work?), and optimized based on topology sorting. + // Or is the sorting not necessary if we just cache? + + std::vector items; + std::map placements; + std::transform(products.begin(), products.end(), std::back_inserter(items), [this, &placements](IfcUtil::IfcBaseClass* p) { + auto item = converter_->mapping()->map(p); + // Product placements do not affect item reuse and should temporarily be swapped to identity + if (item) { + std::swap(placements[item], ((taxonomy::geom_ptr)item)->matrix); + } + return item; + }); + items.erase(std::remove(items.begin(), items.end(), nullptr), items.end()); + std::sort(items.begin(), items.end(), taxonomy::less); + auto it = items.begin(); + while (it < items.end()) { + auto jt = std::upper_bound(it, items.end(), *it, taxonomy::less); + geometry_conversion_result r; + r.item = *it; + std::transform(it, jt, std::back_inserter(r.products), [&r, &placements](taxonomy::ptr product_node) { + return std::make_pair((IfcUtil::IfcBaseEntity*) product_node->instance, placements[product_node]); + }); + tasks_.push_back(r); + it = jt; + } + */ + + Logger::Notice("Created " + boost::lexical_cast(tasks_.size()) + " tasks for " + boost::lexical_cast(num_products) + " products"); + + if (tasks_.size() == 0) { + Logger::Warning("No representations encountered, aborting"); + initialization_outcome_.reset(false); + } else { + + task_iterator_ = tasks_.begin(); + + task_result_index_ = 0; + done = 0; + total = (int)tasks_.size(); + + if (num_threads_ != 1) { + init_future_ = std::async(std::launch::async, [this]() { process_concurrently(); }); + + // wait for the first element, because after init(), get() can be called. + // so the element conversion must succeed + initialization_outcome_ = wait_for_element(); + } else { + initialization_outcome_ = create(); + } + } + + return *initialization_outcome_; +} + +void IfcGeom::Iterator::process_finished_rep(geometry_conversion_result* rep) { + if (rep->elements.empty()) { + return; + } + + std::lock_guard lk(element_ready_mutex_); + + all_processed_elements_.insert(all_processed_elements_.end(), rep->elements.begin(), rep->elements.end()); + all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep->breps.begin(), rep->breps.end()); + + if (!task_result_ptr_initialized) { + task_result_iterator_ = all_processed_elements_.begin(); + native_task_result_iterator_ = all_processed_native_elements_.begin(); + task_result_ptr_initialized = true; + } + + progress_ = (int)(++processed_ * 100 / tasks_.size()); +} + +void IfcGeom::Iterator::process_concurrently() { + size_t conc_threads = num_threads_; + if (conc_threads > tasks_.size()) { + conc_threads = tasks_.size(); + } + + kernel_pool.reserve(conc_threads); + for (unsigned i = 0; i < conc_threads; ++i) { + kernel_pool.push_back(new ifcopenshell::geometry::Converter(geometry_library_, ifc_file, settings_)); + } + + std::vector> threadpool; + + for (auto& rep : tasks_) { + ifcopenshell::geometry::Converter* K = nullptr; + if (threadpool.size() < kernel_pool.size()) { + K = kernel_pool[threadpool.size()]; + } + + while (threadpool.size() == conc_threads) { + for (int i = 0; i < (int)threadpool.size(); i++) { + auto& fu = threadpool[i]; + std::future_status status; + status = fu.wait_for(std::chrono::seconds(0)); + if (status == std::future_status::ready) { + process_finished_rep(fu.get()); + + std::swap(threadpool[i], threadpool.back()); + threadpool.pop_back(); + std::swap(kernel_pool[i], kernel_pool.back()); + K = kernel_pool.back(); + break; + } // if + } // for + } // while + + std::future fu = std::async( + std::launch::async, [this]( + ifcopenshell::geometry::Converter* kernel, + ifcopenshell::geometry::Settings settings, + geometry_conversion_result* rep) { + // Catch exceptions to be safe from freezing the iterator. + try { + this->create_element_(kernel, settings, rep); + } catch (const std::exception& e) { + Logger::Error( + std::string("Exception '") + e.what() + + std::string("' occurred while iterator was creating a shape: "), + rep->item->instance + ); + had_error_processing_elements_ = true; + } catch (...) { + Logger::Error( + "Unknown exception occurred while iteartor was creating a shape: ", + rep->item->instance + ); + had_error_processing_elements_ = true; + } + return rep; + }, + K, + std::ref(settings_), + &rep); + + if (terminating_) { + break; + } + + threadpool.emplace_back(std::move(fu)); + } + + for (auto& fu : threadpool) { + process_finished_rep(fu.get()); + } + + finished_ = true; + + Logger::SetProduct(boost::none); + + if (!terminating_) { + Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + + " objects) "); + } +} + +/// Computes model's bounding box (bounds_min and bounds_max). +/// @note Can take several minutes for large files. +void IfcGeom::Iterator::compute_bounds(bool with_geometry) +{ + for (int i = 0; i < 3; ++i) { + bounds_min_.components()(i) = std::numeric_limits::infinity(); + bounds_max_.components()(i) = -std::numeric_limits::infinity(); + } + + if (with_geometry) { + size_t num_created = 0; + do { + IfcGeom::Element* geom_object = get(); + const IfcGeom::TriangulationElement* o = static_cast(geom_object); + const IfcGeom::Representation::Triangulation& mesh = o->geometry(); + auto mat = o->transformation().data()->ccomponents(); + Eigen::Vector4d vec, transformed; + + for (typename std::vector::const_iterator it = mesh.verts().begin(); it != mesh.verts().end();) { + const double& x = *(it++); + const double& y = *(it++); + const double& z = *(it++); + vec << x, y, z, 1.; + transformed = mat * vec; + + for (int i = 0; i < 3; ++i) { + bounds_min_.components()(i) = std::min(bounds_min_.components()(i), transformed(i)); + bounds_max_.components()(i) = std::max(bounds_max_.components()(i), transformed(i)); + } + } + } while (++num_created, next()); + } else { + std::vector reps; + converter_->mapping()->get_representations(reps, filters_); + + std::vector products; + for (auto& r : reps) { + std::copy(r.products->begin(), r.products->end(), std::back_inserter(products)); + } + + for (auto& product : products) { + auto prod_item = converter_->mapping()->map(product); + auto vec = ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix->translation_part(); + + for (int i = 0; i < 3; ++i) { + bounds_min_.components()(i) = std::min(bounds_min_.components()(i), vec(i)); + bounds_max_.components()(i) = std::max(bounds_max_.components()(i), vec(i)); + } + } + } +} + +const IfcUtil::IfcBaseClass* IfcGeom::Iterator::create_shape_model_for_next_entity() { + geometry_conversion_result* task = nullptr; + for (; task_iterator_ < tasks_.end();) { + task = &*task_iterator_++; + create_element_(converter_, settings_, task); + if (task->elements.empty()) { + task = nullptr; + } else { + break; + } + } + if (task) { + process_finished_rep(task); + return task->item->instance->as(); + } else { + return nullptr; + } +} + +void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kernel, ifcopenshell::geometry::Settings settings, geometry_conversion_result* rep) +{ + if (!settings_.get().get()) { + rep->item = kernel->mapping()->map(rep->representation); + if (!rep->item) { + return; + } + std::transform(rep->products_2->begin(), rep->products_2->end(), std::back_inserter(rep->products), [this, &rep, kernel](IfcUtil::IfcBaseClass* prod) { + auto prod_item = kernel->mapping()->map(prod); + return std::make_pair(prod->as(), ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix); + }); + } else { + } + + auto product_node = rep->products.front(); + const IfcUtil::IfcBaseEntity* product = product_node.first; + const auto& place = product_node.second; + + Logger::SetProduct(product); + + IfcGeom::BRepElement* brep = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product, place, rep]() { + return kernel->create_brep_for_representation_and_product(rep->item, product, place); + })); + + if (!brep) { + return; + } + + auto elem = process_based_on_settings(settings, brep); + if (!elem) { + return; + } + + rep->breps = { brep }; + rep->elements = { elem }; + + for (auto it = rep->products.begin() + 1; it != rep->products.end(); ++it) { + const auto& p = *it; + const IfcUtil::IfcBaseEntity* product2 = p.first; + const auto& place2 = p.second; + + IfcGeom::BRepElement* brep2 = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product2->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product2, place2, brep]() { + return kernel->create_brep_for_processed_representation(product2, place2, brep); + })); + if (brep2) { + auto elem2 = process_based_on_settings(settings, brep2, dynamic_cast(elem)); + if (elem2) { + rep->breps.push_back(brep2); + rep->elements.push_back(elem2); + } + } + } +} + +IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, IfcGeom::TriangulationElement* previous) +{ + if (settings.get().get() == ifcopenshell::geometry::settings::SERIALIZED) { + try { + return new IfcGeom::SerializedElement(*elem); + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); + return nullptr; + } + } else if (settings.get().get() == ifcopenshell::geometry::settings::TRIANGULATED) { + // the part before the hyphen is the representation id + auto gid2 = elem->geometry().id(); + auto hyphen = gid2.find("-"); + if (hyphen != std::string::npos) { + gid2 = gid2.substr(0, hyphen); + } + + return decorate_with_cache_(GeometrySerializer::READ_TRIANGULATION, elem->guid(), gid2, [elem, previous]() { + try { + if (!previous) { + return new TriangulationElement(*elem); + } else { + return new TriangulationElement(*elem, previous->geometry_pointer()); + } + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); + } + return (TriangulationElement*)nullptr; + }); + } else { + return elem; + } +} + +bool IfcGeom::Iterator::wait_for_element() { + while (true) { + size_t s; + { + std::lock_guard lk(element_ready_mutex_); + s = all_processed_elements_.size(); + } + if (s > async_elements_returned_) { + ++async_elements_returned_; + return true; + } else if (finished_) { + return false; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } +} + +void IfcGeom::Iterator::log_timepoints() const { + using std::chrono::high_resolution_clock; + using std::chrono::duration; + using namespace std::string_literals; + + std::array labels = { + "Initializing mapping"s, + "Performing mapping"s, + "Geometry interpretation"s + }; + + for (auto it = time_points.begin() + 1; it != time_points.end(); ++it) { + auto jt = it - 1; + duration ms_double = (*it) - (*jt); + Logger::Notice(labels[std::distance(time_points.begin(), jt)] + " took " + std::to_string(ms_double.count()) + "ms"); + } +} + +/// Moves to the next shape representation, create its geometry, and returns the associated product. +/// Use get() to retrieve the created geometry. +const IfcUtil::IfcBaseClass* IfcGeom::Iterator::next() { + using std::chrono::high_resolution_clock; + + if (*native_task_result_iterator_ != *task_result_iterator_) { + delete* native_task_result_iterator_; + } + delete* task_result_iterator_; + + if (num_threads_ != 1) { + if (!wait_for_element()) { + Logger::SetProduct(boost::none); + time_points[3] = high_resolution_clock::now(); + log_timepoints(); + return nullptr; + } + + task_result_iterator_++; + native_task_result_iterator_++; + + return (*task_result_iterator_)->product(); + } else { + // Increment the iterator over the list of products using the current + // shape representation + if (task_result_iterator_ == --all_processed_elements_.end()) { + if (!create()) { + Logger::SetProduct(boost::none); + time_points[3] = high_resolution_clock::now(); + log_timepoints(); + return nullptr; + } + } + + task_result_iterator_++; + native_task_result_iterator_++; + + return (*task_result_iterator_)->product(); + } +} + +/// Gets the representation of the current geometrical entity. +IfcGeom::Element* IfcGeom::Iterator::get() +{ + if (!initialization_outcome_) { + throw std::runtime_error("Iterator not initialized"); + } + + auto ret = *task_result_iterator_; + + // If we want to organize the element considering their hierarchy + if (settings_.get().get()) { + // We are going to build a vector with the element parents. + // First, create the parent vector + std::vector parents; + + // if the element has a parent + if (ret->parent_id() != -1) { + const IfcGeom::Element* parent_object = NULL; + bool hasParent = true; + + // get the parent + try { + parent_object = get_object(ret->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + // We need to find all the parents + while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) { + // Find the next parent + try { + parent_object = get_object(parent_object->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + hasParent = hasParent && parent_object->parent_id() != -1; + } + + // when done push the parent list in the Element object + ret->SetParents(parents); + } + } + + return ret; +} + +const IfcGeom::Element* IfcGeom::Iterator::get_object(int id) { + ifcopenshell::geometry::taxonomy::matrix4::ptr m4; + int parent_id = -1; + std::string instance_type, product_name, product_guid; + IfcUtil::IfcBaseEntity* ifc_product = 0; + + try { + ifc_product = ifc_file->instance_by_id(id)->as(); + instance_type = ifc_product->declaration().name(); + + if (ifc_product->declaration().is("IfcRoot")) { + product_guid = (std::string)ifc_product->get("GlobalId"); + product_name = ifc_product->get_value("Name", ""); + } + + auto parent_object = converter_->mapping()->get_decomposing_entity(ifc_product); + if (parent_object) { + parent_id = parent_object->id(); + } + + // fails in case of IfcProject + auto mapped = converter_->mapping()->map(ifc_product); + auto casted = mapped ? ifcopenshell::geometry::taxonomy::dcast(mapped) : nullptr; + + if (casted) { + m4 = casted->matrix; + } + } 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 (...) { + Logger::Error("Unknown error returning product"); + } + + Element* ifc_object = new Element(settings_, id, parent_id, product_name, instance_type, product_guid, "", m4, ifc_product); + return ifc_object; +} + +const IfcUtil::IfcBaseClass* IfcGeom::Iterator::create() { + const IfcUtil::IfcBaseClass* product = nullptr; + try { + product = create_shape_model_for_next_entity(); + } 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 (...) { + Logger::Error("Unknown error creating geometry"); + had_error_processing_elements_ = true; + } + return product; +} + +IfcGeom::Iterator::~Iterator() { + if (num_threads_ != 1) { + terminating_ = true; + + if (init_future_.valid()) { + init_future_.wait(); + } + } + + for (auto& k : kernel_pool) { + delete k; + } + + if (task_result_ptr_initialized) { + while (task_result_iterator_ != --all_processed_elements_.end()) { + if (*native_task_result_iterator_ != *task_result_iterator_) { + delete* native_task_result_iterator_; + } + delete* task_result_iterator_++; + native_task_result_iterator_++; + } + } + + delete converter_; +} diff --git a/src/ifcgeom/Iterator.h b/src/ifcgeom/Iterator.h index 45f850acfd..efabdf37bd 100644 --- a/src/ifcgeom/Iterator.h +++ b/src/ifcgeom/Iterator.h @@ -123,7 +123,6 @@ namespace IfcGeom { std::mutex element_ready_mutex_; bool task_result_ptr_initialized = false; - // ? size_t async_elements_returned_ = 0; size_t task_result_index_ = 0; @@ -155,357 +154,13 @@ namespace IfcGeom { // Should not be destructed because, destructor is blocking std::future init_future_; + std::mutex caching_mutex_; std::array time_points; - /// @todo public/private sections all over the place: move all public to the beginning of the class - public: - void set_cache(GeometrySerializer* cache) { cache_ = cache; } - - const std::string& unit_name() const { return converter_->mapping()->get_length_unit_name(); } - double unit_magnitude() const { return converter_->mapping()->get_length_unit(); } - // Check if error occurred during iterator initialization or iteration over elements. - bool had_error_processing_elements() const { return had_error_processing_elements_; } - - boost::optional initialization_outcome_; - - /** - * @return Returns true if the iterator is initialized with any elements, false otherwise. - * - * @note - * - A true return value does not guarantee successful initialization of all elements. - * Some elements may have failed to initialize. Check had_error_processing_elements() - * to see whether there were errors during the initialization. - * - * - For non-concurrent iterators, a false return may occur if initialization of the first - * element fails, even if subsequent elements could be initialized successfully. - */ - bool initialize() { - using std::chrono::high_resolution_clock; - - if (initialization_outcome_) { - return *initialization_outcome_; - } - - time_points[0] = high_resolution_clock::now(); - std::vector reps; - if (num_threads_ != 1) { - // @todo this shouldn't be necessary with properly immutable taxonomy items - converter_->mapping()->use_caching() = false; - } - try { - converter_->mapping()->get_representations(reps, filters_); - } catch (const std::exception& e) { - Logger::Error(e); - } - time_points[1] = high_resolution_clock::now(); - - for (auto& task : reps) { - geometry_conversion_result res; - res.index = task.index; - if (!settings_.get().get()) { - res.representation = task.representation; - res.products_2 = task.products; - } else { - res.item = converter_->mapping()->map(task.representation); - if (!res.item) { - continue; - } - std::transform(task.products->begin(), task.products->end(), std::back_inserter(res.products), [this, &res](IfcUtil::IfcBaseClass* prod) { - auto prod_item = converter_->mapping()->map(prod); - return std::make_pair(prod->as(), ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix); - }); - } - tasks_.push_back(res); - } - - if (settings_.get().get() && settings_.get().get()) { - std::unordered_map< - ifcopenshell::geometry::taxonomy::item::ptr, - std::vector>> folded; - - for (auto& r : tasks_) { - auto i = r.item; - - Eigen::Matrix4d m4 = Eigen::Matrix4d::Identity(); - - while (auto col = std::dynamic_pointer_cast(i)) { - if (col->children.size() == 1) { - if (col->matrix) { - m4 *= col->matrix->ccomponents(); - } - i = col->children[0]; - } else { - break; - } - } - - for (auto& p : r.products) { - auto pl = ifcopenshell::geometry::taxonomy::matrix4::ptr(p.second->clone_()); - pl->components() *= m4; - folded[i].push_back( - { p.first, pl } - ); - } - } - - if (folded.size() < tasks_.size()) { - auto old_size = tasks_.size(); - tasks_.clear(); - size_t i = 0; - for (auto& p : folded) { - tasks_.emplace_back(); - tasks_.back().index = i++; - tasks_.back().item = p.first; - tasks_.back().products = p.second; - } - Logger::Notice("Merged " + std::to_string(old_size) + " tasks into " + std::to_string(tasks_.size()) + " tasks due to permissive shape reuse"); - } - } - - size_t num_products = 0; - for (auto& r : tasks_) { - num_products += !settings_.get().get() ? r.products_2->size() : r.products.size(); - } - - time_points[2] = high_resolution_clock::now(); - - /* - // What to do, map representation and product individually? - // There needs to be two options, mapped item respecting (does that still work?), and optimized based on topology sorting. - // Or is the sorting not necessary if we just cache? - - std::vector items; - std::map placements; - std::transform(products.begin(), products.end(), std::back_inserter(items), [this, &placements](IfcUtil::IfcBaseClass* p) { - auto item = converter_->mapping()->map(p); - // Product placements do not affect item reuse and should temporarily be swapped to identity - if (item) { - std::swap(placements[item], ((taxonomy::geom_ptr)item)->matrix); - } - return item; - }); - items.erase(std::remove(items.begin(), items.end(), nullptr), items.end()); - std::sort(items.begin(), items.end(), taxonomy::less); - auto it = items.begin(); - while (it < items.end()) { - auto jt = std::upper_bound(it, items.end(), *it, taxonomy::less); - geometry_conversion_result r; - r.item = *it; - std::transform(it, jt, std::back_inserter(r.products), [&r, &placements](taxonomy::ptr product_node) { - return std::make_pair((IfcUtil::IfcBaseEntity*) product_node->instance, placements[product_node]); - }); - tasks_.push_back(r); - it = jt; - } - */ - - Logger::Notice("Created " + boost::lexical_cast(tasks_.size()) + " tasks for " + boost::lexical_cast(num_products) + " products"); - - if (tasks_.size() == 0) { - Logger::Warning("No representations encountered, aborting"); - initialization_outcome_.reset(false); - } else { - - task_iterator_ = tasks_.begin(); - - task_result_index_ = 0; - done = 0; - total = (int) tasks_.size(); - - if (num_threads_ != 1) { - init_future_ = std::async(std::launch::async, [this]() { process_concurrently(); }); - - // wait for the first element, because after init(), get() can be called. - // so the element conversion must succeed - initialization_outcome_ = wait_for_element(); - } else { - initialization_outcome_ = create(); - } - } - - return *initialization_outcome_; - } - - size_t processed_ = 0; - - void process_finished_rep(geometry_conversion_result* rep) { - if (rep->elements.empty()) { - return; - } - - std::lock_guard lk(element_ready_mutex_); - - all_processed_elements_.insert(all_processed_elements_.end(), rep->elements.begin(), rep->elements.end()); - all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep->breps.begin(), rep->breps.end()); - - if (!task_result_ptr_initialized) { - task_result_iterator_ = all_processed_elements_.begin(); - native_task_result_iterator_ = all_processed_native_elements_.begin(); - task_result_ptr_initialized = true; - } - - progress_ = (int) (++processed_ * 100 / tasks_.size()); - } - - void process_concurrently() { - size_t conc_threads = num_threads_; - if (conc_threads > tasks_.size()) { - conc_threads = tasks_.size(); - } - - kernel_pool.reserve(conc_threads); - for (unsigned i = 0; i < conc_threads; ++i) { - kernel_pool.push_back(new ifcopenshell::geometry::Converter(geometry_library_, ifc_file, settings_)); - } - - std::vector> threadpool; - - for (auto& rep : tasks_) { - ifcopenshell::geometry::Converter* K = nullptr; - if (threadpool.size() < kernel_pool.size()) { - K = kernel_pool[threadpool.size()]; - } - - while (threadpool.size() == conc_threads) { - for (int i = 0; i < (int)threadpool.size(); i++) { - auto& fu = threadpool[i]; - std::future_status status; - status = fu.wait_for(std::chrono::seconds(0)); - if (status == std::future_status::ready) { - process_finished_rep(fu.get()); - - std::swap(threadpool[i], threadpool.back()); - threadpool.pop_back(); - std::swap(kernel_pool[i], kernel_pool.back()); - K = kernel_pool.back(); - break; - } // if - } // for - } // while - - std::future fu = std::async( - std::launch::async, [this]( - ifcopenshell::geometry::Converter* kernel, - ifcopenshell::geometry::Settings settings, - geometry_conversion_result* rep) { - // Catch exceptions to be safe from freezing the iterator. - try { - this->create_element_(kernel, settings, rep); - } catch (const std::exception& e) { - Logger::Error( - std::string("Exception '") + e.what() + - std::string("' occurred while iterator was creating a shape: "), - rep->item->instance - ); - had_error_processing_elements_ = true; - } catch (...) { - Logger::Error( - "Unknown exception occurred while iteartor was creating a shape: ", - rep->item->instance - ); - had_error_processing_elements_ = true; - } - return rep; - }, - K, - std::ref(settings_), - &rep); - - if (terminating_) { - break; - } - - threadpool.emplace_back(std::move(fu)); - } - - for (auto& fu : threadpool) { - process_finished_rep(fu.get()); - } - - finished_ = true; - - Logger::SetProduct(boost::none); - - if (!terminating_) { - Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + - " objects) "); - } - } - - /// Computes model's bounding box (bounds_min and bounds_max). - /// @note Can take several minutes for large files. - void compute_bounds(bool with_geometry) - { - for (int i = 0; i < 3; ++i) { - bounds_min_.components()(i) = std::numeric_limits::infinity(); - bounds_max_.components()(i) = -std::numeric_limits::infinity(); - } - - if (with_geometry) { - size_t num_created = 0; - do { - IfcGeom::Element* geom_object = get(); - const IfcGeom::TriangulationElement* o = static_cast(geom_object); - const IfcGeom::Representation::Triangulation& mesh = o->geometry(); - auto mat = o->transformation().data()->ccomponents(); - Eigen::Vector4d vec, transformed; - - for (typename std::vector::const_iterator it = mesh.verts().begin(); it != mesh.verts().end();) { - const double& x = *(it++); - const double& y = *(it++); - const double& z = *(it++); - vec << x, y, z, 1.; - transformed = mat * vec; - - for (int i = 0; i < 3; ++i) { - bounds_min_.components()(i) = std::min(bounds_min_.components()(i), transformed(i)); - bounds_max_.components()(i) = std::max(bounds_max_.components()(i), transformed(i)); - } - } - } while (++num_created, next()); - } else { - std::vector reps; - converter_->mapping()->get_representations(reps, filters_); - - std::vector products; - for (auto& r : reps) { - std::copy(r.products->begin(), r.products->end(), std::back_inserter(products)); - } - - for (auto& product : products) { - auto prod_item = converter_->mapping()->map(product); - auto vec = ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix->translation_part(); - - for (int i = 0; i < 3; ++i) { - bounds_min_.components()(i) = std::min(bounds_min_.components()(i), vec(i)); - bounds_max_.components()(i) = std::max(bounds_max_.components()(i), vec(i)); - } - } - } - } - - int progress() const { - return progress_; - } - - std::string getLog() const { return Logger::GetLog(); } - - IfcParse::IfcFile* file() const { return ifc_file; } - - const std::vector& filters() const { return filters_; } - std::vector& filters() { return filters_; } - - const ifcopenshell::geometry::taxonomy::point3& bounds_min() const { return bounds_min_; } - const ifcopenshell::geometry::taxonomy::point3& bounds_max() const { return bounds_max_; } - - private: - - std::mutex caching_mutex_; - template Element* decorate_with_cache_(GeometrySerializer::read_type rt, const std::string& product_guid, const std::string& representation_id, Fn f) { - + bool read_from_cache = false; Element* element = nullptr; @@ -529,357 +184,34 @@ namespace IfcGeom { std::lock_guard lk(caching_mutex_); if (rt == GeometrySerializer::READ_TRIANGULATION) { - cache_->write((IfcGeom::TriangulationElement*) element); + cache_->write((IfcGeom::TriangulationElement*)element); } else { cache_->write((IfcGeom::BRepElement*)element); - } + } } #endif return element; } - const IfcUtil::IfcBaseClass* create_shape_model_for_next_entity() { - geometry_conversion_result* task = nullptr; - for (; task_iterator_ < tasks_.end();) { - task = &*task_iterator_++; - create_element_(converter_, settings_, task); - if (task->elements.empty()) { - task = nullptr; - } else { - break; - } - } - if (task) { - process_finished_rep(task); - return task->item->instance->as(); - } else { - return nullptr; - } - } + const IfcUtil::IfcBaseClass* create_shape_model_for_next_entity(); void create_element_( ifcopenshell::geometry::Converter* kernel, ifcopenshell::geometry::Settings settings, - geometry_conversion_result* rep) - { - if (!settings_.get().get()) { - rep->item = kernel->mapping()->map(rep->representation); - if (!rep->item) { - return; - } - std::transform(rep->products_2->begin(), rep->products_2->end(), std::back_inserter(rep->products), [this, &rep, kernel](IfcUtil::IfcBaseClass* prod) { - auto prod_item = kernel->mapping()->map(prod); - return std::make_pair(prod->as(), ifcopenshell::geometry::taxonomy::cast(prod_item)->matrix); - }); - } else { - } - - auto product_node = rep->products.front(); - const IfcUtil::IfcBaseEntity* product = product_node.first; - const auto& place = product_node.second; - - Logger::SetProduct(product); - - IfcGeom::BRepElement* brep = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product, place, rep]() { - return kernel->create_brep_for_representation_and_product(rep->item, product, place); - })); - - if (!brep) { - return; - } - - auto elem = process_based_on_settings(settings, brep); - if (!elem) { - return; - } - - rep->breps = { brep }; - rep->elements = { elem }; - - for (auto it = rep->products.begin() + 1; it != rep->products.end(); ++it) { - const auto& p = *it; - const IfcUtil::IfcBaseEntity* product2 = p.first; - const auto& place2 = p.second; - - IfcGeom::BRepElement* brep2 = static_cast(decorate_with_cache_(GeometrySerializer::READ_BREP, (std::string)product2->get("GlobalId"), std::to_string(rep->item->instance->as()->id()), [kernel, settings, product2, place2, brep]() { - return kernel->create_brep_for_processed_representation(product2, place2, brep); - })); - if (brep2) { - auto elem2 = process_based_on_settings(settings, brep2, dynamic_cast(elem)); - if (elem2) { - rep->breps.push_back(brep2); - rep->elements.push_back(elem2); - } - } - } - } + geometry_conversion_result* rep); IfcGeom::Element* process_based_on_settings( ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, - IfcGeom::TriangulationElement* previous = nullptr) - { - if (settings.get().get() == ifcopenshell::geometry::settings::SERIALIZED) { - try { - return new IfcGeom::SerializedElement(*elem); - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); - return nullptr; - } - } else if (settings.get().get() == ifcopenshell::geometry::settings::TRIANGULATED) { - // the part before the hyphen is the representation id - auto gid2 = elem->geometry().id(); - auto hyphen = gid2.find("-"); - if (hyphen != std::string::npos) { - gid2 = gid2.substr(0, hyphen); - } + IfcGeom::TriangulationElement* previous = nullptr); - return decorate_with_cache_(GeometrySerializer::READ_TRIANGULATION, elem->guid(), gid2, [elem, previous]() { - try { - if (!previous) { - return new TriangulationElement(*elem); - } else { - return new TriangulationElement(*elem, previous->geometry_pointer()); - } - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); - } - return (TriangulationElement*)nullptr; - }); - } else { - return elem; - } - } + bool wait_for_element(); - bool wait_for_element() { - while (true) { - size_t s; - { - std::lock_guard lk(element_ready_mutex_); - s = all_processed_elements_.size(); - } - if (s > async_elements_returned_) { - ++async_elements_returned_; - return true; - } else if (finished_) { - return false; - } else { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - } - - void log_timepoints() const { - using std::chrono::high_resolution_clock; - using std::chrono::duration; - using namespace std::string_literals; - - std::array labels = { - "Initializing mapping"s, - "Performing mapping"s, - "Geometry interpretation"s - }; - - for (auto it = time_points.begin() + 1; it != time_points.end(); ++it) { - auto jt = it - 1; - duration ms_double = (*it) - (*jt); - Logger::Notice(labels[std::distance(time_points.begin(), jt)] + " took " + std::to_string(ms_double.count()) + "ms"); - } - } + void log_timepoints() const; + /// @todo public/private sections all over the place: move all public to the beginning of the class public: - /// Returns what would be the product for the next shape representation - /// @todo Double-check and test the impl. - //IfcSchema::IfcProduct* peek_next() const - //{ - // if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){ - // return *(ifcproduct_iterator + 1); - // } else { - // return 0; - // } - //} - - /// @todo Would this be as simple as the following code? - //void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } } - - /// Moves to the next shape representation, create its geometry, and returns the associated product. - /// Use get() to retrieve the created geometry. - const IfcUtil::IfcBaseClass* next() { - using std::chrono::high_resolution_clock; - - if (*native_task_result_iterator_ != *task_result_iterator_) { - delete* native_task_result_iterator_; - } - delete *task_result_iterator_; - - if (num_threads_ != 1) { - if (!wait_for_element()) { - Logger::SetProduct(boost::none); - time_points[3] = high_resolution_clock::now(); - log_timepoints(); - return nullptr; - } - - task_result_iterator_++; - native_task_result_iterator_++; - - return (*task_result_iterator_)->product(); - } else { - // Increment the iterator over the list of products using the current - // shape representation - if (task_result_iterator_ == --all_processed_elements_.end()) { - if (!create()) { - Logger::SetProduct(boost::none); - time_points[3] = high_resolution_clock::now(); - log_timepoints(); - return nullptr; - } - } - - task_result_iterator_++; - native_task_result_iterator_++; - - return (*task_result_iterator_)->product(); - } - } - - /// Gets the representation of the current geometrical entity. - Element* get() - { - if (!initialization_outcome_) { - throw std::runtime_error("Iterator not initialized"); - } - - auto ret = *task_result_iterator_; - - // If we want to organize the element considering their hierarchy - if (settings_.get().get()) - { - // We are going to build a vector with the element parents. - // First, create the parent vector - std::vector parents; - - // if the element has a parent - if (ret->parent_id() != -1) - { - const IfcGeom::Element* parent_object = NULL; - bool hasParent = true; - - // get the parent - try { - parent_object = get_object(ret->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - // We need to find all the parents - while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) - { - // Find the next parent - try { - parent_object = get_object(parent_object->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - hasParent = hasParent && parent_object->parent_id() != -1; - } - - // when done push the parent list in the Element object - ret->SetParents(parents); - } - } - - return ret; - } - - /// Gets the native (Open Cascade or CGAL) representation of the current geometrical entity. - BRepElement* get_native() - { - return *native_task_result_iterator_; - } - - const Element* get_object(int id) { - ifcopenshell::geometry::taxonomy::matrix4::ptr m4; - int parent_id = -1; - std::string instance_type, product_name, product_guid; - IfcUtil::IfcBaseEntity* ifc_product = 0; - - try { - ifc_product = ifc_file->instance_by_id(id)->as(); - instance_type = ifc_product->declaration().name(); - - if (ifc_product->declaration().is("IfcRoot")) { - product_guid = (std::string) ifc_product->get("GlobalId"); - product_name = ifc_product->get_value("Name", ""); - } - - auto parent_object = converter_->mapping()->get_decomposing_entity(ifc_product); - if (parent_object) { - parent_id = parent_object->id(); - } - - // fails in case of IfcProject - auto mapped = converter_->mapping()->map(ifc_product); - auto casted = mapped ? ifcopenshell::geometry::taxonomy::dcast(mapped) : nullptr; - - if (casted) { - m4 = casted->matrix; - } - } 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 (...) { - Logger::Error("Unknown error returning product"); - } - - Element* ifc_object = new Element(settings_, id, parent_id, product_name, instance_type, product_guid, "", m4, ifc_product); - return ifc_object; - } - - const IfcUtil::IfcBaseClass* create() { - const IfcUtil::IfcBaseClass* product = nullptr; - try { - product = create_shape_model_for_next_entity(); - } 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 (...) { - Logger::Error("Unknown error creating geometry"); - had_error_processing_elements_ = true; - } - return product; - } - Iterator(const std::string& geometry_library, const ifcopenshell::geometry::Settings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) : settings_(settings) , ifc_file(file) @@ -927,31 +259,70 @@ namespace IfcGeom { { } - ~Iterator() { - if (num_threads_ != 1) { - terminating_ = true; + ~Iterator(); - if (init_future_.valid()) { - init_future_.wait(); - } - } - - for (auto& k : kernel_pool) { - delete k; - } + void set_cache(GeometrySerializer* cache) { cache_ = cache; } - if (task_result_ptr_initialized) { - while (task_result_iterator_ != --all_processed_elements_.end()) { - if (*native_task_result_iterator_ != *task_result_iterator_) { - delete* native_task_result_iterator_; - } - delete *task_result_iterator_++; - native_task_result_iterator_++; - } - } + const std::string& unit_name() const { return converter_->mapping()->get_length_unit_name(); } + double unit_magnitude() const { return converter_->mapping()->get_length_unit(); } + // Check if error occurred during iterator initialization or iteration over elements. + bool had_error_processing_elements() const { return had_error_processing_elements_; } - delete converter_; + boost::optional initialization_outcome_; + + /** + * @return Returns true if the iterator is initialized with any elements, false otherwise. + * + * @note + * - A true return value does not guarantee successful initialization of all elements. + * Some elements may have failed to initialize. Check had_error_processing_elements() + * to see whether there were errors during the initialization. + * + * - For non-concurrent iterators, a false return may occur if initialization of the first + * element fails, even if subsequent elements could be initialized successfully. + */ + bool initialize(); + + size_t processed_ = 0; + + void process_finished_rep(geometry_conversion_result* rep); + + void process_concurrently(); + + /// Computes model's bounding box (bounds_min and bounds_max). + /// @note Can take several minutes for large files. + void compute_bounds(bool with_geometry); + + int progress() const { + return progress_; } + + std::string getLog() const { return Logger::GetLog(); } + + IfcParse::IfcFile* file() const { return ifc_file; } + + const std::vector& filters() const { return filters_; } + std::vector& filters() { return filters_; } + + const ifcopenshell::geometry::taxonomy::point3& bounds_min() const { return bounds_min_; } + const ifcopenshell::geometry::taxonomy::point3& bounds_max() const { return bounds_max_; } + + /// Moves to the next shape representation, create its geometry, and returns the associated product. + /// Use get() to retrieve the created geometry. + const IfcUtil::IfcBaseClass* next(); + + /// Gets the representation of the current geometrical entity. + Element* get(); + + /// Gets the native (Open Cascade or CGAL) representation of the current geometrical entity. + BRepElement* get_native() + { + return *native_task_result_iterator_; + } + + const Element* get_object(int id); + + const IfcUtil::IfcBaseClass* create(); }; } diff --git a/src/svgfill b/src/svgfill index 0ebff78309..37448e9ba5 160000 --- a/src/svgfill +++ b/src/svgfill @@ -1 +1 @@ -Subproject commit 0ebff783092ba479e127285698835c503ce356b9 +Subproject commit 37448e9ba590d0f1046471b84f652e3bbc5789c2