First Commit
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user