diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py index 92bf9f7d58..9154159bb5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py @@ -61,6 +61,7 @@ from .create_representation import create_representation from .distance_along_from_station import distance_along_from_station from .get_alignment import get_alignment from .get_alignment_station import get_alignment_station +from .get_curve_segment_transition_code import get_curve_segment_transition_code from .get_layout_segments import get_layout_segments from .get_horizontal_layout import get_horizontal_layout from .get_vertical_layout import get_vertical_layout @@ -103,6 +104,7 @@ __all__ = [ "get_cant_layout", "get_child_alignments", "get_curve", + "get_curve_segment_transition_code", "get_horizontal_layout", "get_layout_curve", "get_layout_segments", diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/_map_alignment_vertical_segment.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/_map_alignment_vertical_segment.py index 4fb02d56fa..cbdae413da 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/_map_alignment_vertical_segment.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/_map_alignment_vertical_segment.py @@ -158,7 +158,7 @@ def _map_circular_arc(file: ifcopenshell.file, design_parameters: entity_instanc parent_curve = file.createIfcCircle( Position=file.createIfcAxis2Placement2D( - Location=file.createIfcCartesianPoint((x, y)), + Location=file.createIfcCartesianPoint((0.0,0.0)), RefDirection=file.createIfcDirection((1.0, 0.0)), ), Radius=radius, diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/_update_curve_segment_transition_code.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/_update_curve_segment_transition_code.py index d8c4c26386..4f1867dbf5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/_update_curve_segment_transition_code.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/_update_curve_segment_transition_code.py @@ -17,7 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell -import ifcopenshell.api +import ifcopenshell.api.alignment from ifcopenshell import ifcopenshell_wrapper import ifcopenshell.geom from ifcopenshell import entity_instance @@ -30,47 +30,4 @@ def _update_curve_segment_transition_code(prev_segment: entity_instance, segment Updates IfcCurveSegment.Transition of prev_segment based on a comparison of the position, ref. direction, and curvature at the end of the prev_segment and the start of segment. """ - expected_type = "IfcCurveSegment" - if not prev_segment.is_a(expected_type): - raise TypeError(f"Expected to see '{expected_type}', instead received '{prev_segment.is_a()}'.") - - if not segment.is_a(expected_type): - raise TypeError(f"Expected to see '{expected_type}', instead received '{segment.is_a()}'.") - - if len(prev_segment.UsingCurves) != 1: - raise TypeError("prev_segment must belong to exactly one curve") - - if len(segment.UsingCurves) != 1: - raise TypeError("segment must belong to exactly one curve") - - if prev_segment.UsingCurves[0] != segment.UsingCurves[0]: - raise TypeError("Both segments must belong to the same curve") - - settings = ifcopenshell.geom.settings() - settings.set("COMPUTE_CURVATURE", True) - - prev_segment_fn = ifcopenshell_wrapper.map_shape(settings, prev_segment.wrapped_data) - prev_segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, prev_segment_fn) - e = prev_segment_evaluator.evaluate(prev_segment_fn.end()) - end = np.array(e) - - # must add the new segment to the container before mapping it, otherwise the segment doesn't - # have enough context to know if it is for horizontal, vertical, cant - - segment_fn = ifcopenshell_wrapper.map_shape(settings, segment.wrapped_data) - segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn) - s = segment_evaluator.evaluate(segment_fn.start()) - start = np.array(s) - - same_position = True if np.allclose(end[:3, 3], start[:3, 3]) else False - same_gradient = True if np.allclose(end[:3, 0], start[:3, 0]) else False - same_curvature = True if np.allclose(end[3:, :3], start[3:, :3]) else False - - if same_position: - prev_segment.Transition = "CONTINUOUS" - if same_gradient: - prev_segment.Transition = "CONTSAMEGRADIENT" - if same_curvature: - prev_segment.Transition = "CONTSAMEGRADIENTSAMECURVATURE" - else: - prev_segment.Transition = "DISCONTINUOUS" + prev_segment.Transition = ifcopenshell.api.alignment.get_curve_segment_transition_code(prev_segment,segment) diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/get_curve_segment_transition_code.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/get_curve_segment_transition_code.py new file mode 100644 index 0000000000..37338c04eb --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/get_curve_segment_transition_code.py @@ -0,0 +1,79 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2025 Thomas Krijnen +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell +import ifcopenshell.api +from ifcopenshell import ifcopenshell_wrapper +import ifcopenshell.geom +from ifcopenshell import entity_instance +import numpy as np +import math + + +def get_curve_segment_transition_code(prev_segment: entity_instance, segment: entity_instance, tolerance:float = 5.0e-4) -> str: + """ + Returns the IfcCurveSegment.Transition of prev_segment based on a comparison of + the position, ref. direction, and curvature at the end of the prev_segment and the start of segment. + """ + expected_type = "IfcCurveSegment" + if not prev_segment.is_a(expected_type): + raise TypeError(f"Expected to see '{expected_type}', instead received '{prev_segment.is_a()}'.") + + if not segment.is_a(expected_type): + raise TypeError(f"Expected to see '{expected_type}', instead received '{segment.is_a()}'.") + + if len(prev_segment.UsingCurves) != 1: + raise TypeError("prev_segment must belong to exactly one curve") + + if len(segment.UsingCurves) != 1: + raise TypeError("segment must belong to exactly one curve") + + if prev_segment.UsingCurves[0] != segment.UsingCurves[0]: + raise TypeError("Both segments must belong to the same curve") + + settings = ifcopenshell.geom.settings() + settings.set("COMPUTE_CURVATURE", True) + + prev_segment_fn = ifcopenshell_wrapper.map_shape(settings, prev_segment.wrapped_data) + prev_segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, prev_segment_fn) + e = prev_segment_evaluator.evaluate(prev_segment_fn.end()) + end = np.array(e) + + # must add the new segment to the container before mapping it, otherwise the segment doesn't + # have enough context to know if it is for horizontal, vertical, cant + + segment_fn = ifcopenshell_wrapper.map_shape(settings, segment.wrapped_data) + segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn) + s = segment_evaluator.evaluate(segment_fn.start()) + start = np.array(s) + + same_position = True if np.allclose(end[:3, 3], start[:3, 3], atol=tolerance) else False + same_gradient = True if np.allclose(end[:3, 0], start[:3, 0], atol=tolerance) else False + same_curvature = True if np.allclose(end[3:, :3], start[3:, :3], atol=tolerance) else False + + transition_code = "" + if same_position: + transition_code = "CONTINUOUS" + if same_gradient: + transition_code = "CONTSAMEGRADIENT" + if same_curvature: + transition_code = "CONTSAMEGRADIENTSAMECURVATURE" + else: + transition_code = "DISCONTINUOUS" + + return transition_code