First Commit

This commit is contained in:
2026-05-31 10:17:09 +07:00
commit 17a9c69379
4547 changed files with 1170384 additions and 0 deletions
@@ -0,0 +1,203 @@
# 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
import ifcopenshell.api.aggregate
import ifcopenshell.api.alignment
import ifcopenshell.api.geometry
import ifcopenshell.api.nest
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.util.representation
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._add_zero_length_segment import _add_zero_length_segment
def _move_vertical_layout_to_child_alignment(
file: ifcopenshell.file, parent_alignment: entity_instance, vertical_layout: entity_instance
):
"""
Creates a new child alignment and aggregates it to the parent alignment. Moves the vertical alignment from the parent
alignment to the child alignment. Also moves the "Axis/Curve3D" representation to the child alignment, if present.
This function supports the transition of vertical alignment between CT 4.1.4.4.1.1 and 4.1.4.4.1.2 because a subsequent
vertical alignment is being added and the Alignment Layout - Reusing Horizontal Layout concept applies.
"""
# unhook the vertical layout from the parent alignment
ifcopenshell.api.nest.unassign_object(file, related_objects=[vertical_layout])
# create the child alignment
child_alignment = file.createIfcAlignment(
GlobalId=ifcopenshell.guid.new(), Name=f"Child of {parent_alignment.Name}"
)
# nest the vertical layout onto the child alignment
ifcopenshell.api.nest.assign_object(file, related_objects=[vertical_layout], relating_object=child_alignment)
# aggregate the child alignment to the parent alignment
ifcopenshell.api.aggregate.assign_object(file, products=[child_alignment], relating_object=parent_alignment)
# move all referents positioning segments of the vertical layout to the referent nest of the child alignment
child_referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, child_alignment)
parent_referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, parent_alignment)
for referent in parent_referent_nest.RelatedObjects:
for product in referent.Positions[0].RelatedProducts:
if product.is_a("IfcAlignmentSegment") and product.Nests[0].RelatingObject == vertical_layout:
# ifcopenshell.api.nest.change_nest(file,referent,child_alignment) - this doesn't work because referent is assigned to child_alignment.IsNestedBy[0].RelatedObjects
# and it needs to be assigned to child_alignment.IsNestedBy[1].RelatedObjects
# move the referent manually - unassign it and add it to the child alignment's referent nest
ifcopenshell.api.nest.unassign_object(file, [referent])
child_referent_nest.RelatedObjects += (referent,)
# if the parent alignment has a representation, move the Axis/Curve3D represention to the child alignment
base_curve = ifcopenshell.api.alignment.get_basis_curve(parent_alignment)
if base_curve:
representations = ifcopenshell.util.representation.get_representations_iter(parent_alignment)
for representation in representations:
if representation.RepresentationIdentifier == "Axis" and representation.RepresentationType == "Curve3D":
ifcopenshell.api.geometry.unassign_representation(file, parent_alignment, representation)
ifcopenshell.api.geometry.assign_representation(file, child_alignment, representation)
child_alignment.ObjectPlacement = parent_alignment.ObjectPlacement
break
def add_vertical_layout(file: ifcopenshell.file, parent_alignment: entity_instance) -> entity_instance:
"""
Adds a vertical layout to a previously created alignment.
If this is the first vertical layout assigned to the parent_alignment the IFC CT 4.1.4.4.1.1 Alignment Layout - Horizontal, Vertical and Cant
is followed. If this is the second or subsequent vertical layout assigned to the parent_alignment the
IFC CT 4.1.4.4.1.2 Alignment Layout - Reusing Horizontal Layout is followed.
When the second vertical layout is added, the structure of the IFC model must transition from one concept template to the other.
Specifically, the following occurs:
1) The first child IfcAlignment is created and is IfcRelAggregates with the parent alignment.
2) The first vertical layout is unassigned from the IfcRelNests of the parent alignment and is IfcRelNests to the new child alignment.
3) A second child IfcAlignment is created and it is IfcRelAggregates with the parent alignment.
4) The vertical layout is IfcRelNests to the second child alignment
For the third and subsequent vertical layouts, a new child alignment is created and aggregated to the parent alignment.
A zero segment length terminated IfcGradientCurve is created for the new vertical layout
:param parent_alignment: The parent alignment
:return: The new vertical layout, including the manditory zero length segment
"""
vertical_layout = file.createIfcAlignmentVertical(GlobalId=ifcopenshell.guid.new())
# get all the child alignments under alignment
child_alignments = [
c for c in ifcopenshell.util.element.get_decomposition(parent_alignment) if c.is_a("IfcAlignment")
]
# Get all the IfcAlignmentVertical that are nesting alignment (there should be 0 or 1)
# if 0, alignment is just horizontal and we are adding the first vertical so it will nest to the alignment,
# or there are multiple vertical and they nest to the aggregated child alignments
# if 1, there is one vertical alignments. Move it to a child alignment
vertical_layouts_nesting_alignment = [
c for c in ifcopenshell.util.element.get_components(parent_alignment) if c.is_a("IfcAlignmentVertical")
]
# move the vertical layout to a child alignment because there is going to be more than one vertical
assert len(vertical_layouts_nesting_alignment) == 0 or len(vertical_layouts_nesting_alignment) == 1
for vertical_layout_nesting_alignment in vertical_layouts_nesting_alignment:
_move_vertical_layout_to_child_alignment(file, parent_alignment, vertical_layout_nesting_alignment)
if len(child_alignments) == 0 and len(vertical_layouts_nesting_alignment) == 0:
# this is the first vertical layout so nest it into the parent alignment (IFC CT 4.1.4.4.1.1)
ifcopenshell.api.nest.assign_object(file, related_objects=[vertical_layout], relating_object=parent_alignment)
base_curve = ifcopenshell.api.alignment.get_basis_curve(parent_alignment)
# the parent alignment has a Representation so create a representation for the vertical
gradient_curve = file.createIfcGradientCurve(
Segments=[], SelfIntersect=False, BaseCurve=base_curve, EndPoint=None
)
# Per IFC CT 4.1.7.1.1.1, the shape representation for Horizontal geometry only is
# RepresentationIdentifier="Axis" and RepresentationType="Curve2D".
# However, per IFC CT 4.1.7.1.1.2 and 3 the shape represenation with Horizontal, Vertical and Cant
# is RepresentationIdentifier="FootPrint" and RepresentationType="Curve2D" for the horizontal and
# RepresentationIdentifier="Axis" and RepresentationType="Curve3D" for the 2.5D curve.
# Since the alignment is transitioning from horizontal only to horizontal+vertical, the
# RepresentationIdentifier must change from "Axis" to "FootPrint"
representations = ifcopenshell.util.representation.get_representations_iter(parent_alignment)
for representation in representations:
if representation.RepresentationIdentifier == "Axis" and representation.RepresentationType == "Curve2D":
representation.RepresentationIdentifier = "FootPrint"
break
# create the Axis,Curve3D representation
axis_geom_subcontext = ifcopenshell.api.alignment.get_axis_subcontext(file)
axis3d_shape_representation = file.createIfcShapeRepresentation(
ContextOfItems=axis_geom_subcontext,
RepresentationIdentifier="Axis",
RepresentationType="Curve3D",
Items=(gradient_curve,),
)
ifcopenshell.api.geometry.assign_representation(file, parent_alignment, axis3d_shape_representation)
else:
# there are multiple vertical reusing the horizontal (IFC CT 4.1.4.4.1.2)
# this is the second or subsequent vertical reusing the horizontal
# create a new child alignment for the new vertical
child_alignment = file.createIfcAlignment(
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=None,
Name=f"Child of {parent_alignment.Name}",
Description=None,
ObjectType=None,
ObjectPlacement=None,
Representation=None,
PredefinedType=None,
)
# Aggregate the child alignment to the parent alignment
ifcopenshell.api.aggregate.assign_object(file, (child_alignment,), parent_alignment)
# nest the vertical under the child alignment
ifcopenshell.api.nest.assign_object(file, related_objects=[vertical_layout], relating_object=child_alignment)
child_alignment.ObjectPlacement = parent_alignment.ObjectPlacement
# the parent alignment has a Representation so create a representation for the vertical
base_curve = ifcopenshell.api.alignment.get_basis_curve(parent_alignment)
gradient_curve = file.createIfcGradientCurve(
Segments=[], SelfIntersect=False, BaseCurve=base_curve, EndPoint=None
)
axis_geom_subcontext = ifcopenshell.api.alignment.get_axis_subcontext(file)
# create the Curve3D representation
axis3d_shape_representation = file.createIfcShapeRepresentation(
ContextOfItems=axis_geom_subcontext,
RepresentationIdentifier="Axis",
RepresentationType="Curve3D",
Items=(gradient_curve,),
)
# add the representation to the child alignment
ifcopenshell.api.geometry.assign_representation(file, child_alignment, axis3d_shape_representation)
# All alignment layouts must end with a zero length segment. Their geometric representations must also end with a zero length segment.
# Now that all the geometry is setup, add the zero length segment to the layout, which also adds a zero length segment to the representation
_add_zero_length_segment(file, vertical_layout)
return vertical_layout