diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index c55b2c6b37..4d2bfcd19f 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -48,7 +48,7 @@ from bonsai.bim import import_ifc from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData from bonsai.bim.module.model.opening import FilledOpeningGenerator -from ifcopenshell.util.shape_builder import ShapeBuilder +from ifcopenshell.util.shape_builder import ShapeBuilder, np_to_3d from typing import Optional, Union, TypeVar, Any, Iterable, Literal, TYPE_CHECKING, Sequence, TypedDict T = TypeVar("T") @@ -2126,7 +2126,7 @@ class Model(bonsai.core.tool.Model): assert isinstance(obj.data, bpy.types.Mesh) 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 + tool.Ifc.get(), p1=np_to_3d(points[0]), p2=np_to_3d(points[1]), is_si=True, grid_axis=grid_axis ) @classmethod 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 8aedf889ef..b87eea94d0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -22,7 +22,7 @@ import ifcopenshell.util.element import ifcopenshell.util.unit import ifcopenshell.util.placement import numpy as np -from ifcopenshell.util.shape_builder import VectorType, V, ifc_safe_vector_type +from ifcopenshell.util.shape_builder import VectorType, V, ifc_safe_vector_type, np_apply_matrix def create_axis_curve( @@ -38,6 +38,10 @@ def create_axis_curve( 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. + Points are provided as 3D coordinates in world space. + During axis creation, the coordinates will be localized relative to IfcGrid + and saved as 2D. + :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. @@ -61,19 +65,19 @@ def create_axis_curve( model, p1=np.array((0., 0., 0.)), p2=np.array((0., 10., 0.)), grid_axis=axis_1) """ existing_curve = grid_axis.AxisCurve - p1, p2 = V(p1), V(p2) + points = V([p1, p2]) if is_si: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) - p1 /= unit_scale - p2 /= unit_scale + points /= unit_scale - grid = [i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")][0] + grid = next(i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")) grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement)) + p1, p2 = ifc_safe_vector_type(np_apply_matrix(points, grid_matrix_i)) grid_axis.AxisCurve = file.create_entity( "IfcPolyline", ( - file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(grid_matrix_i @ p1)), - file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(grid_matrix_i @ p2)), + file.create_entity("IfcCartesianPoint", p1[:2]), + file.create_entity("IfcCartesianPoint", p2[:2]), ), ) diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py index 47926c7e0a..e352187843 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py @@ -129,6 +129,16 @@ def np_to_4x4(matrix_3x3: np.ndarray) -> np.ndarray: return matrix_4x4 +def np_apply_matrix(vectors: SequenceOfVectors, matrix: npt.NDArray) -> npt.NDArray: + """ + :param vectors: Nx3 array of vectors. + :param matrix: 4x4 transformation matrix. + """ + m3x3 = matrix[:3, :3] + translation = matrix[:3, 3] + return vectors @ m3x3.T + translation + + def np_angle(a: VectorType, b: VectorType) -> float: """Get angle between vectors in radians. Designed to work similar to `Vector.angle`.