diff --git a/src/bonsai/bonsai/bim/module/boundary/operator.py b/src/bonsai/bonsai/bim/module/boundary/operator.py index be93e5e41d..89b4bf6e72 100644 --- a/src/bonsai/bonsai/bim/module/boundary/operator.py +++ b/src/bonsai/bonsai/bim/module/boundary/operator.py @@ -40,6 +40,7 @@ from mathutils import Vector, Matrix from bonsai.bim.ifc import IfcStore from bonsai.bim.module.model.decorator import ProfileDecorator from bonsai.bim.module.boundary.decorator import BoundaryDecorator +from ifcopenshell.util.shape_builder import ShapeBuilder import bonsai.core import bonsai.core.geometry from typing import Union, Optional @@ -1058,15 +1059,11 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) tool.Model.unit_scale = self.unit_scale + builder = ShapeBuilder(tool.Ifc.get()) surface = tool.Ifc.get().createIfcCurveBoundedPlane() - surface.BasisSurface = tool.Ifc.get().createIfcPlane( - tool.Ifc.get().createIfcAxis2Placement3D( - tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]), - tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]), - tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]), - ) - ) + placement = builder.create_axis2_placement_3d([o / self.unit_scale for o in p1], z_axis, x_axis) + surface.BasisSurface = tool.Ifc.get().create_entity("IfcPlane", placement) if tool.Ifc.get().schema != "IFC2X3": points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords] diff --git a/src/bonsai/bonsai/bim/module/geometry/helper.py b/src/bonsai/bonsai/bim/module/geometry/helper.py index 38ca24d15d..7f70df4b96 100644 --- a/src/bonsai/bonsai/bim/module/geometry/helper.py +++ b/src/bonsai/bonsai/bim/module/geometry/helper.py @@ -28,6 +28,7 @@ import bonsai.tool as tool from math import pi, pow from mathutils import Vector, Matrix, geometry from typing import Union, Any, TypeVar, Optional +from ifcopenshell.util.shape_builder import ShapeBuilder T = TypeVar("T") @@ -35,6 +36,7 @@ T = TypeVar("T") class Helper: def __init__(self, file: ifcopenshell.file): self.file = file + self.builder = ShapeBuilder(file) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) # We can detect a rectangular extrusion by picking any face, then find an @@ -340,8 +342,10 @@ class Helper: def create_extruded_area_solid( self, mesh: bpy.types.Mesh, extrusion_indices: list[int], profile_def: dict[str, Any] ) -> ifcopenshell.entity_instance: - position = self.create_ifc_axis_2_placement_3d( - profile_def["curve_ucs"]["center"], profile_def["curve_ucs"]["z_axis"], profile_def["curve_ucs"]["x_axis"] + position = self.builder.create_axis2_placement_3d( + self.convert_si_to_unit(profile_def["curve_ucs"]["center"]), + profile_def["curve_ucs"]["z_axis"], + profile_def["curve_ucs"]["x_axis"], ) direction = self.get_extrusion_direction(mesh, extrusion_indices, profile_def["curve_ucs"]) unit_direction = direction.normalized() @@ -505,12 +509,3 @@ class Helper: return self.file.createIfcAxis2Placement2D( self.create_cartesian_point(point.x, point.y), self.file.createIfcDirection((forward.x, forward.y)) ) - - def create_ifc_axis_2_placement_3d( - self, point: Vector, up: Vector, forward: Vector - ) -> ifcopenshell.entity_instance: - return self.file.createIfcAxis2Placement3D( - self.create_cartesian_point(point.x, point.y, point.z), - self.file.createIfcDirection((up.x, up.y, up.z)), - self.file.createIfcDirection((forward.x, forward.y, forward.z)), - ) diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 3bd0a700ba..f308810ee1 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -58,6 +58,7 @@ from fractions import Fraction from typing import Optional, Union, Any, Literal, TYPE_CHECKING, NamedTuple from collections.abc import Iterable, Sequence from pathlib import Path +from ifcopenshell.util.shape_builder import ShapeBuilder if TYPE_CHECKING: from bonsai.bim.module.drawing.prop import ( @@ -778,11 +779,8 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def add_literal(cls, **attributes: str) -> ifcopenshell.entity_instance: ifc_file = tool.Ifc.get() - origin = ifc_file.createIfcAxis2Placement3D( - ifc_file.createIfcCartesianPoint((0.0, 0.0, 0.0)), - ifc_file.createIfcDirection((0.0, 0.0, 1.0)), - ifc_file.createIfcDirection((1.0, 0.0, 0.0)), - ) + builder = ShapeBuilder(ifc_file) + origin = builder.create_axis2_placement_3d() ifc_literal = ifc_file.create_entity( "IfcTextLiteralWithExtent", attributes.get("Literal", "Literal"), diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 40ac1ef677..7da6f83e1a 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -238,6 +238,8 @@ class Model(bonsai.core.tool.Model): @classmethod def export_surface(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]: + ifc_file = tool.Ifc.get() + builder = ShapeBuilder(ifc_file) p1, p2, p3 = [v.co.copy() for v in obj.data.vertices[0:3]] edge1 = p2 - p1 @@ -271,13 +273,8 @@ class Model(bonsai.core.tool.Model): cls.bm.edges.ensure_lookup_table() surface = tool.Ifc.get().createIfcCurveBoundedPlane() - surface.BasisSurface = tool.Ifc.get().createIfcPlane( - tool.Ifc.get().createIfcAxis2Placement3D( - tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]), - tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]), - tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]), - ) - ) + placement = builder.create_axis2_placement_3d([o / cls.unit_scale for o in p1], z_axis, x_axis) + surface.BasisSurface = ifc_file.create_entity("IfcPlane", placement) surface.OuterBoundary = tool.Ifc.get().add(profile_def.OuterCurve) if profile_def.is_a("IfcArbitraryProfileDefWithVoids"): @@ -2124,13 +2121,8 @@ class Model(bonsai.core.tool.Model): @classmethod def add_extrusion_position(cls, extrusion: ifcopenshell.entity_instance, position: Vector) -> None: ifc_file = tool.Ifc.get() - - new_position = ifc_file.createIfcAxis2Placement3D( - ifc_file.createIfcCartesianPoint(position), - ifc_file.createIfcDirection((0.0, 0.0, 1.0)), - ifc_file.createIfcDirection((1.0, 0.0, 0.0)), - ) - + builder = ShapeBuilder(ifc_file) + new_position = builder.create_axis2_placement_3d(position) extrusion.Position = new_position @classmethod diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py index 159127c03e..2e37e99e26 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py @@ -22,6 +22,7 @@ import ifcopenshell.api.owner import ifcopenshell.util.unit import ifcopenshell.util.element import ifcopenshell.util.placement +from ifcopenshell.util.shape_builder import ShapeBuilder from typing import Optional, Union, Any NPArrayOfFloats = npt.NDArray[np.float64] @@ -74,6 +75,7 @@ class Usecase: if not hasattr(self.settings["product"], "ObjectPlacement"): return self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) + self.builder = ShapeBuilder(self.file) if not self.settings["is_si"]: self.convert_matrix_to_si(self.settings["matrix"]) @@ -183,25 +185,12 @@ class Usecase: o = np.array((m[0][3], m[1][3], m[2][3])) object_matrix = ifcopenshell.util.placement.a2p(o, z, x) relative_placement_matrix = np.linalg.inv(relating_object_matrix) @ object_matrix - return self.create_ifc_axis_2_placement_3d( - relative_placement_matrix[:, 3][0:3], + return self.builder.create_axis2_placement_3d( + self.convert_si_to_unit(relative_placement_matrix[:, 3][0:3]), relative_placement_matrix[:, 2][0:3], relative_placement_matrix[:, 0][0:3], ) - def create_ifc_axis_2_placement_3d( - self, point: NPArrayOfFloats, up: NPArrayOfFloats, forward: NPArrayOfFloats - ) -> ifcopenshell.entity_instance: - return self.file.createIfcAxis2Placement3D( - self.create_cartesian_point(point), - self.file.createIfcDirection(up.tolist()), - self.file.createIfcDirection(forward.tolist()), - ) - - def create_cartesian_point(self, co: NPArrayOfFloats) -> ifcopenshell.entity_instance: - co = self.convert_si_to_unit(co) - return self.file.createIfcCartesianPoint(co.tolist()) - def convert_si_to_unit(self, co: NPArrayOfFloats) -> NPArrayOfFloats: return co / self.unit_scale diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py index f453d2e94a..25fb4f3b6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_wcs.py @@ -18,9 +18,9 @@ import ifcopenshell import ifcopenshell.util.element -import ifcopenshell.util.geolocation import ifcopenshell.util.unit import numpy as np +from ifcopenshell.util.shape_builder import ShapeBuilder from math import sin, cos, radians @@ -62,6 +62,7 @@ def edit_wcs( ifcopenshell.api.georeference.edit_wcs(model) """ unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) + builder = ShapeBuilder(file) if np.isclose(rotation, 0): xaxis_x = 1.0 xaxis_y = 0.0 @@ -74,14 +75,10 @@ def edit_wcs( old_wcs = context.WorldCoordinateSystem if context.CoordinateSpaceDimension == 3: if is_si: - point = file.createIfcCartesianPoint((x / unit_scale, y / unit_scale, z / unit_scale)) + xyz = (x / unit_scale, y / unit_scale, z / unit_scale) else: - point = file.createIfcCartesianPoint((x, y, z)) - placement = file.createIfcAxis2Placement3D( - point, - file.createIfcDirection((0.0, 0.0, 1.0)), - file.createIfcDirection((xaxis_x, xaxis_y, 0.0)), - ) + xyz = (x, y, z) + placement = builder.create_axis2_placement_3d(xyz, (0.0, 0.0, 1.0), (xaxis_x, xaxis_y, 0.0)) elif context.CoordinateSpaceDimension == 2: if is_si: point = file.createIfcCartesianPoint((x / unit_scale, y / unit_scale)) diff --git a/src/ifcopenshell-python/ifcopenshell/util/data.py b/src/ifcopenshell-python/ifcopenshell/util/data.py index 75fae18ed5..c65a87ddb0 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/data.py +++ b/src/ifcopenshell-python/ifcopenshell/util/data.py @@ -21,6 +21,7 @@ import numpy as np import ifcopenshell from typing import Any, Union from dataclasses import dataclass +from ifcopenshell.util.shape_builder import ShapeBuilder @dataclass @@ -78,9 +79,7 @@ class Clipping: if not ifc_file: ifc_file = first_operand.file - - location = ifc_file.createIfcCartesianPoint([i / unit_scale for i in self.location]) - direction = ifc_file.createIfcDirection(self.normal) + builder = ShapeBuilder(ifc_file) normal = np.array(self.normal) if np.allclose(normal, np.array([0.0, 0.0, 1.0]), atol=1e-2) or np.allclose( @@ -92,9 +91,9 @@ class Clipping: x_axis = np.cross(normal, arbitrary_vector) x_axis /= np.linalg.norm(x_axis) - x_axis = ifc_file.createIfcDirection(x_axis.tolist()) - plane = ifc_file.createIfcPlane(ifc_file.createIfcAxis2Placement3D(location, direction, x_axis)) + placement = builder.create_axis2_placement_3d([i / unit_scale for i in self.location], self.normal, x_axis) + plane = ifc_file.create_entity("IfcPlane", placement) second_operand = ifc_file.createIfcHalfSpaceSolid(plane, False) return ifc_file.createIfcBooleanClippingResult("DIFFERENCE", first_operand, second_operand) diff --git a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py index 765cef7336..d5cd0e4947 100644 --- a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py +++ b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py @@ -21,6 +21,7 @@ import numpy as np import ifcopenshell import ifcopenshell.util.placement import typing +from ifcopenshell.util.shape_builder import ShapeBuilder class Patcher: @@ -97,6 +98,7 @@ class Patcher: self.ax = None self.ay = None self.az = None + self.builder = ShapeBuilder(file) try: self.ax = float(ax) @@ -183,15 +185,4 @@ class Patcher: z = np.array((m[0][2], m[1][2], m[2][2])) o = np.array((m[0][3], m[1][3], m[2][3])) object_matrix = ifcopenshell.util.placement.a2p(o, z, x) - return self.create_ifc_axis_2_placement_3d( - object_matrix[:, 3][0:3], - object_matrix[:, 2][0:3], - object_matrix[:, 0][0:3], - ) - - def create_ifc_axis_2_placement_3d(self, point, up, forward): - return self.file.createIfcAxis2Placement3D( - self.file.createIfcCartesianPoint(point.tolist()), - self.file.createIfcDirection(up.tolist()), - self.file.createIfcDirection(forward.tolist()), - ) + return self.builder.create_axis2_placement_3d_from_matrix(object_matrix)