From 774a770c4897d17d3b9b22b60e2944f20c22a702 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 16 Feb 2025 08:20:59 +1100 Subject: [PATCH] Purge usage of mathutils in grid API functions The only thing left is add_representation which is Blender specific anyway and is slowly being refactored out. --- .../bonsai/bim/module/geometry/operator.py | 2 +- src/bonsai/bonsai/bim/module/model/grid.py | 4 +- src/bonsai/bonsai/tool/model.py | 8 ++ .../ifcopenshell/api/geometry/__init__.py | 22 ++--- .../ifcopenshell/api/grid/__init__.py | 5 +- .../api/grid/create_axis_curve.py | 80 +++++++------------ src/ifcopenshell-python/pyproject.toml | 3 - 7 files changed, 46 insertions(+), 78 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index f592c0f98d..c07f427851 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -474,7 +474,7 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator): if product.is_a("IfcGridAxis"): # Grid geometry does not follow the "representation" paradigm and needs to be treated specially - ifcopenshell.api.grid.create_axis_curve(self.file, axis_curve=obj, grid_axis=product) + tool.Model.create_axis_curve(obj, product) return elif product.is_a("IfcRelSpaceBoundary"): # TODO refactor diff --git a/src/bonsai/bonsai/bim/module/model/grid.py b/src/bonsai/bonsai/bim/module/model/grid.py index 2d4cdfa501..0411f2875d 100644 --- a/src/bonsai/bonsai/bim/module/model/grid.py +++ b/src/bonsai/bonsai/bim/module/model/grid.py @@ -50,7 +50,7 @@ def add_object(self, context): "grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="UAxes", grid=grid ) tool.Ifc.link(result, obj) - ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result) + tool.Model.create_axis_curve(obj, result) tool.Collector.assign(obj) for i in range(0, self.total_v): @@ -69,7 +69,7 @@ def add_object(self, context): "grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="VAxes", grid=grid ) tool.Ifc.link(result, obj) - ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result) + tool.Model.create_axis_curve(obj, result) tool.Collector.assign(obj) tool.Root.reload_grid_decorator() diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 0956dd6041..c20f2762fd 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -1978,3 +1978,11 @@ class Model(bonsai.core.tool.Model): x_angle = Vector((0, 1)).angle_signed(Vector((y, z))) return x_angle + + @classmethod + def create_axis_curve(cls, obj: bpy.types.Object, grid_axis: ifcopenshell.entity_instance): + m = tool.Surveyor.get_absolute_matrix(obj) + points = [m @ np.array(v.co.to_4d()) for v in obj.data.vertices[0:2]] + ifcopenshell.api.grid.create_axis_curve( + tool.Ifc.get(), p1=points[0], p2=points[1], is_si=True, grid_axis=grid_axis + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py index 8a253eee73..49bfe5f758 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py @@ -26,32 +26,20 @@ geometry extrusions). from .. import wrap_usecases from .add_axis_representation import add_axis_representation from .add_boolean import add_boolean - -try: - from .add_door_representation import add_door_representation -except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: geometry.add_door_representation - {e}") +from .add_door_representation import add_door_representation from .add_footprint_representation import add_footprint_representation from .add_mesh_representation import add_mesh_representation from .add_profile_representation import add_profile_representation - -try: - from .add_railing_representation import add_railing_representation -except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: geometry.add_railing_representation - {e}") +from .add_railing_representation import add_railing_representation try: from .add_representation import add_representation -except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}") +except ModuleNotFoundError: + pass # Silently fail. This is Blender / Bonsai specific and on its way out. from .add_shape_aspect import add_shape_aspect from .add_slab_representation import add_slab_representation from .add_wall_representation import add_wall_representation - -try: - from .add_window_representation import add_window_representation -except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: geometry.add_window_representation - {e}") +from .add_window_representation import add_window_representation from .assign_representation import assign_representation from .connect_element import connect_element from .connect_path import connect_path diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py index 5cd7199e63..cc759ce7e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py @@ -23,10 +23,7 @@ A grid in IFC may contain two or more axes running in two or more directions. from .. import wrap_usecases -try: - from .create_axis_curve import create_axis_curve -except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: grid.create_axis_curve - {e}") +from .create_axis_curve import create_axis_curve from .create_grid_axis import create_grid_axis from .remove_grid_axis import remove_grid_axis diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py index 7e3840753b..d65a96ee54 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -21,27 +21,26 @@ import ifcopenshell import ifcopenshell.util.element import ifcopenshell.util.unit import ifcopenshell.util.placement -from mathutils import Matrix # For now, we depend on Blender -import bpy.types +import numpy as np def create_axis_curve( - file: ifcopenshell.file, axis_curve: bpy.types.Object, grid_axis: ifcopenshell.entity_instance + file: ifcopenshell.file, + *, + p1: np.ndarray, + p2: np.ndarray, + grid_axis: ifcopenshell.entity_instance, + is_si: bool = True, ) -> None: """Adds curve geometry to a grid axis to represent the axis extents - This currently depends on the Blender geometry kernel to function. - An IFC grid will have a minimum of two axes (typically perpendicular). Each axis will then have a line which represents the extents of the axis. - :param axis_curve: The Blender object that contains a mesh data block with a - single edge. - :type axis_curve: bpy.types.Object + :param p1: The first point of the grid axis + :param p2: The second point of the grid axis :param grid_axis: The IfcGridAxis element to add geometry to. - :type grid_axis: ifcopenshell.entity_instance - :return: None - :rtype: None + :param is_si: If true, the points are in meters, not project units Example: @@ -54,48 +53,27 @@ def create_axis_curve( axis_1 = ifcopenshell.api.grid.create_grid_axis(model, axis_tag="1", uvw_axes="VAxes", grid=grid) - # Assume you have these Blender objects in your active Blender session - obj1 = bpy.data.objects.get("AxisA") - obj2 = bpy.data.objects.get("Axis1") - ifcopenshell.api.grid.create_axis_curve(model, axis_curve=obj1, grid_axis=axis_a) - ifcopenshell.api.grid.create_axis_curve(model, axis_curve=obj2, grid_axis=axis_1) + # By convention, alphabetic grids are horizontal, and numeric are vertical + ifcopenshell.api.grid.create_axis_curve( + model, p1=np.array((0., 0., 0.)), p2=np.array((10., 0., 0.)), grid_axis=axis_a) + ifcopenshell.api.grid.create_axis_curve( + model, p1=np.array((0., 0., 0.)), p2=np.array((0., 10., 0.)), grid_axis=axis_1) """ - usecase = Usecase() - usecase.file = file - usecase.settings = { - "axis_curve": axis_curve, # A Blender object - "grid_axis": grid_axis, - } - return usecase.execute() + existing_curve = grid_axis.AxisCurve + if is_si: + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) + p1 /= unit_scale + p2 /= unit_scale -class Usecase: - def execute(self): - existing_curve = self.settings["grid_axis"].AxisCurve - - self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) - grid = [i for i in self.file.get_inverse(self.settings["grid_axis"]) if i.is_a("IfcGrid")][0] - grid_matrix_i = Matrix(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement)) - grid_matrix_i.translation *= self.settings["unit_scale"] - grid_matrix_i = grid_matrix_i.inverted() - points = [ - grid_matrix_i @ (self.settings["axis_curve"].matrix_world @ v.co) - for v in self.settings["axis_curve"].data.vertices[0:2] - ] - self.settings["grid_axis"].AxisCurve = self.file.createIfcPolyline( - [ - self.create_cartesian_point(points[0][0], points[0][1]), - self.create_cartesian_point(points[1][0], points[1][1]), - ] + grid = [i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")][0] + grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement)) + grid_axis.AxisCurve = file.createIfcPolyline( + ( + file.createIfcCartesianPoint((grid_matrix_i @ p1).tolist()), + file.createIfcCartesianPoint((grid_matrix_i @ p2).tolist()), ) + ) - if existing_curve: - ifcopenshell.util.element.remove_deep2(self.file, existing_curve) - - def create_cartesian_point(self, x, y): - x = self.convert_si_to_unit(x) - y = self.convert_si_to_unit(y) - return self.file.createIfcCartesianPoint((x, y)) - - def convert_si_to_unit(self, co): - return co / self.settings["unit_scale"] + if existing_curve: + ifcopenshell.util.element.remove_deep2(file, existing_curve) diff --git a/src/ifcopenshell-python/pyproject.toml b/src/ifcopenshell-python/pyproject.toml index ce752e4720..02fcf321cc 100644 --- a/src/ifcopenshell-python/pyproject.toml +++ b/src/ifcopenshell-python/pyproject.toml @@ -25,9 +25,6 @@ dependencies = [ ] [project.optional-dependencies] -# mathutils is broken on Python < 3.10. -# See: https://gitlab.com/ideasman42/blender-mathutils/-/merge_requests/3 -geometry = ["mathutils"] dev = ["pytest"] [project.urls]