mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Merge remote-tracking branch 'origin/v0.8.0' into tfk-rocksdb-storage
This commit is contained in:
@@ -60,7 +60,9 @@ 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_station import get_alignment_station
|
||||
from .get_alignment_layout_nest import get_alignment_layout_nest
|
||||
from .get_alignment_segment_nest import get_alignment_segment_nest
|
||||
from .get_alignment_start_station import get_alignment_start_station
|
||||
from .get_curve_segment_transition_code import get_curve_segment_transition_code
|
||||
from .get_layout_segments import get_layout_segments
|
||||
from .get_horizontal_layout import get_horizontal_layout
|
||||
@@ -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,8 +100,10 @@ __all__ = [
|
||||
"create_segment_representations",
|
||||
"distance_along_from_station",
|
||||
"get_alignment",
|
||||
"get_alignment_layout_nest",
|
||||
"get_alignment_layouts",
|
||||
"get_alignment_station",
|
||||
"get_alignment_segment_nest",
|
||||
"get_alignment_start_station",
|
||||
"get_axis_subcontext",
|
||||
"get_basis_curve",
|
||||
"get_cant_layout",
|
||||
@@ -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",
|
||||
|
||||
@@ -60,132 +60,93 @@ def _add_segment_to_layout(file: ifcopenshell.file, layout: entity_instance, seg
|
||||
# swap the last two segments
|
||||
ifcopenshell.api.nest.reorder_nesting(file, segment, -1, -1)
|
||||
|
||||
# add the new segment to the geometric representation curve
|
||||
_add_segment_to_curve(file, segment, curve)
|
||||
if curve:
|
||||
# add the new segment to the geometric representation curve
|
||||
_add_segment_to_curve(file, segment, curve)
|
||||
|
||||
# gather information to:
|
||||
# (1) add a referent at the start of this segment
|
||||
# (2) update the name of the zero length segment's referent
|
||||
# gather information to:
|
||||
# (1) add a referent at the start of this segment
|
||||
# (2) update the name of the zero length segment's referent
|
||||
|
||||
# get the distance along the alignment to the start of the new segment
|
||||
dist_along = 0.0
|
||||
if layout.is_a("IfcAlignmentHorizontal"):
|
||||
for nest in layout.IsNestedBy:
|
||||
for seg in nest.RelatedObjects:
|
||||
if seg.is_a("IfcAlignmentSegment"):
|
||||
dist_along += seg.DesignParameters.SegmentLength
|
||||
# get the distance along the alignment to the start of the new segment
|
||||
dist_along = 0.0
|
||||
if layout.is_a("IfcAlignmentHorizontal"):
|
||||
for nest in layout.IsNestedBy:
|
||||
for seg in nest.RelatedObjects:
|
||||
if seg.is_a("IfcAlignmentSegment"):
|
||||
dist_along += seg.DesignParameters.SegmentLength
|
||||
|
||||
# the length of the current segment is in dist_along, so subtract it out
|
||||
dist_along -= segment.DesignParameters.SegmentLength
|
||||
else:
|
||||
dist_along = segment.DesignParameters.StartDistAlong
|
||||
|
||||
# get the station of the start of the segment
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
|
||||
station = start_station + dist_along
|
||||
|
||||
# update the zero length layout segment
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
|
||||
|
||||
zero_length_segment = layout.IsNestedBy[0].RelatedObjects[-1]
|
||||
# DesignParameters.StartPoint for IfcAlignmentHorizontalSegment is automatically updated when the
|
||||
# geometric representation is updated because the semantic and geometric data use the same IfcPoint.
|
||||
# This is not the case of IfcAlignmentVerticalSegment and IfcAlignmentCantSegment. For these
|
||||
# segment types, the design parameters of the zero length segment must be updated explicitly.
|
||||
if zero_length_segment.DesignParameters.is_a(
|
||||
"IfcAlignmentVerticalSegment"
|
||||
) or zero_length_segment.DesignParameters.is_a("IfcAlignmentCantSegment"):
|
||||
# get the geometric representation for the new segment
|
||||
mapped_segments = ifcopenshell.api.alignment.get_mapped_segments(segment)
|
||||
mapped_segment = mapped_segments[0] if mapped_segments[1] == None else mapped_segments[1]
|
||||
|
||||
# compute the end point matrix
|
||||
settings = ifcopenshell.geom.settings()
|
||||
segment_fn = ifcopenshell_wrapper.map_shape(settings, mapped_segment.wrapped_data)
|
||||
segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn)
|
||||
e = segment_evaluator.evaluate(segment_fn.end())
|
||||
end = np.array(e)
|
||||
|
||||
# update the zero length segment semantic representation parameters
|
||||
if zero_length_segment.DesignParameters.is_a("IfcAlignmentVerticalSegment"):
|
||||
y = float(end[1, 3]) / unit_scale
|
||||
zero_length_segment.DesignParameters.StartHeight = y
|
||||
dx = float(end[0, 0])
|
||||
dy = float(end[1, 0])
|
||||
zero_length_segment.DesignParameters.StartGradient = dy / dx
|
||||
zero_length_segment.DesignParameters.EndGradient = zero_length_segment.DesignParameters.StartGradient
|
||||
# the length of the current segment is in dist_along, so subtract it out
|
||||
dist_along -= segment.DesignParameters.SegmentLength
|
||||
else:
|
||||
z = float(end[2, 3]) / unit_scale
|
||||
dx = float(end[0, 1])
|
||||
dy = float(end[1, 1])
|
||||
dz = float(end[2, 1])
|
||||
ds = math.sqrt(dx * dx + dy * dy)
|
||||
slope = dz / ds
|
||||
railhead = layout.RailHeadDistance
|
||||
dist_along = segment.DesignParameters.StartDistAlong
|
||||
|
||||
zero_length_segment.DesignParameters.StartCantLeft = z + slope * railhead / 2.0
|
||||
zero_length_segment.DesignParameters.StartCantRight = z - slope * railhead / 2.0
|
||||
|
||||
# updated the referent's name because the referent is now at a new station
|
||||
start_dist_along = 0.0
|
||||
if segment.DesignParameters.is_a("IfcAlignmentHorizontalSegment"):
|
||||
start_dist_along = dist_along + segment.DesignParameters.SegmentLength
|
||||
else:
|
||||
start_dist_along = segment.DesignParameters.StartDistAlong + segment.DesignParameters.HorizontalLength
|
||||
zero_length_segment.DesignParameters.StartDistAlong = start_dist_along
|
||||
|
||||
end_referent = zero_length_segment.IsNestedBy[0].RelatedObjects[0]
|
||||
end_referent.Name = f"{_get_segment_start_point_label(zero_length_segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,start_station+start_dist_along)})"
|
||||
|
||||
# update the referent's geometric representation's location
|
||||
end_referent.ObjectPlacement.RelativePlacement.Location.DistanceAlong.wrappedValue = start_dist_along
|
||||
settings = ifcopenshell.geom.settings()
|
||||
basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment)
|
||||
curve_fn = ifcopenshell_wrapper.map_shape(settings, basis_curve.wrapped_data)
|
||||
curve_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, curve_fn)
|
||||
p = curve_evaluator.evaluate(start_dist_along * unit_scale)
|
||||
p = np.array(p)
|
||||
|
||||
x = float(p[0, 3]) / unit_scale
|
||||
y = float(p[1, 3]) / unit_scale
|
||||
z = float(p[2, 3]) / unit_scale
|
||||
|
||||
rx = float(p[0, 0])
|
||||
ry = float(p[1, 0])
|
||||
rz = float(p[2, 0])
|
||||
|
||||
ax = float(p[0, 2])
|
||||
ay = float(p[1, 2])
|
||||
az = float(p[2, 2])
|
||||
|
||||
end_referent.ObjectPlacement.CartesianPosition.Location.Coordinates = (x, y, z)
|
||||
end_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
|
||||
end_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
|
||||
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
|
||||
end_referent_station = start_station + start_dist_along
|
||||
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=end_referent, name="Pset_Stationing")
|
||||
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": end_referent_station})
|
||||
|
||||
# create the start of segment referent
|
||||
|
||||
# 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
|
||||
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
|
||||
)
|
||||
|
||||
if len(curve.Segments) == 2 and layout.is_a("IfcAlignmentHorizontal"):
|
||||
# this is the first real segment in the horizontal alignment
|
||||
# update the location of the alignment's stationing referent
|
||||
# get the station of the start of the segment
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
stationing_referent = alignment.IsNestedBy[0].RelatedObjects[0]
|
||||
p = curve_evaluator.evaluate(
|
||||
stationing_referent.ObjectPlacement.RelativePlacement.Location.DistanceAlong.wrappedValue
|
||||
)
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
|
||||
station = start_station + dist_along
|
||||
|
||||
# update the zero length layout segment
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
|
||||
|
||||
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
|
||||
zero_length_segment = segment_nest.RelatedObjects[-1]
|
||||
# DesignParameters.StartPoint for IfcAlignmentHorizontalSegment is automatically updated when the
|
||||
# geometric representation is updated because the semantic and geometric data use the same IfcPoint.
|
||||
# This is not the case of IfcAlignmentVerticalSegment and IfcAlignmentCantSegment. For these
|
||||
# segment types, the design parameters of the zero length segment must be updated explicitly.
|
||||
if zero_length_segment.DesignParameters.is_a(
|
||||
"IfcAlignmentVerticalSegment"
|
||||
) or zero_length_segment.DesignParameters.is_a("IfcAlignmentCantSegment"):
|
||||
# get the geometric representation for the new segment
|
||||
mapped_segments = ifcopenshell.api.alignment.get_mapped_segments(segment)
|
||||
mapped_segment = mapped_segments[0] if mapped_segments[1] == None else mapped_segments[1]
|
||||
|
||||
# compute the end point matrix
|
||||
settings = ifcopenshell.geom.settings()
|
||||
segment_fn = ifcopenshell_wrapper.map_shape(settings, mapped_segment.wrapped_data)
|
||||
segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn)
|
||||
e = segment_evaluator.evaluate(segment_fn.end())
|
||||
end = np.array(e)
|
||||
|
||||
# update the zero length segment semantic representation parameters
|
||||
if zero_length_segment.DesignParameters.is_a("IfcAlignmentVerticalSegment"):
|
||||
y = float(end[1, 3]) / unit_scale
|
||||
zero_length_segment.DesignParameters.StartHeight = y
|
||||
dx = float(end[0, 0])
|
||||
dy = float(end[1, 0])
|
||||
zero_length_segment.DesignParameters.StartGradient = dy / dx
|
||||
zero_length_segment.DesignParameters.EndGradient = zero_length_segment.DesignParameters.StartGradient
|
||||
else:
|
||||
z = float(end[2, 3]) / unit_scale
|
||||
dx = float(end[0, 1])
|
||||
dy = float(end[1, 1])
|
||||
dz = float(end[2, 1])
|
||||
ds = math.sqrt(dx * dx + dy * dy)
|
||||
slope = dz / ds
|
||||
railhead = layout.RailHeadDistance
|
||||
|
||||
zero_length_segment.DesignParameters.StartCantLeft = z + slope * railhead / 2.0
|
||||
zero_length_segment.DesignParameters.StartCantRight = z - slope * railhead / 2.0
|
||||
|
||||
# updated the referent's name because the referent is now at a new station
|
||||
start_dist_along = 0.0
|
||||
if segment.DesignParameters.is_a("IfcAlignmentHorizontalSegment"):
|
||||
start_dist_along = dist_along + segment.DesignParameters.SegmentLength
|
||||
else:
|
||||
start_dist_along = segment.DesignParameters.StartDistAlong + segment.DesignParameters.HorizontalLength
|
||||
zero_length_segment.DesignParameters.StartDistAlong = start_dist_along
|
||||
|
||||
end_referent = zero_length_segment.PositionedRelativeTo[0].RelatingPositioningElement
|
||||
end_referent.Name = f"{_get_segment_start_point_label(zero_length_segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,start_station+start_dist_along)})"
|
||||
|
||||
# update the referent's geometric representation's location
|
||||
end_referent.ObjectPlacement.RelativePlacement.Location.DistanceAlong.wrappedValue = start_dist_along
|
||||
settings = ifcopenshell.geom.settings()
|
||||
basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment)
|
||||
curve_fn = ifcopenshell_wrapper.map_shape(settings, basis_curve.wrapped_data)
|
||||
curve_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, curve_fn)
|
||||
p = curve_evaluator.evaluate(start_dist_along * unit_scale)
|
||||
p = np.array(p)
|
||||
|
||||
x = float(p[0, 3]) / unit_scale
|
||||
@@ -200,6 +161,48 @@ def _add_segment_to_layout(file: ifcopenshell.file, layout: entity_instance, seg
|
||||
ay = float(p[1, 2])
|
||||
az = float(p[2, 2])
|
||||
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.Location.Coordinates = (x, y, z)
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
|
||||
end_referent.ObjectPlacement.CartesianPosition.Location.Coordinates = (x, y, z)
|
||||
end_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
|
||||
end_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
|
||||
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
|
||||
end_referent_station = start_station + start_dist_along
|
||||
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=end_referent, name="Pset_Stationing")
|
||||
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": end_referent_station})
|
||||
|
||||
# create the start of segment referent
|
||||
|
||||
# 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 = 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, alignment, distance_along=dist_along, station=station, name=name, positioned_product=segment
|
||||
)
|
||||
|
||||
if len(curve.Segments) == 2 and layout.is_a("IfcAlignmentHorizontal"):
|
||||
# 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)
|
||||
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
|
||||
)
|
||||
p = np.array(p)
|
||||
|
||||
x = float(p[0, 3]) / unit_scale
|
||||
y = float(p[1, 3]) / unit_scale
|
||||
z = float(p[2, 3]) / unit_scale
|
||||
|
||||
rx = float(p[0, 0])
|
||||
ry = float(p[1, 0])
|
||||
rz = float(p[2, 0])
|
||||
|
||||
ax = float(p[0, 2])
|
||||
ay = float(p[1, 2])
|
||||
az = float(p[2, 2])
|
||||
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.Location.Coordinates = (x, y, z)
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
|
||||
stationing_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
|
||||
|
||||
@@ -48,8 +48,9 @@ def _add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance) -
|
||||
if curve:
|
||||
ifcopenshell.api.alignment.add_zero_length_segment(file, curve)
|
||||
|
||||
segment = layout.IsNestedBy[0].RelatedObjects[-1]
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
|
||||
name = f"{_get_segment_start_point_label(segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
|
||||
ifcopenshell.api.alignment.add_stationing_referent(file, segment, 0.0, station, name=name)
|
||||
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
|
||||
segment = segment_nest.RelatedObjects[-1]
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
|
||||
name = f"{_get_segment_start_point_label(segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
|
||||
referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, station, name, segment)
|
||||
|
||||
@@ -30,19 +30,20 @@ import numpy as np
|
||||
|
||||
def add_stationing_referent(
|
||||
file: ifcopenshell.file,
|
||||
element: entity_instance,
|
||||
alignment: entity_instance,
|
||||
distance_along: float,
|
||||
station: float,
|
||||
name: str,
|
||||
positioned_product: entity_instance,
|
||||
) -> entity_instance:
|
||||
"""
|
||||
Adds an IfcReferent to the element with the Pset_Stationing property set.
|
||||
If element is an IfcAlignment, IfcReferent.PredefinedType is set to "STATION", otherwise "POSITION"
|
||||
Adds an IfcReferent to the alignment with the Pset_Stationing property set.
|
||||
|
||||
:param element: the element to receive the referent, expected to be an IfcAlignment or IfcAlignmentSegment
|
||||
:param distance_along: distance along the alignment curve
|
||||
:param alignment: the alignment to receive the referent
|
||||
:param distance_along: distance along the alignment basis curve
|
||||
:param station: station value
|
||||
:param name: name to assign to IfcReferent.Name, typically a stringized version of the station value
|
||||
:param positioned_product: the product whose position is informed by the referent
|
||||
:return: referent
|
||||
|
||||
Example:
|
||||
@@ -50,13 +51,8 @@ def add_stationing_referent(
|
||||
.. code:: python
|
||||
|
||||
alignment = model.by_type("IfcAlignment")[0]
|
||||
ifcopenshell.api.alignment.add_stationing_referent(model,entity=alignment,distance_along=0.0,station=100.0)
|
||||
ifcopenshell.api.alignment.add_stationing_referent(model,alignment=alignment,distance_along=0.0,station=100.0)
|
||||
"""
|
||||
alignment = element
|
||||
|
||||
if element.is_a("IfcAlignmentSegment"):
|
||||
layout = element.Nests[0].RelatingObject
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
|
||||
basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment)
|
||||
|
||||
@@ -76,6 +72,8 @@ def add_stationing_referent(
|
||||
)
|
||||
|
||||
is_valid_curve = True
|
||||
if basis_curve.is_a("IfcCompositeCurve") and len(basis_curve.Segments) == 0:
|
||||
is_valid_curve = False
|
||||
if basis_curve.is_a("IfcPolyline") and len(basis_curve.Points) < 2:
|
||||
is_valid_curve = False
|
||||
elif basis_curve.is_a("IfcIndexedPolyCurve") and len(basis_curve.Points.CoordList) < 2:
|
||||
@@ -91,6 +89,7 @@ def add_stationing_referent(
|
||||
fn = ifcopenshell_wrapper.convert_loop_to_function_item(fn)
|
||||
|
||||
evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, fn)
|
||||
|
||||
p = evaluator.evaluate(distance_along * unit_scale)
|
||||
p = np.array(p)
|
||||
|
||||
@@ -105,12 +104,23 @@ def add_stationing_referent(
|
||||
ax = float(p[0, 2])
|
||||
ay = float(p[1, 2])
|
||||
az = float(p[2, 2])
|
||||
else:
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
z = 0.0
|
||||
rx = 1.0
|
||||
ry = 0.0
|
||||
rz = 0.0
|
||||
ax = 0.0
|
||||
ay = 0.0
|
||||
az = 1.0
|
||||
|
||||
object_placement.CartesianPosition = file.createIfcAxis2Placement3D(
|
||||
Location=file.createIfcCartesianPoint((x, y, z)),
|
||||
Axis=file.createIfcDirection((ax, ay, az)),
|
||||
RefDirection=file.createIfcDirection((rx, ry, rz)),
|
||||
)
|
||||
|
||||
object_placement.CartesianPosition = file.createIfcAxis2Placement3D(
|
||||
Location=file.createIfcCartesianPoint((x, y, z)),
|
||||
Axis=file.createIfcDirection((ax, ay, az)),
|
||||
RefDirection=file.createIfcDirection((rx, ry, rz)),
|
||||
)
|
||||
# this commented out code is what you would do to add a geometric representation of the referent
|
||||
# the example is a circle. a better way would be to pass a representation into the function
|
||||
# representation = file.create_entity(
|
||||
@@ -120,7 +130,6 @@ def add_stationing_referent(
|
||||
# )
|
||||
|
||||
# create referent for the station
|
||||
predefined_type = "STATION" if element.is_a("IfcAlignment") else "POSITION"
|
||||
referent = file.createIfcReferent(
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=None,
|
||||
@@ -129,21 +138,27 @@ def add_stationing_referent(
|
||||
ObjectType=None,
|
||||
ObjectPlacement=object_placement,
|
||||
Representation=representation,
|
||||
PredefinedType=predefined_type,
|
||||
PredefinedType="STATION",
|
||||
)
|
||||
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)
|
||||
|
||||
if len(alignment.Positions) == 0:
|
||||
nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
nest.RelatedObjects += (referent,)
|
||||
|
||||
nest.RelatedObjects = sorted(
|
||||
nest.RelatedObjects, key=lambda x: ifcopenshell.util.element.get_pset(x, name="Pset_Stationing", prop="Station")
|
||||
)
|
||||
|
||||
if len(referent.Positions) == 0:
|
||||
rel_positions = file.createIfcRelPositions(
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatingPositioningElement=alignment,
|
||||
RelatingPositioningElement=referent,
|
||||
RelatedProducts=[
|
||||
referent,
|
||||
positioned_product,
|
||||
],
|
||||
)
|
||||
else:
|
||||
alignment.Positions[0].RelatedProducts += (referent,)
|
||||
referent.Positions[0].RelatedProducts += (positioned_product,)
|
||||
|
||||
return referent
|
||||
|
||||
@@ -50,9 +50,21 @@ def _move_vertical_layout_to_child_alignment(
|
||||
# nest the vertical layout onto the child alignment
|
||||
ifcopenshell.api.nest.assign_object(file, related_objects=[vertical_layout], relating_object=child_alignment)
|
||||
|
||||
# aggreage the child alignment to the parent 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:
|
||||
|
||||
@@ -231,8 +231,11 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
|
||||
|
||||
if include_referent:
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
|
||||
station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
|
||||
name = f"{_get_segment_start_point_label(zero_length_curve_segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
|
||||
ifcopenshell.api.alignment.add_stationing_referent(file, zero_length_curve_segment, 0.0, station, name=name)
|
||||
referent = ifcopenshell.api.alignment.add_stationing_referent(
|
||||
file, alignment, 0.0, station, name, zero_length_curve_segment
|
||||
)
|
||||
referent.Description = f"Positions zero length segment {zero_length_curve_segment.id()}"
|
||||
|
||||
return True
|
||||
|
||||
@@ -33,27 +33,31 @@ def create(
|
||||
name: str,
|
||||
include_vertical: bool = False,
|
||||
include_cant: bool = False,
|
||||
include_geometry: bool = True,
|
||||
start_station: float = 0.0,
|
||||
) -> entity_instance:
|
||||
"""
|
||||
Creates a new IfcAlignment with an IfcRelNests nesting an IfcReferent (for stationing) and IfcAlignmentHorizontal. The nest relationship can optionally
|
||||
include IfcAlignmentVertical and IfcAlignmentCant. Geometric representations for the alignment layouts (IfcCompositeCurve,
|
||||
IfcGradientCurve, IfcSegmentedReferenceCurve) are created as well.
|
||||
Creates a new alignment with a horizontal layout. Optionally, vertical and cant layouts can be created as well.
|
||||
The geometric representations are created as well, unless they are explicitly excluded.
|
||||
Zero length segments are added at the end of the layouts and geometric representations.
|
||||
The alignment is automatically aggreated to the project if it exists.
|
||||
|
||||
Zero length segments are added at the end.
|
||||
Use get_horizontal_layout(alignment), get_vertical_layout(alignment) and get_cant_layout(alignment) to get the
|
||||
corresponding IfcAlignmentHorizontal, IfcAlignmentVertical, and IfcAlignmentCant layout entities.
|
||||
|
||||
The IfcAlignment is aggreated to IfcProject
|
||||
If the alignment has Viennese Bend transition curves, create the segments in the cant layout before the horizontal layout using create_layout_segment().
|
||||
The horizontal geometry in the Viennese Bend transition curves depends on the Viennese Bend cant parameters. create_layout_segment() automatically creates
|
||||
the geometric representation from the semantic definition. The horizontal segment geometric representation will fail if the cant segment is not defined.
|
||||
|
||||
Use get_horizontal_layout(alignment) to get the IfcAlignmentHorizontal layout.
|
||||
|
||||
If the alignment has Viennese Bend transition curves, create the cant layout before the horizontal layout. This is because the horizontal layout
|
||||
in the Viennese Bend transition curves depends on the Viennese Bend cant parameters.
|
||||
If geometric representations are created, the alignment stationing referent is also created using the start_station value. IfcReferent.ObjectPlacement
|
||||
is required for linear positiion elements and IfcLinearPlacement is defined relative to alignment curve geometry.
|
||||
|
||||
:param file:
|
||||
:param name: name assigned to IfcAlignment.Name
|
||||
:param include_vertical: If True, IfcAlignmentVertical and IfcGradientCurve are created
|
||||
:param include_cant: If True, IfcAlignmentCant and IfcSegmentedReferenceCurve are created
|
||||
:param start_station: station value at the start of the alignment
|
||||
:param include_vertical: If True, IfcAlignmentVertical is created. IfcGradientCurve is created if include_geometry is True
|
||||
:param include_cant: If True, IfcAlignmentCant is created. IfcSegmentedReferenceCurve is created if include_geometry is True
|
||||
:param include_geometry: If True, the geometric representations are added
|
||||
:param start_station: station value at the start of the alignment.
|
||||
:return: Returns an IfcAlignment
|
||||
"""
|
||||
alignment = file.createIfcAlignment(
|
||||
@@ -73,16 +77,17 @@ def create(
|
||||
|
||||
ifcopenshell.api.nest.assign_object(file, related_objects=alignment_layouts, relating_object=alignment)
|
||||
|
||||
_create_geometric_representation(file, alignment)
|
||||
if include_geometry:
|
||||
_create_geometric_representation(file, alignment)
|
||||
|
||||
name = ifcopenshell.util.alignment.station_as_string(file, start_station)
|
||||
referent = ifcopenshell.api.alignment.add_stationing_referent(
|
||||
file, alignment, 0.0, start_station, name, alignment
|
||||
)
|
||||
|
||||
for layout in alignment_layouts:
|
||||
_add_zero_length_segment(file, layout)
|
||||
|
||||
# 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
|
||||
project = file.by_type("IfcProject")[0]
|
||||
if project:
|
||||
|
||||
@@ -53,11 +53,6 @@ def create_as_offset_curve(
|
||||
|
||||
_create_offset_curve_representation(file, alignment, offsets)
|
||||
|
||||
# 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
|
||||
project = file.by_type("IfcProject")[0]
|
||||
if project:
|
||||
|
||||
@@ -137,14 +137,11 @@ def create_as_polyline(
|
||||
Name=name,
|
||||
)
|
||||
|
||||
# _create_layout(file,alignment,points)
|
||||
|
||||
_create_polyline_representation(file, alignment, points)
|
||||
|
||||
# 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)
|
||||
referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, start_station, name, alignment)
|
||||
|
||||
# IFC 4.1.4.1.1 Alignment Aggregation To Project
|
||||
project = file.by_type("IfcProject")[0]
|
||||
|
||||
@@ -23,20 +23,21 @@ from ifcopenshell import entity_instance
|
||||
import math
|
||||
from ifcopenshell import ifcopenshell_wrapper
|
||||
import numpy as np
|
||||
from typing import Union
|
||||
|
||||
from ifcopenshell.api.alignment._add_segment_to_layout import _add_segment_to_layout
|
||||
|
||||
|
||||
def create_layout_segment(
|
||||
file: ifcopenshell.file, layout: entity_instance, design_parameters: entity_instance
|
||||
) -> np.array:
|
||||
) -> Union[np.array, None]:
|
||||
"""
|
||||
Creates a new IfcAlignmentSegment using the IfcAlignmentParameterSegment design parameters.
|
||||
The new segment is appended to the layout alignment and the corresponding IfcCurveSegment is created in the geometric representation
|
||||
The new segment is appended to the layout alignment and the corresponding IfcCurveSegment is created in the geometric representation if it exists.
|
||||
|
||||
:param layout: The layout to receive the new layout segment. This parameter is expected to be IfcAlignmentHorizontal, IfcAlignmentVertical or IfcAlignmentCant
|
||||
:param design_parameters: The parameters defining the segment. Expected to be the appropreate subclass of IfcAlignmentParameterSegment
|
||||
:return: 4x4 matrix at end of segment as np.array intended to be used as the start point geometry for the next segment.
|
||||
:return: 4x4 matrix at end of segment as np.array intended to be used as the start point geometry for the next segment or None if there is the geometric representation is not defined.
|
||||
"""
|
||||
expected_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant"]
|
||||
if not layout.is_a() in expected_types:
|
||||
@@ -60,25 +61,28 @@ def create_layout_segment(
|
||||
alignment = ifcopenshell.api.alignment.get_alignment(layout)
|
||||
curve = ifcopenshell.api.alignment.get_curve(alignment)
|
||||
|
||||
if layout.is_a("IfcAlignmentHorizontal"):
|
||||
if curve.is_a("IfcGradientCurve"):
|
||||
curve = curve.BaseCurve
|
||||
elif curve.is_a("IfcSegmentedReferenceCurve"):
|
||||
curve = (
|
||||
curve.BaseCurve.BaseCurve
|
||||
) # layout is horizontal and curve is segmented ref ... we want the curve's base curve
|
||||
elif layout.is_a("IfcAlignmentVertical"):
|
||||
if curve.is_a("IfcSegmentedReferenceCurve"):
|
||||
curve = curve.BaseCurve
|
||||
if curve:
|
||||
if layout.is_a("IfcAlignmentHorizontal"):
|
||||
if curve.is_a("IfcGradientCurve"):
|
||||
curve = curve.BaseCurve
|
||||
elif curve.is_a("IfcSegmentedReferenceCurve"):
|
||||
curve = (
|
||||
curve.BaseCurve.BaseCurve
|
||||
) # layout is horizontal and curve is segmented ref ... we want the curve's base curve
|
||||
elif layout.is_a("IfcAlignmentVertical"):
|
||||
if curve.is_a("IfcSegmentedReferenceCurve"):
|
||||
curve = curve.BaseCurve
|
||||
|
||||
# the new segment is two from the end... the end segment is zero length
|
||||
curve_segment = curve.Segments[-2]
|
||||
# the new segment is two from the end... the end segment is zero length
|
||||
curve_segment = curve.Segments[-2]
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings = ifcopenshell.geom.settings()
|
||||
|
||||
segment_fn = ifcopenshell_wrapper.map_shape(settings, curve_segment.wrapped_data)
|
||||
segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn)
|
||||
e = segment_evaluator.evaluate(segment_fn.end())
|
||||
end = np.array(e)
|
||||
segment_fn = ifcopenshell_wrapper.map_shape(settings, curve_segment.wrapped_data)
|
||||
segment_evaluator = ifcopenshell_wrapper.function_item_evaluator(settings, segment_fn)
|
||||
e = segment_evaluator.evaluate(segment_fn.end())
|
||||
end = np.array(e)
|
||||
|
||||
return end
|
||||
return end
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -50,5 +50,6 @@ def create_representation(
|
||||
layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment)
|
||||
for layout in layouts:
|
||||
curve = ifcopenshell.api.alignment.get_layout_curve(layout)
|
||||
for segment in layout.IsNestedBy[0].RelatedObjects:
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
|
||||
for segment in layout_nest.RelatedObjects:
|
||||
_add_segment_to_curve(file, segment, curve)
|
||||
|
||||
@@ -43,6 +43,6 @@ def distance_along_from_station(file: ifcopenshell.file, alignment: entity_insta
|
||||
print(dist_along) # 100.00
|
||||
"""
|
||||
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
|
||||
dist_along = station - start_station
|
||||
return dist_along
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
# 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,37 @@
|
||||
# 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,34 @@
|
||||
# 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
|
||||
+8
-7
@@ -22,11 +22,11 @@ import ifcopenshell.util.element
|
||||
from ifcopenshell import entity_instance
|
||||
|
||||
|
||||
def get_alignment_station(file: ifcopenshell.file, alignment: entity_instance) -> float:
|
||||
def get_alignment_start_station(file: ifcopenshell.file, alignment: entity_instance) -> float:
|
||||
"""
|
||||
Returns the start station of the alignment. If the alignment is nested by an IfcReferent
|
||||
the referent is checked for PredefinedType of STATION and an occurance of Pset_Stationing.Station,
|
||||
otherwise start station is taken to be 0.0.
|
||||
Returns the start station of the alignment. The starting station is defined by the first nested IfcReferent.
|
||||
This is interpreted to mean the first IfcReferent with an occurance of Pset_Stationing.Station,
|
||||
otherwise returns 0.0.
|
||||
"""
|
||||
|
||||
if not alignment.is_a("IfcAlignment"):
|
||||
@@ -36,12 +36,13 @@ def get_alignment_station(file: ifcopenshell.file, alignment: entity_instance) -
|
||||
|
||||
parent_alignment = ifcopenshell.api.alignment.get_parent_alignment(alignment)
|
||||
if parent_alignment:
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_station(file, parent_alignment)
|
||||
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, parent_alignment)
|
||||
else:
|
||||
components = ifcopenshell.util.element.get_components(alignment)
|
||||
for c in components:
|
||||
if c.is_a("IfcReferent") and ifcopenshell.util.element.get_predefined_type(c) == "STATION":
|
||||
if c.is_a("IfcReferent"):
|
||||
start_station = ifcopenshell.util.element.get_pset(c, name="Pset_Stationing", prop="Station")
|
||||
break
|
||||
if not start_station == None:
|
||||
break
|
||||
|
||||
return start_station
|
||||
@@ -0,0 +1,40 @@
|
||||
# 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, alignment: entity_instance) -> entity_instance:
|
||||
"""
|
||||
Searches for the IfcRelNest that contains IfcReferent. If one is not found, a empty IfcRelNests is created.
|
||||
|
||||
:param file:
|
||||
:param alignment: The IfcAlignment which hosts IfcReferent
|
||||
:return: Returns the IfcRelNests.
|
||||
"""
|
||||
if not alignment.is_a("IfcAlignment"):
|
||||
raise TypeError(f"Expected IfcAlignment, instead received {alignment.is_a()}")
|
||||
|
||||
for nest in alignment.IsNestedBy:
|
||||
for related_object in nest.RelatedObjects:
|
||||
if related_object.is_a("IfcReferent"):
|
||||
return nest
|
||||
|
||||
nest = file.createIfcRelNests(GlobalId=ifcopenshell.guid.new(), RelatingObject=alignment, RelatedObjects=[])
|
||||
return nest
|
||||
@@ -162,3 +162,12 @@ def print_composite_curve_deep(curve):
|
||||
print(" " * 4, segment.Placement)
|
||||
print(" " * 4, segment.Placement.Location)
|
||||
print(" " * 4, segment.Placement.RefDirection)
|
||||
|
||||
|
||||
def print_positioned_products(file: ifcopenshell.file):
|
||||
referents = file.by_type("IfcReferent")
|
||||
for referent in referents:
|
||||
print(referent)
|
||||
for rel in referent.Positions:
|
||||
for product in rel.RelatedProducts:
|
||||
print(" " * 2, product)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
import inspect
|
||||
import random
|
||||
import operator
|
||||
import warnings
|
||||
@@ -225,10 +226,24 @@ def serialize_shape(shape):
|
||||
shapes.SetFormatNb(2)
|
||||
|
||||
shapes.Add(shape)
|
||||
|
||||
# Check if WriteToString method exists and has the correct signature
|
||||
# In PythonOCC >= 7.8.0, WriteToString signature changed and requires additional arguments
|
||||
if hasattr(shapes, "WriteToString"):
|
||||
return shapes.WriteToString()
|
||||
else:
|
||||
return shapes.Write()
|
||||
try:
|
||||
# Try to get the method signature
|
||||
sig = inspect.signature(shapes.WriteToString)
|
||||
# If WriteToString has no parameters (just self), use it
|
||||
# This works for PythonOCC < 7.8.0
|
||||
if len(sig.parameters) == 0:
|
||||
return shapes.WriteToString()
|
||||
except (ValueError, TypeError):
|
||||
# If signature inspection fails, fall through to Write() method
|
||||
pass
|
||||
|
||||
# Fall back to Write() method for newer PythonOCC versions (>= 7.8.0)
|
||||
# or when WriteToString is not available/compatible
|
||||
return shapes.Write()
|
||||
|
||||
|
||||
def create_shape_from_serialization(
|
||||
|
||||
@@ -68,13 +68,10 @@ def test_add_segment_to_layout():
|
||||
_add_segment_to_layout(file, horizontal_alignment, alignment_segment)
|
||||
|
||||
assert len(horizontal_alignment.IsNestedBy) == 1
|
||||
assert (
|
||||
len(horizontal_alignment.IsNestedBy[0].RelatedObjects) == 2
|
||||
) # The the segment we added and the automatically created zero length segment
|
||||
assert horizontal_alignment.IsNestedBy[0].RelatedObjects[0] == alignment_segment
|
||||
assert (
|
||||
alignment_segment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
|
||||
) # a referent is automatically added at the start of the segment
|
||||
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(horizontal_alignment)
|
||||
assert len(segment_nest.RelatedObjects) == 2
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
assert len(referent_nest.RelatedObjects) == 3
|
||||
|
||||
|
||||
test_add_segment_to_layout()
|
||||
|
||||
@@ -39,14 +39,14 @@ def test_add_stationing_to_alignment():
|
||||
|
||||
alignment = ifcopenshell.api.alignment.create(file, "TestAlignment", start_station=2000.0)
|
||||
|
||||
for rel in alignment.IsNestedBy:
|
||||
for referent in rel.RelatedObjects:
|
||||
if referent.is_a("IfcReferent"):
|
||||
assert referent.PredefinedType == "STATION"
|
||||
assert referent.Name == "2+000.000"
|
||||
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing")
|
||||
assert (
|
||||
ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing", prop="Station")
|
||||
== 2000.0
|
||||
)
|
||||
assert referent.ObjectPlacement != None
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
referent = referent_nest.RelatedObjects[0]
|
||||
|
||||
assert referent.PredefinedType == "STATION"
|
||||
assert referent.Name == "2+000.000"
|
||||
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing")
|
||||
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing", prop="Station") == 2000.0
|
||||
assert referent.ObjectPlacement != None
|
||||
|
||||
|
||||
test_add_stationing_to_alignment()
|
||||
|
||||
@@ -32,23 +32,25 @@ 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
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
|
||||
assert len(layout_nest.RelatedObjects) == 1
|
||||
assert layout_nest.RelatedObjects[0].is_a("IfcAlignmentHorizontal")
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
assert len(referent_nest.RelatedObjects) == 2
|
||||
assert referent_nest.RelatedObjects[0].is_a("IfcReferent")
|
||||
|
||||
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(layout_nest.RelatedObjects) == 2
|
||||
assert layout_nest.RelatedObjects[0].is_a("IfcAlignmentHorizontal")
|
||||
assert layout_nest.RelatedObjects[1].is_a("IfcAlignmentVertical")
|
||||
|
||||
curve = ifcopenshell.api.alignment.get_curve(alignment)
|
||||
assert curve.is_a("IfcGradientCurve")
|
||||
|
||||
@@ -56,20 +58,18 @@ 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")
|
||||
assert len(child_alignment.IsNestedBy) == 1 # one nesting relationship for the IfcAlignmentVertical
|
||||
assert len(child_alignment.IsNestedBy[0].RelatedObjects) == 1 # The IfcAlignmentVertical
|
||||
assert child_alignment.IsNestedBy[0].RelatedObjects[0].is_a("IfcAlignmentVertical")
|
||||
assert len(child_alignment.IsNestedBy) == 2
|
||||
child_layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(child_alignment)
|
||||
assert len(child_layout_nest.RelatedObjects) == 1 # The IfcAlignmentVertical
|
||||
assert child_layout_nest.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(layout_nest.RelatedObjects) == 1
|
||||
assert layout_nest.RelatedObjects[0].is_a("IfcAlignmentHorizontal")
|
||||
|
||||
|
||||
test_add_vertical_alignment()
|
||||
|
||||
@@ -46,22 +46,21 @@ 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
|
||||
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
|
||||
assert len(layout_nest.RelatedObjects) == 2
|
||||
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
assert len(referent_nest.RelatedObjects) == 19
|
||||
|
||||
horizontal_layout = ifcopenshell.api.alignment.get_horizontal_layout(alignment)
|
||||
horizontal_segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(horizontal_layout)
|
||||
assert len(horizontal_segment_nest.RelatedObjects) == 8
|
||||
|
||||
vertical_layout = ifcopenshell.api.alignment.get_vertical_layout(alignment)
|
||||
vertical_segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(vertical_layout)
|
||||
assert len(vertical_segment_nest.RelatedObjects) == 10
|
||||
|
||||
|
||||
test_create_by_pi_method()
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# 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 pytest
|
||||
import ifcopenshell.api.alignment
|
||||
import ifcopenshell.api.context
|
||||
import ifcopenshell.api.unit
|
||||
|
||||
|
||||
def test_create_no_geometry():
|
||||
file = ifcopenshell.file(schema="IFC4X3_ADD2")
|
||||
project = file.createIfcProject(GlobalId=ifcopenshell.guid.new(), Name="Test")
|
||||
length = ifcopenshell.api.unit.add_si_unit(file, unit_type="LENGTHUNIT")
|
||||
ifcopenshell.api.unit.assign_unit(file, units=[length])
|
||||
|
||||
# creates an IfcAlignment with an IfcAlignmentHorizontal layout containing only the zero length segment
|
||||
ali = ifcopenshell.api.alignment.create(file, "A1", include_vertical=True, include_geometry=False)
|
||||
|
||||
# append a segment to the horizontal layout
|
||||
horizontal_alignment = ifcopenshell.api.alignment.get_horizontal_layout(ali)
|
||||
vertical_alignment = ifcopenshell.api.alignment.get_vertical_layout(ali)
|
||||
|
||||
curve = ifcopenshell.api.alignment.get_curve(ali)
|
||||
assert curve == None
|
||||
|
||||
design_parameters = file.create_entity(
|
||||
type="IfcAlignmentHorizontalSegment",
|
||||
StartTag=None,
|
||||
EndTag=None,
|
||||
StartPoint=file.createIfcCartesianPoint(Coordinates=((0.0, 0.0))),
|
||||
StartDirection=0.0,
|
||||
StartRadiusOfCurvature=0.0,
|
||||
EndRadiusOfCurvature=0.0,
|
||||
SegmentLength=100.0,
|
||||
GravityCenterLineHeight=None,
|
||||
PredefinedType="LINE",
|
||||
)
|
||||
end = ifcopenshell.api.alignment.create_layout_segment(file, horizontal_alignment, design_parameters)
|
||||
assert end == None
|
||||
|
||||
design_parameters = file.createIfcAlignmentVerticalSegment(
|
||||
StartDistAlong=0.0,
|
||||
HorizontalLength=50.0,
|
||||
StartHeight=20.0,
|
||||
StartGradient=1.0 / 100.0,
|
||||
EndGradient=1.0 / 100.0,
|
||||
PredefinedType="CONSTANTGRADIENT",
|
||||
)
|
||||
end = ifcopenshell.api.alignment.create_layout_segment(file, vertical_alignment, design_parameters)
|
||||
assert end == None
|
||||
|
||||
|
||||
test_create_no_geometry()
|
||||
@@ -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()
|
||||
|
||||
@@ -45,12 +45,15 @@ def test_name_segments():
|
||||
file, "TestAlignment", coordinates, radii, vpoints, lengths
|
||||
)
|
||||
|
||||
for rel in alignment.IsNestedBy:
|
||||
for a in rel.RelatedObjects:
|
||||
if a.is_a("IfcLinearElement"):
|
||||
ifcopenshell.api.alignment.name_segments("Q", a)
|
||||
i = 1
|
||||
for sr in a.IsNestedBy:
|
||||
for s in sr.RelatedObjects:
|
||||
assert f"Q{i}" == s.Name
|
||||
i += 1
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
|
||||
for layout in layout_nest.RelatedObjects:
|
||||
assert layout.is_a("IfcLinearElement")
|
||||
ifcopenshell.api.alignment.name_segments("Q", layout)
|
||||
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
|
||||
i = 1
|
||||
for segment in segment_nest.RelatedObjects:
|
||||
assert f"Q{i}" == segment.Name
|
||||
i += 1
|
||||
|
||||
|
||||
test_name_segments()
|
||||
|
||||
@@ -97,32 +97,16 @@ def callback_alignment():
|
||||
|
||||
|
||||
def test_with_default_names(default_names_alignment):
|
||||
hlayout = ifcopenshell.api.alignment.get_horizontal_layout(default_names_alignment)
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(None, default_names_alignment)
|
||||
|
||||
assert "P.O.B." in hlayout.IsNestedBy[0].RelatedObjects[0].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "P.C." in hlayout.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "P.T." in hlayout.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "P.O.E." in hlayout.IsNestedBy[0].RelatedObjects[-1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
|
||||
vlayout = ifcopenshell.api.alignment.get_vertical_layout(default_names_alignment)
|
||||
|
||||
assert "V.P.O.B." in vlayout.IsNestedBy[0].RelatedObjects[0].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "P.V.C." in vlayout.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "P.V.T." in vlayout.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "V.P.O.E." in vlayout.IsNestedBy[0].RelatedObjects[-1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
expected = ["P.O.B", "P.C.", "P.T.", "P.O.E.", "V.P.O.B.", "P.V.C.", "P.V.T.", "V.P.O.E"]
|
||||
for r in referent_nest.RelatedObjects:
|
||||
assert [x in r.Name for x in expected]
|
||||
|
||||
|
||||
def test_with_callbacks(callback_alignment):
|
||||
hlayout = ifcopenshell.api.alignment.get_horizontal_layout(callback_alignment)
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(None, callback_alignment)
|
||||
|
||||
assert "A" in hlayout.IsNestedBy[0].RelatedObjects[0].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "Q" in hlayout.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "Q" in hlayout.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "Z" in hlayout.IsNestedBy[0].RelatedObjects[-1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
|
||||
vlayout = ifcopenshell.api.alignment.get_vertical_layout(callback_alignment)
|
||||
|
||||
assert "a" in vlayout.IsNestedBy[0].RelatedObjects[0].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "q" in vlayout.IsNestedBy[0].RelatedObjects[1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "q" in vlayout.IsNestedBy[0].RelatedObjects[2].IsNestedBy[0].RelatedObjects[0].Name
|
||||
assert "z" in vlayout.IsNestedBy[0].RelatedObjects[-1].IsNestedBy[0].RelatedObjects[0].Name
|
||||
expected = ["A", "Q", "Z", "a", "q", "z"]
|
||||
for r in referent_nest.RelatedObjects:
|
||||
assert [x in r.Name for x in expected]
|
||||
|
||||
@@ -58,22 +58,17 @@ 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
|
||||
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_layout_nest(alignment)
|
||||
assert len(layout_nest.RelatedObjects) == 2
|
||||
|
||||
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
|
||||
assert len(referent_nest.RelatedObjects) == 6
|
||||
|
||||
segment_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(vlayout)
|
||||
assert len(segment_nest.RelatedObjects) == 3
|
||||
|
||||
|
||||
test_vertical_layout_by_pi_method()
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||
FILE_NAME('','2025-02-14T16:16:53',(''),(''),'IfcOpenShell 0.8.1-c49ca69','IfcOpenShell 0.8.1-c49ca69','');
|
||||
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON($,$,'Pravin.Muthukrishnan',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION($,'BG&E Pty Ltd','BG&E Pty Ltd',$,$);
|
||||
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#4=IFCAPPLICATION(#2,'15.0C1n','12d Model','12d Model');
|
||||
#5=IFCOWNERHISTORY(#3,#4,$,.NOCHANGE.,$,$,$,1739508662);
|
||||
#6=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#7=IFCDIRECTION((0.,0.,1.));
|
||||
#8=IFCDIRECTION((1.,0.,0.));
|
||||
#9=IFCAXIS2PLACEMENT3D(#6,#7,#8);
|
||||
#10=IFCDIRECTION((0.,1.,0.));
|
||||
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-06,#9,#10);
|
||||
#12=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#14=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#15=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#16=IFCUNITASSIGNMENT((#12,#13,#14,#15));
|
||||
#17=IFCPROJECT('3f4tUCcZz5OBp_RAlF3Yjd',#5,'DRAINAGE DESIGN','No description set',$,$,$,(#11),#16);
|
||||
#18=IFCPERSON($,$,'Pravin.Muthukrishnan',$,$,$,$,$);
|
||||
#19=IFCORGANIZATION($,'BG&E Pty Ltd','BG&E Pty Ltd',$,$);
|
||||
#20=IFCPERSONANDORGANIZATION(#18,#19,$);
|
||||
#21=IFCAPPLICATION(#19,'15.0C1n','12d Model','12d Model');
|
||||
#22=IFCOWNERHISTORY(#20,#21,$,.NOCHANGE.,$,$,$,1739508662);
|
||||
#30=IFCCARTESIANPOINT((0.,0.));
|
||||
#31=IFCAXIS2PLACEMENT2D(#30,$);
|
||||
#32=IFCCIRCLEPROFILEDEF(.AREA.,$,#31,0.179);
|
||||
#33=IFCCARTESIANPOINT((289.080,582.2,118.886));
|
||||
#34=IFCCARTESIANPOINT((290.081,582.9,118.886));
|
||||
#35=IFCPOLYLINE((#33,#34));
|
||||
#36=IFCDIRECTION((1.,0.,0.));
|
||||
#37=IFCFIXEDREFERENCESWEPTAREASOLID(#32,$,#35,$,$,#36);
|
||||
#38=IFCSHAPEREPRESENTATION(#11,'Body','AdvancedSweptSolid',(#37));
|
||||
#39=IFCPRODUCTDEFINITIONSHAPE($,$,(#38));
|
||||
#40=IFCFLOWSEGMENT('1iJOpv7cX1SBoQLaHDDolV',#108,'5','5-1 to 1-1','EXISTING (RETAINED)',#103,#39,$);
|
||||
#41=IFCRELDEFINESBYPROPERTIES('10rIwnj8r8RR3PKc1TXqSB',#22,'12d Model','Attributes',(#40),#94);
|
||||
#42=IFCPROPERTYSINGLEVALUE('calculated critical cover chainage',$,IFCREAL(2.24873590275508),$);
|
||||
#43=IFCPROPERTYSINGLEVALUE('nominal diameter',$,IFCREAL(0.3),$);
|
||||
#44=IFCPROPERTYSINGLEVALUE('pipe id',$,IFCINTEGER(35),$);
|
||||
#45=IFCPROPERTYSINGLEVALUE('colour',$,IFCLABEL('orange'),$);
|
||||
#46=IFCPROPERTYSINGLEVALUE('group',$,IFCLABEL('EX'),$);
|
||||
#47=IFCCOMPLEXPROPERTY('pipe sched','12d_attribute_group',$,(#46));
|
||||
#48=IFCPROPERTYSINGLEVALUE('name',$,IFCLABEL('EX'),$);
|
||||
#49=IFCCOMPLEXPROPERTY('lplot','12d_attribute_group',$,(#48));
|
||||
#50=IFCPROPERTYSINGLEVALUE('pipe name',$,IFCLABEL('5-1 to 1-1'),$);
|
||||
#51=IFCPROPERTYSINGLEVALUE('calculated direct pipe flow total minor',$,IFCREAL(0.),$);
|
||||
#52=IFCPROPERTYSINGLEVALUE('calculated direct pipe flow total major',$,IFCREAL(0.),$);
|
||||
#53=IFCPROPERTYSINGLEVALUE('calculated direct pit and pipe flow total minor',$,IFCREAL(0.),$);
|
||||
#54=IFCPROPERTYSINGLEVALUE('calculated direct pit and pipe flow total major',$,IFCREAL(0.),$);
|
||||
#55=IFCPROPERTYSINGLEVALUE('pipe type',$,IFCLABEL('EXISTING (RETAINED)'),$);
|
||||
#56=IFCPROPERTYSINGLEVALUE('roughness text',$,IFCLABEL('n=0.013'),$);
|
||||
#57=IFCPROPERTYSINGLEVALUE('calculated design grade minimum',$,IFCREAL(1.),$);
|
||||
#58=IFCPROPERTYSINGLEVALUE('calculated pipe length',$,IFCREAL(7.19890895752825),$);
|
||||
#59=IFCPROPERTYSINGLEVALUE('invert us',$,IFCREAL(118.736),$);
|
||||
#60=IFCPROPERTYSINGLEVALUE('invert ds',$,IFCREAL(118.736),$);
|
||||
#61=IFCPROPERTYSINGLEVALUE('calculated pipe grade',$,IFCREAL(-0.),$);
|
||||
#62=IFCPROPERTYSINGLEVALUE('calculated pipe vert angle',$,IFCREAL(0.),$);
|
||||
#63=IFCPROPERTYSINGLEVALUE('calculated pipe grade 1 in',$,IFCREAL(1000000.),$);
|
||||
#64=IFCPROPERTYSINGLEVALUE('pipe size',$,IFCLABEL('300'),$);
|
||||
#65=IFCPROPERTYSINGLEVALUE('diameter',$,IFCREAL(0.3),$);
|
||||
#66=IFCPROPERTYSINGLEVALUE('nominal pipe size',$,IFCLABEL('300'),$);
|
||||
#67=IFCPROPERTYSINGLEVALUE('calculated us deflection',$,IFCREAL(0.),$);
|
||||
#68=IFCPROPERTYSINGLEVALUE('calculated ds deflection',$,IFCREAL(89.4496469263205),$);
|
||||
#69=IFCPROPERTYSINGLEVALUE('calculated design drop',$,IFCREAL(0.02),$);
|
||||
#70=IFCPROPERTYSINGLEVALUE('calculated drop',$,IFCREAL(0.),$);
|
||||
#71=IFCPROPERTYSINGLEVALUE('calculated pipe vert ds deflection angle',$,IFCREAL(-4.09980829540645),$);
|
||||
#72=IFCPROPERTYSINGLEVALUE('calculated design cover',$,IFCREAL(1.1),$);
|
||||
#73=IFCPROPERTYSINGLEVALUE('minimum cover',$,IFCREAL(1.67561504823626),$);
|
||||
#74=IFCPROPERTYSINGLEVALUE('pipe colour',$,IFCINTEGER(8),$);
|
||||
#75=IFCPROPERTYSINGLEVALUE('conduit shape',$,IFCLABEL('Circular'),$);
|
||||
#76=IFCPROPERTYSINGLEVALUE('calculated invert us',$,IFCREAL(118.736),$);
|
||||
#77=IFCPROPERTYSINGLEVALUE('calculated invert ds',$,IFCREAL(118.736),$);
|
||||
#78=IFCPROPERTYSINGLEVALUE('calculated hgl us',$,IFCREAL(118.736),$);
|
||||
#79=IFCPROPERTYSINGLEVALUE('calculated hgl ds',$,IFCREAL(118.736),$);
|
||||
#80=IFCPROPERTYSINGLEVALUE('lock us il',$,IFCINTEGER(1),$);
|
||||
#81=IFCPROPERTYSINGLEVALUE('lock ds il',$,IFCINTEGER(1),$);
|
||||
#82=IFCPROPERTYSINGLEVALUE('critical barrel',$,IFCINTEGER(0),$);
|
||||
#83=IFCPROPERTYSINGLEVALUE('critical side',$,IFCINTEGER(0),$);
|
||||
#84=IFCPROPERTYSINGLEVALUE('Pipe Name',$,IFCLABEL('5-1 to 1-1'),$);
|
||||
#85=IFCPROPERTYSINGLEVALUE('Pipe Type',$,IFCLABEL('EXISTING (RETAINED)'),$);
|
||||
#86=IFCPROPERTYSINGLEVALUE('Justification',$,IFCLABEL('Invert'),$);
|
||||
#87=IFCPROPERTYSINGLEVALUE('Start x',$,IFCREAL(289080.818499209),$);
|
||||
#88=IFCPROPERTYSINGLEVALUE('Start y',$,IFCREAL(5822851.36621952),$);
|
||||
#89=IFCPROPERTYSINGLEVALUE('Start z',$,IFCREAL(118.736),$);
|
||||
#90=IFCPROPERTYSINGLEVALUE('End x',$,IFCREAL(289081.570466963),$);
|
||||
#91=IFCPROPERTYSINGLEVALUE('End y',$,IFCREAL(5822857.54453541),$);
|
||||
#92=IFCPROPERTYSINGLEVALUE('End z',$,IFCREAL(118.736),$);
|
||||
#93=IFCCOMPLEXPROPERTY('Geometry','12d_attribute_group',$,(#86,#87,#88,#89,#90,#91,#92));
|
||||
#94=IFCPROPERTYSET('0pqqHlNsb54w9VRYcNog0C',#22,'12d Model',$,(#42,#43,#44,#45,#47,#49,#50,#51,#52,#53,#54,#55,#56,#57,#58,#59,#60,#61,#62,#63,#64,#65,#66,#67,#68,#69,#70,#71,#72,#73,#74,#75,#76,#77,#78,#79,#80,#81,#82,#83,#84,#85,#93));
|
||||
#95=IFCSTYLEDITEM(#37,(#98),$);
|
||||
#96=IFCCOLOURRGB('orange',1.,0.647058823529412,0.);
|
||||
#97=IFCSURFACESTYLERENDERING(#96,$,$,$,$,$,IFCNORMALISEDRATIOMEASURE(0.00390625),IFCSPECULAREXPONENT(10.),.NOTDEFINED.);
|
||||
#98=IFCSURFACESTYLE($,.POSITIVE.,(#97));
|
||||
#99=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#100=IFCDIRECTION((0.,0.,1.));
|
||||
#101=IFCDIRECTION((1.,0.,0.));
|
||||
#102=IFCAXIS2PLACEMENT3D(#99,#100,#101);
|
||||
#103=IFCLOCALPLACEMENT($,#102);
|
||||
#104=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$);
|
||||
#105=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$);
|
||||
#106=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#104),(#105));
|
||||
#107=IFCAPPLICATION(#106,'0.8.1-alpha250208-3832077','Bonsai','Bonsai');
|
||||
#108=IFCOWNERHISTORY(#20,#21,$,.MODIFIED.,1739510213,#3,#107,1739508662);
|
||||
#114=IFCSITE('2CTfxIYCTCsRAvUEqFZoH3',#120,'Default Site','Description of Default Site',$,#119,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#115=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#116=IFCDIRECTION((0.,0.,1.));
|
||||
#117=IFCDIRECTION((1.,0.,0.));
|
||||
#118=IFCAXIS2PLACEMENT3D(#115,#116,#117);
|
||||
#119=IFCLOCALPLACEMENT($,#118);
|
||||
#120=IFCOWNERHISTORY(#20,#21,$,.MODIFIED.,1739510213,#3,#107,1739508662);
|
||||
#121=IFCRELCONTAINEDINSPATIALSTRUCTURE('05AVd4pPf1jeS4OU20v7N9',#5,$,$,(#40),#114);
|
||||
#122=IFCRELAGGREGATES('3PxVuUIG14dQdQEHf8wUca',#5,$,$,#17,(#114));
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,74 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('/dev/null','2025-08-27T14:53:55+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
||||
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('2MG118AczBnBRhfOi7rLGC',$,'AdaProject',$,$,$,$,(#11),#6);
|
||||
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#5=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#6=IFCUNITASSIGNMENT((#5,#3,#4,#2));
|
||||
#7=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#8=IFCDIRECTION((0.,0.,1.));
|
||||
#9=IFCDIRECTION((1.,0.,0.));
|
||||
#10=IFCAXIS2PLACEMENT3D(#7,#8,#9);
|
||||
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#10,$);
|
||||
#12=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#11,$,.MODEL_VIEW.,$);
|
||||
#13=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Model',*,*,*,*,#11,$,.GRAPH_VIEW.,$);
|
||||
#14=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#11,$,.MODEL_VIEW.,$);
|
||||
#15=IFCACTORROLE(.ENGINEER.,$,$);
|
||||
#16=IFCPERSON('AdaUser',$,$,$,$,$,(#15),$);
|
||||
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
||||
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
||||
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
||||
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756299235);
|
||||
#21=IFCDIRECTION((0.,0.,1.));
|
||||
#22=IFCDIRECTION((1.,0.,0.));
|
||||
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
||||
#25=IFCLOCALPLACEMENT($,#24);
|
||||
#26=IFCSITE('2V9fV9zKnAP9J5CfXLkqps',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#27=IFCRELAGGREGATES('12FX4aagX3HQdiLOLDvGX_',#20,'Project Container',$,#1,(#26));
|
||||
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
||||
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
||||
#30=IFCPROPERTYSET('2yltt4JCTAOAyhAFFKmDBW',#20,'Properties',$,(#28,#29));
|
||||
#31=IFCRELDEFINESBYPROPERTIES('3U0CyjG8XALx2oClwZMPg9',#20,'Properties',$,(#26),#30);
|
||||
#32=IFCMATERIAL('S355',$,'Steel');
|
||||
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
||||
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
||||
#35=IFCPROPERTYSINGLEVALUE('YoungModulus',$,IFCMODULUSOFELASTICITYMEASURE(210000000000.),$);
|
||||
#36=IFCPROPERTYSINGLEVALUE('PoissonRatio',$,IFCPOSITIVERATIOMEASURE(0.3),$);
|
||||
#37=IFCPROPERTYSINGLEVALUE('ThermalExpansionCoefficient',$,IFCTHERMALEXPANSIONCOEFFICIENTMEASURE(1.2E-05),$);
|
||||
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
||||
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
||||
#41=IFCRELASSOCIATESMATERIAL('3__DXGCfr15fk06ugmrrR4',#20,'S355','Objects related to S355',(#64),#32);
|
||||
#42=IFCDIRECTION((0.,0.,1.));
|
||||
#43=IFCDIRECTION((1.,0.,0.));
|
||||
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#45=IFCAXIS2PLACEMENT3D(#44,#42,#43);
|
||||
#46=IFCLOCALPLACEMENT(#25,#45);
|
||||
#47=IFCCARTESIANPOINTLIST2D(((0.,0.),(0.,0.1),(0.1,0.),(0.1,0.1)),$);
|
||||
#48=IFCINDEXEDPOLYCURVE(#47,(IFCLINEINDEX((3,1)),IFCLINEINDEX((1,2)),IFCLINEINDEX((2,4)),IFCLINEINDEX((4,3))),$);
|
||||
#49=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#48);
|
||||
#50=IFCCARTESIANPOINTLIST3D(((-1.,0.,0.8),(-0.14253,0.,0.542759),(-0.039541321421393,0.,0.470579850861542),(0.,0.,0.),(0.,0.,0.351194)),$);
|
||||
#51=IFCINDEXEDPOLYCURVE(#50,(IFCLINEINDEX((4,5)),IFCARCINDEX((5,3,2)),IFCLINEINDEX((2,1))),$);
|
||||
#52=IFCDIRECTION((-0.,1.,0.));
|
||||
#53=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#54=IFCDIRECTION((1.,0.,0.));
|
||||
#55=IFCDIRECTION((0.,0.,-1.));
|
||||
#56=IFCAXIS2PLACEMENT3D(#53,#54,#55);
|
||||
#57=IFCFIXEDREFERENCESWEPTAREASOLID(#49,#56,#51,$,$,#52);
|
||||
#58=IFCSHAPEREPRESENTATION(#12,'Body','AdvancedSweptSolid',(#57));
|
||||
#59=IFCPRODUCTDEFINITIONSHAPE($,$,(#58));
|
||||
#60=IFCCOLOURRGB('Color1',0.8,0.8,0.8);
|
||||
#61=IFCSURFACESTYLESHADING(#60,0.);
|
||||
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
||||
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
||||
#64=IFCBUILDINGELEMENTPROXY('07ngBzsO5BK8C3rDUqQfSD',#20,'sweep1',$,$,#46,#59,$,$);
|
||||
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('1_r$SsBj1C3wgAjnhfmkqs',#20,'Physical model',$,(#64),#26);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,74 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('/dev/null','2025-08-28T09:45:32+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
||||
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('3kk2KcD29DrxlrjJ23wiG2',$,'AdaProject',$,$,$,$,(#11),#6);
|
||||
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#5=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#6=IFCUNITASSIGNMENT((#3,#5,#4,#2));
|
||||
#7=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#8=IFCDIRECTION((0.,0.,1.));
|
||||
#9=IFCDIRECTION((1.,0.,0.));
|
||||
#10=IFCAXIS2PLACEMENT3D(#7,#8,#9);
|
||||
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#10,$);
|
||||
#12=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#11,$,.MODEL_VIEW.,$);
|
||||
#13=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Model',*,*,*,*,#11,$,.GRAPH_VIEW.,$);
|
||||
#14=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#11,$,.MODEL_VIEW.,$);
|
||||
#15=IFCACTORROLE(.ENGINEER.,$,$);
|
||||
#16=IFCPERSON('AdaUser',$,$,$,$,$,(#15),$);
|
||||
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
||||
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
||||
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
||||
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756367132);
|
||||
#21=IFCDIRECTION((0.,0.,1.));
|
||||
#22=IFCDIRECTION((1.,0.,0.));
|
||||
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
||||
#25=IFCLOCALPLACEMENT($,#24);
|
||||
#26=IFCSITE('3192LLwf9Et9NYY6Dp1HMI',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#27=IFCRELAGGREGATES('3sB8i$7If0y9iXdBJx7Et7',#20,'Project Container',$,#1,(#26));
|
||||
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
||||
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
||||
#30=IFCPROPERTYSET('16zSs_zL5FTQaH0f6iQohK',#20,'Properties',$,(#28,#29));
|
||||
#31=IFCRELDEFINESBYPROPERTIES('2hQUoEcor7uf16HkCAa06h',#20,'Properties',$,(#26),#30);
|
||||
#32=IFCMATERIAL('S355',$,'Steel');
|
||||
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
||||
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
||||
#35=IFCPROPERTYSINGLEVALUE('YoungModulus',$,IFCMODULUSOFELASTICITYMEASURE(210000000000.),$);
|
||||
#36=IFCPROPERTYSINGLEVALUE('PoissonRatio',$,IFCPOSITIVERATIOMEASURE(0.3),$);
|
||||
#37=IFCPROPERTYSINGLEVALUE('ThermalExpansionCoefficient',$,IFCTHERMALEXPANSIONCOEFFICIENTMEASURE(1.2E-05),$);
|
||||
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
||||
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
||||
#41=IFCRELASSOCIATESMATERIAL('3GCQxFEKjCmPtz21j5vEC_',#20,'S355','Objects related to S355',(#64),#32);
|
||||
#42=IFCDIRECTION((0.,0.,1.));
|
||||
#43=IFCDIRECTION((1.,0.,0.));
|
||||
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#45=IFCAXIS2PLACEMENT3D(#44,#42,#43);
|
||||
#46=IFCLOCALPLACEMENT(#25,#45);
|
||||
#47=IFCCARTESIANPOINTLIST2D(((0.1,0.05),(0.,0.),(0.1,0.),(0.1,0.05)),$);
|
||||
#48=IFCINDEXEDPOLYCURVE(#47,$,$);
|
||||
#49=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#48);
|
||||
#50=IFCCARTESIANPOINTLIST3D(((50.,100.,200.),(50.,100.,200.389146),(50.0003133895072,100.025071208213,200.486102587622),(50.001174980994,100.093998658131,200.558749),(50.0086491903016,100.691934415524,200.932458971772),(50.0310627108963,100.770687414196,201.008805063266),(50.089001837944,100.800000190195,201.100002410475),(50.6760204909973,100.800000000803,201.843063903121),(50.7636872882004,100.858578730855,201.954034522829),(50.7999999397009,101.700000021708,201.999999960467),(50.8000002509973,100.999999960803,202.000000303121)),$);
|
||||
#51=IFCINDEXEDPOLYCURVE(#50,(IFCLINEINDEX((1,2)),IFCARCINDEX((2,3,4)),IFCLINEINDEX((4,5)),IFCARCINDEX((5,6,7)),IFCLINEINDEX((7,8)),IFCARCINDEX((8,9,11)),IFCLINEINDEX((11,10))),$);
|
||||
#52=IFCDIRECTION((1.,0.,0.));
|
||||
#53=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#54=IFCDIRECTION((0.,0.,1.));
|
||||
#55=IFCDIRECTION((1.,0.,0.));
|
||||
#56=IFCAXIS2PLACEMENT3D(#53,#54,#55);
|
||||
#57=IFCFIXEDREFERENCESWEPTAREASOLID(#49,#56,#51,$,$,#52);
|
||||
#58=IFCSHAPEREPRESENTATION(#12,'Body','AdvancedSweptSolid',(#57));
|
||||
#59=IFCPRODUCTDEFINITIONSHAPE($,$,(#58));
|
||||
#60=IFCCOLOURRGB('Color1',0.8,0.8,0.8);
|
||||
#61=IFCSURFACESTYLESHADING(#60,0.);
|
||||
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
||||
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
||||
#64=IFCBUILDINGELEMENTPROXY('0Yr8J8fnj47xCNdvnijjk4',#20,'sweep2',$,$,#46,#59,$,$);
|
||||
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('1Bl6up0hb69elL9bAj0Yd6',#20,'Physical model',$,(#64),#26);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,85 @@
|
||||
import pathlib
|
||||
from collections.abc import Sequence
|
||||
|
||||
import ifcopenshell
|
||||
import pytest
|
||||
|
||||
|
||||
def _bbox_from_vertices(verts: list[tuple[float, float, float]]):
|
||||
if not verts:
|
||||
return (0, 0, 0), (0, 0, 0)
|
||||
xs = [v[0] for v in verts]
|
||||
ys = [v[1] for v in verts]
|
||||
zs = [v[2] for v in verts]
|
||||
mn = (min(xs), min(ys), min(zs))
|
||||
mx = (max(xs), max(ys), max(zs))
|
||||
return mn, mx
|
||||
|
||||
|
||||
def _size_from_bbox(mn, mx):
|
||||
return (mx[0] - mn[0], mx[1] - mn[1], mx[2] - mn[2])
|
||||
|
||||
|
||||
def _triples(flat: Sequence[float]) -> list[tuple[float, float, float]]:
|
||||
return [(float(flat[i]), float(flat[i + 1]), float(flat[i + 2])) for i in range(0, len(flat), 3)]
|
||||
|
||||
|
||||
def load_ifc_mesh_bbox(ifc_path: str):
|
||||
"""Load first product's mesh from IFC using ifcopenshell.geom and return bbox and size."""
|
||||
try:
|
||||
import ifcopenshell.geom as geom
|
||||
except Exception as e:
|
||||
raise RuntimeError("ifcopenshell.geom not available: cannot validate IFC geometry") from e
|
||||
|
||||
settings = geom.settings()
|
||||
settings.set(settings.USE_WORLD_COORDS, True)
|
||||
|
||||
f = ifcopenshell.open(ifc_path)
|
||||
# Prefer the proxy we created, otherwise take any product with representation
|
||||
products = f.by_type("IfcProduct")
|
||||
target = None
|
||||
for p in products:
|
||||
if p.is_a("IfcBuildingElementProxy") and getattr(p, "Representation", None):
|
||||
target = p
|
||||
break
|
||||
if target is None:
|
||||
for p in products:
|
||||
if getattr(p, "Representation", None):
|
||||
target = p
|
||||
break
|
||||
if target is None:
|
||||
raise RuntimeError("No representable product found in IFC for validation")
|
||||
|
||||
shape = geom.create_shape(settings, target)
|
||||
verts = _triples(shape.geometry.verts)
|
||||
mn, mx = _bbox_from_vertices(verts)
|
||||
return mn, mx, _size_from_bbox(mn, mx)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def geom_dir():
|
||||
return pathlib.Path(__file__).parent.parent.resolve().absolute() / "fixtures/geom"
|
||||
|
||||
|
||||
def test_simple_sweep_1(geom_dir):
|
||||
ifc_file_path = geom_dir / "simple_sweep_1.ifc"
|
||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||
assert ifc_sz == pytest.approx((0.8957825463853046, 0.1, 1.1))
|
||||
assert ifc_mn == pytest.approx((0.0, 0, -0.1))
|
||||
assert ifc_mx == pytest.approx((0.8957825463853046, 0.1, 1.0))
|
||||
|
||||
|
||||
def test_simple_sweep_2(geom_dir):
|
||||
ifc_file_path = geom_dir / "simple_sweep_2.ifc"
|
||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||
assert ifc_mn == pytest.approx((50.0, 100.0, 200.0))
|
||||
assert ifc_mx == pytest.approx((50.89584911299009, 101.70000025609394, 202.0000003710634))
|
||||
assert ifc_sz == pytest.approx((0.8958491129900921, 1.7000002560939436, 2.0000003710634076))
|
||||
|
||||
|
||||
def test_pipe_12d(geom_dir):
|
||||
ifc_file_path = geom_dir / "pipe.ifc"
|
||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||
assert ifc_sz == pytest.approx((1.205888147422229, 0.9929900508137735, 0.35776115971654576))
|
||||
assert ifc_mn == pytest.approx((288.9774190979147, 582.0537006391681, 118.70711942014172))
|
||||
assert ifc_mx == pytest.approx((290.18330724533695, 583.0466906899819, 119.06488057985827))
|
||||
@@ -21,7 +21,7 @@ import pytest
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
TEST_FILE_DIR = Path("../../test/input/")
|
||||
TEST_FILE_DIR = Path(__file__).parent / "../../../test/input/"
|
||||
|
||||
|
||||
class TestOpen:
|
||||
|
||||
@@ -22,7 +22,7 @@ import pytest
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
TEST_FILE_DIR = Path("../../test/input/")
|
||||
TEST_FILE_DIR = Path(__file__).parent / "../../../test/input/"
|
||||
|
||||
|
||||
class TestWrite:
|
||||
|
||||
Reference in New Issue
Block a user