From 7ed8584edc6609654cea608d699348c9cca7ce5d Mon Sep 17 00:00:00 2001 From: Richard Brice <37087370+RickBrice@users.noreply.github.com> Date: Sat, 8 Aug 2026 10:35:20 -0700 Subject: [PATCH] Revised update_alignment_parameter_segment_tags to make EndTag optional --- ...update_alignment_parameter_segment_tags.py | 38 +++++++++++-------- ...update_alignment_parameter_segment_tags.py | 36 ++++++++++++++++-- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/update_alignment_parameter_segment_tags.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/update_alignment_parameter_segment_tags.py index 9a72b19d49..caebc78a1f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/update_alignment_parameter_segment_tags.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/update_alignment_parameter_segment_tags.py @@ -25,25 +25,32 @@ from ifcopenshell.api.alignment._get_segment_start_point_label import ( ) -def update_alignment_parameter_segment_tags(file: ifcopenshell.file, layout: entity_instance) -> None: +def update_alignment_parameter_segment_tags( + file: ifcopenshell.file, layout: entity_instance, label_end_tag: bool = False +) -> None: """ - Sets IfcAlignmentParameterSegment.StartTag/EndTag for every segment transition in an alignment - layout. Unlike update_key_point_referents, this does not create any IfcReferent or IfcRelNests -- - it only mutates the StartTag/EndTag string attributes already present on each segment's - DesignParameters. + Sets IfcAlignmentParameterSegment.StartTag (and, optionally, EndTag) for every segment + transition in an alignment layout. Unlike update_key_point_referents, this does not create any + IfcReferent or IfcRelNests -- it only mutates the StartTag/EndTag string attributes already + present on each segment's DesignParameters. - For each transition between two consecutive segments, the outgoing segment's EndTag and the - incoming segment's StartTag are both set to the same computed tag (they describe the same - physical point), using the same label-and-station format as update_key_point_referents' Name - minus the alignment name (via _get_key_point_tag), e.g. "145+98.32 (P.C.)". Every real segment - ends up with both StartTag and EndTag populated: the first segment's StartTag and the last - segment's EndTag come from the "Beginning of Alignment"/"End of Alignment" boundary labels. + Every real segment's StartTag is set to a computed tag describing the point where it begins, + using the same label-and-station format as update_key_point_referents' Name minus the alignment + name (via _get_key_point_tag), e.g. "145+98.32 (P.C.)". The first segment's StartTag comes from + the "Beginning of Alignment" boundary label. + + EndTag is left untouched unless `label_end_tag` is True. When enabled, for each transition + between two consecutive segments, the outgoing segment's EndTag is set to the same tag as the + incoming segment's StartTag (they describe the same physical point), and the last segment's + EndTag is set from the "End of Alignment" boundary label. Labels come from _get_segment_start_point_label -- if a callback has been registered via register_referent_name_callback(), its output is used instead of the built-in labels, exactly as in update_key_point_referents. :param layout: IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant + :param label_end_tag: if True, also sets EndTag on every real segment. If False (default), + EndTag is left untouched. :return: None -- this function mutates segment.DesignParameters.StartTag/EndTag in place Example: @@ -85,7 +92,7 @@ def update_alignment_parameter_segment_tags(file: ifcopenshell.file, layout: ent tag = _get_key_point_tag(file, label, station) dp.StartTag = tag - if prev_segment is not None: + if prev_segment is not None and label_end_tag: prev_segment.DesignParameters.EndTag = tag if is_horizontal: @@ -95,6 +102,7 @@ def update_alignment_parameter_segment_tags(file: ifcopenshell.file, layout: ent prev_segment = segment - label = _get_segment_start_point_label(prev_segment, None) - station = start_station + distance_along - prev_segment.DesignParameters.EndTag = _get_key_point_tag(file, label, station) + if label_end_tag: + label = _get_segment_start_point_label(prev_segment, None) + station = start_station + distance_along + prev_segment.DesignParameters.EndTag = _get_key_point_tag(file, label, station) diff --git a/src/ifcopenshell-python/test/api/alignment/test_update_alignment_parameter_segment_tags.py b/src/ifcopenshell-python/test/api/alignment/test_update_alignment_parameter_segment_tags.py index daaeaf062a..ef114ec3dd 100644 --- a/src/ifcopenshell-python/test/api/alignment/test_update_alignment_parameter_segment_tags.py +++ b/src/ifcopenshell-python/test/api/alignment/test_update_alignment_parameter_segment_tags.py @@ -138,7 +138,7 @@ def test_single_real_segment_produces_only_boundary_tags(): ) ifcopenshell.api.alignment.create_layout_segment(file, horizontal, design_parameters) - ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, horizontal) + ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, horizontal, label_end_tag=True) segments = _real_segments(horizontal) assert len(segments) == 1 @@ -147,12 +147,39 @@ def test_single_real_segment_produces_only_boundary_tags(): assert _label(dp.EndTag) == "P.O.E." +def test_end_tag_not_labelled_by_default(): + file = _new_file_no_context() + alignment = ifcopenshell.api.alignment.create(file, "A1", include_geometry=False) + horizontal = ifcopenshell.api.alignment.get_horizontal_layout(alignment) + + design_parameters = file.createIfcAlignmentHorizontalSegment( + StartTag=None, + EndTag=None, + StartPoint=file.createIfcCartesianPoint((0.0, 0.0)), + StartDirection=0.0, + StartRadiusOfCurvature=0.0, + EndRadiusOfCurvature=0.0, + SegmentLength=100.0, + GravityCenterLineHeight=None, + PredefinedType="LINE", + ) + ifcopenshell.api.alignment.create_layout_segment(file, horizontal, design_parameters) + + ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, horizontal) + + segments = _real_segments(horizontal) + assert len(segments) == 1 + dp = segments[0].DesignParameters + assert _label(dp.StartTag) == "P.O.B." + assert dp.EndTag is None + + def test_horizontal_tag_labels_and_adjacency(): file = _new_file() alignment = _build_alignment(file) horizontal = ifcopenshell.api.alignment.get_horizontal_layout(alignment) - ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, horizontal) + ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, horizontal, label_end_tag=True) segments = _real_segments(horizontal) assert len(segments) == 7 @@ -177,7 +204,7 @@ def test_vertical_tag_labels_and_adjacency(): alignment = _build_alignment(file) vertical = ifcopenshell.api.alignment.get_vertical_layout(alignment) - ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, vertical) + ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, vertical, label_end_tag=True) segments = _real_segments(vertical) assert len(segments) == 9 @@ -242,7 +269,7 @@ def test_cant_layout_boundary_tags(): ) ifcopenshell.api.alignment.create_layout_segment(file, cant, dp2) - ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, cant) + ifcopenshell.api.alignment.update_alignment_parameter_segment_tags(file, cant, label_end_tag=True) segments = _real_segments(cant) assert _label(segments[0].DesignParameters.StartTag) == "C.P.O.B." @@ -273,6 +300,7 @@ test_returns_none() test_no_referents_or_rel_nests_created() test_no_real_segments_leaves_tags_none() test_single_real_segment_produces_only_boundary_tags() +test_end_tag_not_labelled_by_default() test_horizontal_tag_labels_and_adjacency() test_vertical_tag_labels_and_adjacency() test_cant_layout_boundary_tags()