From 90418572773ec43d5f70cb81f31a93259cc109e8 Mon Sep 17 00:00:00 2001 From: Richard Brice <37087370+RickBrice@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:57:51 -0800 Subject: [PATCH] Updates segment_vertices to handle IfcAlignmentSegment --- .../api/alignment/segment_vertices.py | 32 ++++++++++++++++--- .../api/alignment/test_segment_vertices.py | 32 +++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/segment_vertices.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/segment_vertices.py index eb3c13a580..ae1fd86656 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/segment_vertices.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/segment_vertices.py @@ -42,7 +42,7 @@ def _intersect_lines(p1, d1, p2, d2): return (x, y) -def segment_vertices(curve_segment: entity_instance): +def segment_vertices(file: ifcopenshell.file, segment: entity_instance): """ Generates segment vertices. Segment vertices are at the start and end as well as the points where the tangents at the start and end of the segment intersect (the TI point) and where lines @@ -50,18 +50,36 @@ def segment_vertices(curve_segment: entity_instance): TI and NI are None if intersection points do not exist, such as in the case of a line. - :param curve_segment: A curve segment + :param curve_segment: A curve segment of type IfcAlignmentSegment or IfcCurveSegment :return: tuples for Start, End, TI, NI """ - supported_segment_types = ["IFCCURVESEGMENT"] - segment_type = curve_segment.is_a().upper() + supported_segment_types = ["IFCALIGNMENTSEGMENT", "IFCCURVESEGMENT"] + segment_type = segment.is_a().upper() if not segment_type in supported_segment_types: raise NotImplementedError( f"Expected entity type to be one of {[_ for _ in supported_segment_types]}, got '{segment_type}" ) + # in the general case an IfcAlignmentSegment for a Helmert transition curve + # maps into two IfcCurveSegment geometric representations. + # For that reason, we have a start_segment_curve and and end_segment_curve. + # In the more common case, there is only one IfcCurveSegment geometric representation + # and start_segment_curve and end_segment_curve are equal + if segment_type == "IFCALIGNMENTSEGMENT": + representations = ifcopenshell.util.representation.get_representations_iter(segment) + for representation in representations: + if representation.RepresentationIdentifier == "Axis" and representation.RepresentationType == "Segment": + start_segment_curve = representation.Items[0] + end_segment_curve = representation.Items[-1] + break + else: + start_segment_curve = segment + end_segment_curve = segment + settings = ifcopenshell.geom.settings() - segment_fn = ifcopenshell_wrapper.map_shape(settings, curve_segment.wrapped_data) + + # get parameters at start of start_segment_curve + segment_fn = ifcopenshell_wrapper.map_shape(settings, start_segment_curve.wrapped_data) segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn) s = segment_evaluator.evaluate(segment_fn.start()) @@ -71,6 +89,10 @@ def segment_vertices(curve_segment: entity_instance): sdx = float(start[0, 0]) sdy = float(start[1, 0]) + # get parameters at end of end_segment_curve + segment_fn = ifcopenshell_wrapper.map_shape(settings, end_segment_curve.wrapped_data) + segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn) + e = segment_evaluator.evaluate(segment_fn.end()) end = np.array(e) ex = float(end[0, 3]) diff --git a/src/ifcopenshell-python/test/api/alignment/test_segment_vertices.py b/src/ifcopenshell-python/test/api/alignment/test_segment_vertices.py index 44a1b2a871..0075bf7f57 100644 --- a/src/ifcopenshell-python/test/api/alignment/test_segment_vertices.py +++ b/src/ifcopenshell-python/test/api/alignment/test_segment_vertices.py @@ -83,9 +83,23 @@ def test_segment_vertices(): [(7790.932128312587, 4006.7307645487535), (8480.0, 2010.0000000000002), None, None], [(8480.0, 2010.0000000000002), (8480.0, 2010.0000000000002), None, None], ] + + layout = ifcopenshell.api.alignment.get_horizontal_layout(alignment) + segments = ifcopenshell.api.alignment.get_layout_segments(layout) + for segment, expected in zip(segments, expect): + s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(file, segment) + s = unit_convert(unit_scale, s) + e = unit_convert(unit_scale, e) + ti = unit_convert(unit_scale, ti) + ni = unit_convert(unit_scale, ni) + assert s == pytest.approx(expected[0]) + assert e == pytest.approx(expected[1]) + assert ti == pytest.approx(expected[2]) + assert ni == pytest.approx(expected[3]) + curve = ifcopenshell.api.alignment.get_basis_curve(alignment) for segment, expected in zip(curve.Segments, expect): - s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(segment) + s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(file, segment) s = unit_convert(unit_scale, s) e = unit_convert(unit_scale, e) ti = unit_convert(unit_scale, ti) @@ -128,9 +142,23 @@ def test_segment_vertices(): [(10200.0, 103.0), (12800.0, 90.0), None, None], [(12800.0, 90.0), (12800.0, 90.0), None, None], ] + + layout = ifcopenshell.api.alignment.get_vertical_layout(alignment) + segments = ifcopenshell.api.alignment.get_layout_segments(layout) + for segment, expected in zip(segments, expect): + s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(file, segment) + s = unit_convert(unit_scale, s) + e = unit_convert(unit_scale, e) + ti = unit_convert(unit_scale, ti) + ni = unit_convert(unit_scale, ni) + assert s == pytest.approx(expected[0]) + assert e == pytest.approx(expected[1]) + assert ti == pytest.approx(expected[2]) + assert ni == pytest.approx(expected[3]) + curve = ifcopenshell.api.alignment.get_curve(alignment) for segment, expected in zip(curve.Segments, expect): - s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(segment) + s, e, ti, ni = ifcopenshell.api.alignment.segment_vertices(file, segment) s = unit_convert(unit_scale, s) e = unit_convert(unit_scale, e) ti = unit_convert(unit_scale, ti)