diff --git a/src/ifcopenshell-python/test/util/test_shape_builder.py b/src/ifcopenshell-python/test/util/test_shape_builder.py index e99e87d23d..ba17a28296 100644 --- a/src/ifcopenshell-python/test/util/test_shape_builder.py +++ b/src/ifcopenshell-python/test/util/test_shape_builder.py @@ -20,13 +20,15 @@ import pytest import test.bootstrap import ifcopenshell.api import numpy as np -from ifcopenshell.util.shape_builder import ShapeBuilder, V, is_x, np_rotation_matrix -from math import degrees, radians, tan -from mathutils import Vector, Matrix +from ifcopenshell.util.shape_builder import ShapeBuilder, is_x, np_rotation_matrix, np_to_3d, np_angle, V +from math import degrees, radians +from typing import Any, Union class TestNumpyRotationMatrix(test.bootstrap.IFC4): def test_run(self): + from mathutils import Matrix, Vector + # 2D. assert np.allclose(Matrix.Rotation(radians(45), 2), np_rotation_matrix(radians(45), 2)) assert np.allclose(Matrix.Rotation(radians(45), 2, "Z"), np_rotation_matrix(radians(45), 2, "Z")) @@ -68,12 +70,12 @@ class TestCreatePolyline(test.bootstrap.IFC4): builder = ShapeBuilder(self.file) # rectangle - points = Vector((0, 0)), Vector((1, 0)), Vector((1, 1)), Vector((0, 1)) - position = Vector((2, 0)) + points = V([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]) + position = (2.0, 0.0) polyline = builder.polyline(points, closed=True, position_offset=position) - points = [p + position for p in points] - assert np.allclose(np.array(points), np.array(polyline.Points.CoordList)) + points += position + assert np.allclose(points, polyline.Points.CoordList) # use 1 line index if there are no arcs assert len(polyline.Segments) == 1 segment = polyline.Segments[0] @@ -83,13 +85,13 @@ class TestCreatePolyline(test.bootstrap.IFC4): def test_polyline_with_arc(self): builder = ShapeBuilder(self.file) - points = Vector((1, 0)), Vector((0.707, 0.707)), Vector((0, 1)), Vector((0, 2)) - position = Vector((2, 0)) + points = V([(1, 0), (0.707, 0.707), (0, 1), (0, 2)]) + position = (2, 0) arc_points = (1,) - # 4=IfcIndexedPolyCurve(#3,(IfcArcIndex((1,2,3)),IfcLineIndex((3,4,1))),$) + # 4=IfcIndexedPolyCurve(# 3,(IfcArcIndex((1,2,3)),IfcLineIndex((3,4,1))),$) polyline = builder.polyline(points, closed=False, position_offset=position, arc_points=arc_points) - points = [p + position for p in points] - assert np.allclose(np.array(points), np.array(polyline.Points.CoordList)) + points += position + assert np.allclose(points, polyline.Points.CoordList) assert len(polyline.Segments) == 2 segment = polyline.Segments[0] @@ -103,13 +105,13 @@ class TestCreatePolyline(test.bootstrap.IFC4): def test_closed_polyline_ending_with_arc(self): builder = ShapeBuilder(self.file) - points = Vector((0, 0)), Vector((1, 0)), Vector((0.5, 0.5)) - position = Vector((2, 0)) + points = V([(0, 0), (1, 0), (0.5, 0.5)]) + position = (2, 0) arc_points = (2,) # 4=IfcIndexedPolyCurve(#3,(IfcLineIndex((1,2)),IfcArcIndex((2,3,1))),$) polyline = builder.polyline(points, closed=True, position_offset=position, arc_points=arc_points) - points = [p + position for p in points] - assert np.allclose(np.array(points), np.array(polyline.Points.CoordList)) + points += position + assert np.allclose(points, polyline.Points.CoordList) assert len(polyline.Segments) == 2 segment = polyline.Segments[0] @@ -122,12 +124,16 @@ class TestCreatePolyline(test.bootstrap.IFC4): class TestCalculateTransitions(test.bootstrap.IFC4): - def calculate_and_test(self, params, length): + def calculate_and_test(self, params: dict[str, Any], length: Union[float, None]): + np_X, np_Y = 0, 1 + np_XY = slice(2) + np_YX = [1, 0] + end_profile = params["end_profile"] - start_half_dim = params["start_half_dim"] - end_half_dim = params["end_half_dim"] - offset = params["offset"] - offset = offset if not end_profile else offset.yx + start_half_dim: np.ndarray = params["start_half_dim"] + end_half_dim: np.ndarray = params["end_half_dim"] + offset: np.ndarray = params["offset"] + offset = offset if not end_profile else offset[np_YX] angle = params["angle"] calculated_length = self.builder.mep_transition_calculate(**params) @@ -135,47 +141,50 @@ class TestCalculateTransitions(test.bootstrap.IFC4): assert calculated_length is None return - assert is_x(calculated_length, length) + assert calculated_length is not None and is_x(calculated_length, length) # angle confirmation methods: # A - between two profiles of different dimensions # B - between two profiles of same dimensions, no offset by x # C - between two profiles of same dimensions, has offset by x - diff = start_half_dim.xy - end_half_dim.xy - same_dimension = is_x(diff.x if not end_profile else diff.y, 0) + diff = np.subtract(start_half_dim[np_XY], end_half_dim[np_XY]) + same_dimension = is_x(diff[np_X] if not end_profile else diff[np_Y], 0) if not same_dimension: confirmation_method = "A" else: - confirmation_method = "B" if is_x(offset.x, 0) else "C" + confirmation_method = "B" if is_x(offset[np_X], 0) else "C" if confirmation_method == "A": - A = (end_half_dim if end_profile else start_half_dim) * V(1, 0, 0) - end_profile_offset = offset.to_3d() + V(0, 0, length) - D = (start_half_dim if end_profile else end_half_dim) * V(1, 0, 0) + A = (end_half_dim if end_profile else start_half_dim) * (1, 0, 0) + end_profile_offset = np_to_3d(offset, length) + D = (start_half_dim if end_profile else end_half_dim) * (1, 0, 0) B, C = -A, -D C += end_profile_offset D += end_profile_offset - tested_angle = degrees((A - D).angle(B - C)) + tested_angle = degrees(np_angle(A - D, B - C)) assert is_x(tested_angle, angle) elif confirmation_method == "B": - O = V(0, 0, 0) - A = V(-start_half_dim.x, 0, length) + offset.to_3d() - B = A * V(-1, 1, 1) - tested_angle = degrees((A - O).angle(B - O)) + O = np.zeros(3) + A = (-start_half_dim[np_X], 0, length) + np_to_3d(offset) + B = A * (-1, 1, 1) + tested_angle = degrees(np_angle(A - O, B - O)) assert is_x(tested_angle, angle) elif confirmation_method == "C": - A = V(-start_half_dim.x, 0, 0) - H = A + V(0, 0, length) - H.y += offset.y + A = V(-start_half_dim[np_X], 0, 0) + H = A + (0, 0, length) + H[np_Y] += offset[np_Y] D = H.copy() - D.x += offset.x - tested_angle = degrees((H - A).angle(D - A)) + D[np_X] += offset[np_X] + tested_angle = degrees(np_angle(H - A, D - A)) assert is_x(tested_angle, angle) - angle = self.builder.mep_transition_calculate(**params | {"angle": None, "length": calculated_length}) - assert is_x(angle, angle) + calculated_angle = self.builder.mep_transition_calculate( + **params | {"angle": None, "length": calculated_length} + ) + assert calculated_angle is not None + assert is_x(calculated_angle, angle) def test_mep_transition_same_dims_no_offset(self): self.builder = ShapeBuilder(self.file) @@ -257,5 +266,5 @@ class TestCalculateTransitions(test.bootstrap.IFC4): self.calculate_and_test(params, None) # method C - params["offset"].x = 10 + params["offset"][0] = 10.0 self.calculate_and_test(params, None)