Bug fixes with zero length segment and b2g mapping when project angle is not radian

This commit is contained in:
Richard Brice
2025-07-21 16:24:20 -07:00
parent f3c72920de
commit c12ed46c49
4 changed files with 160 additions and 107 deletions
@@ -24,7 +24,7 @@ import numpy as np
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._update_curve_segment_transition_code import _update_curve_segment_transition_code
from ifcopenshell.api.alignment._map_alignment_horizontal_segment import __map_alignment_horizontal_segment
from ifcopenshell.api.alignment._map_alignment_horizontal_segment import _map_alignment_horizontal_segment
from ifcopenshell.api.alignment._map_alignment_vertical_segment import _map_alignment_vertical_segment
from ifcopenshell.api.alignment._map_alignment_cant_segment import _map_alignment_cant_segment
@@ -47,47 +47,44 @@ def _add_curve_segment_to_composite_curve(
composite_curve.Segments += (curve_segment,)
assert len(curve_segment.UsingCurves) == 1
else:
# get the segment before the zero length segment
prev_segment = (
composite_curve.Segments[-2]
if composite_curve.Segments != None and 1 < len(composite_curve.Segments)
else None
)
zero_length_segment = composite_curve.Segments[-1] if ifcopenshell.api.alignment.has_zero_length_segment(composite_curve) else None
prev_segment = None
if zero_length_segment and 1 < len(composite_curve.Segments):
prev_segment = composite_curve.Segments[-2]
elif zero_length_segment == None:
prev_segment = composite_curve.Segments[-1]
curve_segment.Transition = "CONTINUOUS"
# must add the new segment to the curve before updating the transition code
# add the new segment before the zero length segment
zero_length_segment = composite_curve.Segments[-1]
if prev_segment:
segments = composite_curve.Segments[0:-1]
segments += (
curve_segment,
zero_length_segment,
)
segments = composite_curve.Segments[0:-1]
if zero_length_segment:
segments += (curve_segment,zero_length_segment,)
composite_curve.Segments = []
composite_curve.Segments += segments
_update_curve_segment_transition_code(prev_segment, curve_segment)
else:
composite_curve.Segments = (curve_segment, zero_length_segment)
composite_curve.Segments += (curve_segment,)
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)
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
x = float(end[0, 3]) / unit_scale
y = float(end[1, 3]) / unit_scale
dx = float(end[0, 0])
dy = float(end[1, 0])
if prev_segment:
_update_curve_segment_transition_code(prev_segment, curve_segment)
# assume IfcAxis2Placement2D
zero_length_segment.Placement.Location.Coordinates = (x, y)
zero_length_segment.Placement.RefDirection.DirectionRatios = (dx, dy)
if zero_length_segment:
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)
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
x = float(end[0, 3]) / unit_scale
y = float(end[1, 3]) / unit_scale
dx = float(end[0, 0])
dy = float(end[1, 0])
_update_curve_segment_transition_code(curve_segment, zero_length_segment)
# assume IfcAxis2Placement2D
zero_length_segment.Placement.Location.Coordinates = (x, y)
zero_length_segment.Placement.RefDirection.DirectionRatios = (dx, dy)
_update_curve_segment_transition_code(curve_segment, zero_length_segment)
def _add_segment_to_curve(file: ifcopenshell.file, segment: entity_instance, curve: entity_instance) -> None:
@@ -119,7 +116,7 @@ def _add_segment_to_curve(file: ifcopenshell.file, segment: entity_instance, cur
# map the IfcAlignmentSegment to an IfcCurveSegment (or two in the case of helmert curves)
if segment.DesignParameters.is_a("IfcAlignmentHorizontalSegment"):
mapped_segments = __map_alignment_horizontal_segment(file, segment)
mapped_segments = _map_alignment_horizontal_segment(file, segment)
elif segment.DesignParameters.is_a("IfcAlignmentVerticalSegment"):
mapped_segments = _map_alignment_vertical_segment(file, segment)
elif segment.DesignParameters.is_a("IfcAlignmentCantSegment"):
@@ -37,6 +37,9 @@ def _map_line(file: ifcopenshell.file, design_parameters: entity_instance) -> Se
start_direction = design_parameters.StartDirection
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
parent_curve = file.create_entity(
@@ -77,6 +80,9 @@ def _map_circular_arc(file: ifcopenshell.file, design_parameters: entity_instanc
start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
parent_curve = file.createIfcCircle(
@@ -108,6 +114,9 @@ def _map_clothoid(file: ifcopenshell.file, design_parameters: entity_instance) -
end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
f = _get_curve_factor(design_parameters)
@@ -147,6 +156,9 @@ def _map_cubic(file: ifcopenshell.file, design_parameters: entity_instance) -> S
end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
offset = 0.0
@@ -197,6 +209,9 @@ def _map_helmert_curve(file: ifcopenshell.file, design_parameters: entity_instan
end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
f = _get_curve_factor(design_parameters)
@@ -300,6 +315,9 @@ def _map_bloss_curve(file: ifcopenshell.file, design_parameters: entity_instance
start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
f = _get_curve_factor(design_parameters)
@@ -344,6 +362,9 @@ def _map_cosine_curve(file: ifcopenshell.file, design_parameters: entity_instanc
start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
f = _get_curve_factor(design_parameters)
@@ -384,6 +405,9 @@ def _map_sine_curve(file: ifcopenshell.file, design_parameters: entity_instance)
start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
start_direction *= angle_unit_scale
transition = "DISCONTINUOUS"
f = _get_curve_factor(design_parameters)
@@ -424,7 +448,7 @@ def _map_viennese_bend(file: ifcopenshell.file, design_parameters: entity_instan
raise NotImplementedError("VIENNESEBEND not implemented")
def __map_alignment_horizontal_segment(file: ifcopenshell.file, segment: entity_instance) -> Sequence[entity_instance]:
def _map_alignment_horizontal_segment(file: ifcopenshell.file, segment: entity_instance) -> Sequence[entity_instance]:
"""
Creates IfcCurveSegment entities for the represention of the supplied IfcAlignmentHorizontalSegment business logic entity instance.
A pair of entities is returned because a single business logic segment of type HELMERTCURVE maps to two representaiton entities.
@@ -23,8 +23,10 @@ import ifcopenshell.util.alignment
from ifcopenshell import entity_instance
import ifcopenshell.ifcopenshell_wrapper as wrapper
import numpy as np
import math
from ifcopenshell.api.alignment._get_segment_start_point_label import _get_segment_start_point_label
from ifcopenshell.api.alignment._map_alignment_horizontal_segment import _map_alignment_horizontal_segment
def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, include_referent : bool = True) -> bool:
@@ -68,7 +70,6 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
y = float(e[1,3])
dx = float(e[0,0])
dy = float(e[1,0])
segment_start = last_segment.SegmentStart.wrappedValue + last_segment.SegmentLength.wrappedValue
parent_curve = file.createIfcLine(
Pnt=file.createIfcCartesianPoint(Coordinates=((0.0, 0.0))),
@@ -83,7 +84,7 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
Location=file.createIfcCartesianPoint((x, y)),
RefDirection=file.createIfcDirection((dx, dy)),
),
SegmentStart=file.createIfcLengthMeasure(segment_start),
SegmentStart=file.createIfcLengthMeasure(0.0),
SegmentLength=file.createIfcLengthMeasure(0.0),
ParentCurve=parent_curve,
)
@@ -98,11 +99,40 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
else:
segment = None
if layout.is_a("IfcAlignmentHorizontal"):
x = 0.
y = 0.
dx = 1.
dy = 0.
last_segment = None
for rel in layout.IsNestedBy:
if 0 < len(rel.RelatedObjects):
last_segment = rel.RelatedObjects[-1]
break
if last_segment:
file.begin_transaction() # use a transaction so we can discard any temporary IFC entities created
settings = ifcopenshell.geom.settings()
mapped_segments = _map_alignment_horizontal_segment(file,last_segment)
geometry_segment = mapped_segments[0] if mapped_segments[1] == None else mapped_segments[1]
fn = wrapper.map_shape(settings,geometry_segment.wrapped_data)
eval = wrapper.function_item_evaluator(settings,fn)
e = np.array(eval.evaluate(fn.end()))
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
x = float(e[0,3]) / unit_scale
y = float(e[1,3]) / unit_scale
dx = float(e[0,0])
dy = float(e[1,0])
file.discard_transaction()
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
design_parameters = file.createIfcAlignmentHorizontalSegment(
StartPoint=file.createIfcCartesianPoint(
(0.0, 0.0)
), # this is a little problematic. need to know the end point and tangent
StartDirection=0.0, # of the previous segment, which requires geometry mapping
(x,y)
),
StartDirection=math.atan2(dy,dx) / angle_unit_scale,
StartRadiusOfCurvature=0.0,
EndRadiusOfCurvature=0.0,
SegmentLength=0.0,
@@ -114,11 +144,12 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
last_segment_end_gradient = 0.0
for rel in layout.IsNestedBy:
if 0 < len(rel.RelatedObjects):
last_segment = rel.RelatedObjects[1]
last_segment = rel.RelatedObjects[-1]
last_segment_dist_along = (
last_segment.DesignParameters.StartDistAlong + last_segment.DesignParameters.HorizontalLength
)
last_segment_end_gradient = last_segment.DesignParameters.EndGradient
break
design_parameters = file.createIfcAlignmentVerticalSegment(
StartDistAlong=last_segment_dist_along,
@@ -135,7 +166,7 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
last_segment_cant_right = 0.0
for rel in layout.IsNestedBy:
if 0 < len(rel.RelatedObjects):
last_segment = rel.RelatedObjects[1]
last_segment = rel.RelatedObjects[-1]
last_segment_dist_along = (
last_segment.DesignParameters.StartDistAlong + last_segment.DesignParameters.HorizontalLength
)
@@ -149,6 +180,7 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
if last_segment.DesignParameters.EndCantRight != None
else last_segment.DesignParameters.StartCantRight
)
break
design_parameters = file.createIfcAlignmentCantSegment(
StartDistAlong=last_segment_dist_along,