Separates referent and alignment layout usages into different referents

This commit is contained in:
Richard Brice
2025-08-25 19:58:55 -07:00
parent 9331e89d15
commit 1de5940294
12 changed files with 160 additions and 71 deletions
@@ -60,6 +60,8 @@ from .create_segment_representations import create_segment_representations
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_layout_nest import get_alignment_layout_nest
from .get_alignment_segment_nest import get_alignment_segment_nest
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
@@ -74,6 +76,7 @@ from .get_curve import get_curve
from .get_layout_curve import get_layout_curve
from .get_mapped_segments import get_mapped_segments
from .get_parent_alignment import get_parent_alignment
from .get_referent_nest import get_referent_nest
from .has_zero_length_segment import has_zero_length_segment
from .layout_horizontal_alignment_by_pi_method import layout_horizontal_alignment_by_pi_method
from .layout_vertical_alignment_by_pi_method import layout_vertical_alignment_by_pi_method
@@ -97,7 +100,9 @@ __all__ = [
"create_segment_representations",
"distance_along_from_station",
"get_alignment",
"get_alignment_layout_nest",
"get_alignment_layouts",
"get_alignment_segment_nest",
"get_alignment_station",
"get_axis_subcontext",
"get_basis_curve",
@@ -109,6 +114,7 @@ __all__ = [
"get_layout_curve",
"get_layout_segments",
"get_parent_alignment",
"get_referent_nest",
"get_vertical_layout",
"has_zero_length_segment",
"layout_horizontal_alignment_by_pi_method",
@@ -173,7 +173,8 @@ def _add_segment_to_layout(file: ifcopenshell.file, layout: entity_instance, seg
# get the previous segment. Working from the end of the basis curve, -1 is zero length segment
# -2 is the newly added segment, so -3 is the segment occuring just before the newly added segment
prev_segment = layout.IsNestedBy[0].RelatedObjects[-3] if 2 < len(layout.IsNestedBy[0].RelatedObjects) else None
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
prev_segment = segment_nest.RelatedObjects[-3] if 2 < len(segment_nest.RelatedObjects) else None
name = f"{_get_segment_start_point_label(prev_segment,segment)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
referent = ifcopenshell.api.alignment.add_stationing_referent(
file, segment, distance_along=dist_along, station=station, name=name
@@ -183,7 +184,8 @@ def _add_segment_to_layout(file: ifcopenshell.file, layout: entity_instance, seg
# this is the first real segment in the horizontal alignment
# update the location of the alignment's stationing referent
alignment = ifcopenshell.api.alignment.get_alignment(layout)
stationing_referent = alignment.IsNestedBy[0].RelatedObjects[0]
ref_nest = ifcopenshell.api.alignment.get_referent_nest(file,alignment)
stationing_referent = ref_nest.RelatedObjects[0]
p = curve_evaluator.evaluate(
stationing_referent.ObjectPlacement.RelativePlacement.Location.DistanceAlong.wrappedValue
)
@@ -133,7 +133,9 @@ def add_stationing_referent(
)
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=referent, name="Pset_Stationing")
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": station})
ifcopenshell.api.nest.assign_object(file, related_objects=[referent], relating_object=element)
nest = ifcopenshell.api.alignment.get_referent_nest(file,element)
nest.RelatedObjects += (referent,)
if len(alignment.Positions) == 0:
rel_positions = file.createIfcRelPositions(
@@ -87,7 +87,6 @@ def create(
# define stationing
name = ifcopenshell.util.alignment.station_as_string(file, start_station)
referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, start_station, name)
ifcopenshell.api.nest.reorder_nesting(file, referent, -1, 0)
# IFC 4.1.4.1.1 Alignment Aggregation To Project
@@ -16,16 +16,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util
from ifcopenshell import entity_instance
from collections.abc import Sequence
import ifcopenshell.util.representation
def get_alignment(layout: entity_instance) -> entity_instance:
"""
Returns the alignment that nests this layout
"""
return layout.Nests[0].RelatingObject if 0 < len(layout.Nests) else None
alignment = None
for nest in layout.Nests:
if nest.RelatingObject.is_a("IfcAlignment"):
alignment = nest.RelatingObject
break
return alignment
@@ -0,0 +1,36 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# 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 <http://www.gnu.org/licenses/>.
import ifcopenshell
from ifcopenshell import entity_instance
def get_alignment_layout_nest(alignment: entity_instance) -> entity_instance:
"""
Searches for the IfcRelNest that contains IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant
:param alignment: the alignment
:return: Returns the IfcRelNests containing the alignment layout
"""
layout_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant"]
for nest in alignment.IsNestedBy:
for related_object in nest.RelatedObjects:
if related_object.is_a() in layout_types:
return nest
return None
@@ -0,0 +1,33 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# 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 <http://www.gnu.org/licenses/>.
import ifcopenshell
from ifcopenshell import entity_instance
def get_alignment_segment_nest(layout: entity_instance) -> entity_instance:
"""
Searches for the IfcRelNest that contains IfcAlignmentSegment
:param layout: an alignment layout, expected to be one of IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant
:return: Returns the IfcRelNests
"""
for nest in layout.IsNestedBy:
for related_object in nest.RelatedObjects:
if related_object.is_a("IfcAlignmentSegment"):
return nest
return None
@@ -0,0 +1,36 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# 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 <http://www.gnu.org/licenses/>.
import ifcopenshell
from ifcopenshell import entity_instance
def get_referent_nest(file: ifcopenshell.file,entity: entity_instance) -> entity_instance:
"""
Searches for the IfcRelNest that contains IfcReferent. If one is not found, a empty IfcRelNests is created.
:param file:
:param entity: any entity that is the parent in a nesting relationship, but intended to be IfcAlignment, IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant
:return: Returns the IfcRelNests.
"""
for nest in entity.IsNestedBy:
for related_object in nest.RelatedObjects:
if related_object.is_a("IfcReferent"):
return nest
nest = file.createIfcRelNests(GlobalId=ifcopenshell.guid.new(),RelatingObject=entity,RelatedObjects=[])
return nest
@@ -32,23 +32,20 @@ def test_add_vertical_alignment():
alignment = ifcopenshell.api.alignment.create(file, "A1", include_vertical=False)
assert len(alignment.IsDecomposedBy) == 0 # no child alignments
assert len(alignment.IsNestedBy) == 1 # one nest
assert len(alignment.IsNestedBy[0].RelatedObjects) == 2 # nesting IfcReferent, IfcAlignmentHorizontal
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert len(alignment.IsNestedBy) == 2 # nests for layout and referents
assert len(alignment.IsNestedBy[0].RelatedObjects) == 1
assert ifcopenshell.api.alignment.get_referent_nest(file,alignment).RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcAlignmentHorizontal")
curve = ifcopenshell.api.alignment.get_curve(alignment)
assert curve.is_a("IfcCompositeCurve")
vertical_layout = ifcopenshell.api.alignment.add_vertical_layout(file, alignment)
assert len(alignment.IsDecomposedBy) == 0 # no child alignments
assert len(alignment.IsNestedBy) == 1 # one nest
assert (
len(alignment.IsNestedBy[0].RelatedObjects) == 3
) # nesting IfcReferent, IfcAlignmentHorizontal, IfcAlignmentVertical
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert alignment.IsNestedBy[0].RelatedObjects[2].is_a("IfcAlignmentVertical")
assert len(alignment.IsNestedBy) == 2
assert (len(alignment.IsNestedBy[0].RelatedObjects) == 2)
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcAlignmentHorizontal")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentVertical")
curve = ifcopenshell.api.alignment.get_curve(alignment)
assert curve.is_a("IfcGradientCurve")
@@ -56,9 +53,7 @@ def test_add_vertical_alignment():
vertical_layout = ifcopenshell.api.alignment.add_vertical_layout(file, alignment)
assert len(alignment.IsDecomposedBy) == 1 # 1 IfcRelAggreates relationship for the child algiments
assert (
len(alignment.IsDecomposedBy[0].RelatedObjects) == 2
) # two child alignments, one for the first vertical and one for the vertical just added
assert (len(alignment.IsDecomposedBy[0].RelatedObjects) == 2)
for child_alignment in alignment.IsDecomposedBy[0].RelatedObjects:
assert child_alignment.is_a("IfcAlignment")
@@ -66,10 +61,9 @@ def test_add_vertical_alignment():
assert len(child_alignment.IsNestedBy[0].RelatedObjects) == 1 # The IfcAlignmentVertical
assert child_alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcAlignmentVertical")
assert len(alignment.IsNestedBy) == 1 # 1 nesting relationsip for the alignments
assert len(alignment.IsNestedBy[0].RelatedObjects) == 2 # nesting IfcReferent and IfcAlignmentHorizontal
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert len(alignment.IsNestedBy) == 2
assert len(alignment.IsNestedBy[0].RelatedObjects) == 1
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcAlignmentHorizontal")
test_add_vertical_alignment()
@@ -46,22 +46,15 @@ def test_create_by_pi_method():
)
assert len(alignment.IsDecomposedBy) == 0 # no child alignments
assert len(alignment.IsNestedBy) == 1 # one nest
assert (
len(alignment.IsNestedBy[0].RelatedObjects) == 3
) # nesting IfcReferent, IfcAlignmentHorizontal, IfcAlignmentVertical
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert alignment.IsNestedBy[0].RelatedObjects[2].is_a("IfcAlignmentVertical")
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy) == 1
) # nesting of segments beneath IfcAlignmentHorizontal
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects) == 8
) # segments in horizontal layout
assert (
len(alignment.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects) == 10
) # segments in vertical layout
assert len(alignment.IsNestedBy) == 2 # one nest
assert (len(alignment.IsNestedBy[0].RelatedObjects) == 2)
assert ifcopenshell.api.alignment.get_referent_nest(file,alignment).RelatedObjects[0].is_a("IfcReferent")
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
assert layout_nest.RelatedObjects[0].is_a("IfcAlignmentHorizontal")
assert layout_nest.RelatedObjects[1].is_a("IfcAlignmentVertical")
assert (len(layout_nest.RelatedObjects[0].IsNestedBy) == 1) # nesting of segments beneath IfcAlignmentHorizontal
assert (len(layout_nest.RelatedObjects[0].IsNestedBy[0].RelatedObjects) == 8) # segments in horizontal layout
assert (len(layout_nest.RelatedObjects[1].IsNestedBy[0].RelatedObjects) == 10) # segments in vertical layout
test_create_by_pi_method()
@@ -45,16 +45,13 @@ def test_horizontal_layout_by_pi_method():
alignment = ifcopenshell.api.alignment.create_by_pi_method(file, "TestAlignment", coordinates, radii)
assert len(alignment.IsDecomposedBy) == 0 # no child alignments
assert len(alignment.IsNestedBy) == 1 # one nest
assert len(alignment.IsNestedBy[0].RelatedObjects) == 2 # nesting IfcReferent, IfcAlignmentHorizontal
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy) == 1
) # nesting of segments beneath IfcAlignmentHorizontal
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects) == 3
) # segments in horizontal layout
assert len(alignment.IsNestedBy) == 2
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file,alignment)
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
assert referent_nest.RelatedObjects[0].is_a("IfcReferent")
assert layout_nest.RelatedObjects[0].is_a("IfcAlignmentHorizontal")
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout_nest.RelatedObjects[0])
assert (len(segment_nest.RelatedObjects) == 3) # segments in horizontal layout
test_horizontal_layout_by_pi_method()
@@ -58,22 +58,13 @@ def test_vertical_layout_by_pi_method():
ifcopenshell.api.alignment.layout_vertical_alignment_by_pi_method(file, vlayout, vpoints, lengths)
assert len(alignment.IsDecomposedBy) == 0 # no child alignments
assert len(alignment.IsNestedBy) == 1 # one nest
assert (
len(alignment.IsNestedBy[0].RelatedObjects) == 3
) # nesting IfcReferent, IfcAlignmentHorizontal, IfcAlignmentVertical
assert alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
assert alignment.IsNestedBy[0].RelatedObjects[1].is_a("IfcAlignmentHorizontal")
assert alignment.IsNestedBy[0].RelatedObjects[2].is_a("IfcAlignmentVertical")
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy) == 1
) # nesting of segments beneath IfcAlignmentHorizontal
assert (
len(alignment.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects) == 2
) # segments in horizontal layout
assert (
len(alignment.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects) == 3
) # segments in vertical layout
assert len(alignment.IsNestedBy) == 2
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file,alignment)
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
assert(len(referent_nest.RelatedObjects) == 1)
assert(len(layout_nest.RelatedObjects) == 2)
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(vlayout)
assert(len(segment_nest.RelatedObjects) == 3)
test_vertical_layout_by_pi_method()