mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
finish implementing clothoid transition
This commit is contained in:
@@ -0,0 +1 @@
|
||||
tests/
|
||||
@@ -18,12 +18,12 @@
|
||||
###############################################################################
|
||||
|
||||
from enum import Enum
|
||||
from dataclasses import DataClass
|
||||
from dataclasses import dataclass
|
||||
import math
|
||||
|
||||
from OCC.gp import gp_Pnt2d
|
||||
from OCC.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
|
||||
from OCC.BRepBuilderAPI import BRepBuilderAPI_MakeWire
|
||||
from OCC.Core.gp import gp_Pnt2d
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge2d
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeWire
|
||||
|
||||
|
||||
class IfcTransitionCurveType(Enum):
|
||||
@@ -40,142 +40,149 @@ class IfcTransitionCurveType(Enum):
|
||||
SINECURVE = 6 # NOTE also referred to as Klein curve
|
||||
|
||||
|
||||
@DataClass
|
||||
class IfcTransitionSegment2D:
|
||||
"""IFC 4.1 Section 8.9.3.65
|
||||
[https://standards.buildingsmart.org/IFC/RELEASE/IFC4_1/FINAL/HTML/schema/ifcgeometryresource/lexical/ifctransitioncurvesegment2d.htm]
|
||||
|
||||
A curve that transitions between a straight line and a circular arc (or the reverse).
|
||||
@dataclass
|
||||
class TransitionCurve:
|
||||
"""
|
||||
A curve that transitions between a straight line and a circular arc
|
||||
(or the reverse).
|
||||
"""
|
||||
StartPoint: tuple # IfcSchema::IfcCartesianPoint
|
||||
StartDirection: float # IfcSchema::IfcPlaneAngleMeasure
|
||||
SegmentLength: float # IfcSchema::IfcPositiveLengthMeasure
|
||||
StartRadius: float = None # IfcSchema::IfcPositiveLengthMeasure
|
||||
EndRadius: float = None # IfcSchema::IfcPositiveLengthMeasure
|
||||
IsStartRadiusCCW: bool # IfcSchema::IfcBoolean
|
||||
IsEndRadiusCCW: bool # IfcSchema::IfcBoolean
|
||||
TransitionCurveType: IfcTransitionCurveType
|
||||
StartRadius: float = None # IfcSchema::IfcPositiveLengthMeasure
|
||||
EndRadius: float = None # IfcSchema::IfcPositiveLengthMeasure
|
||||
|
||||
def _calc_biquadratic_parabola_point(self, lpt, L, R, ccw):
|
||||
x = lpt
|
||||
if (x <= (L / 2)):
|
||||
y = x**4 / (6 * R * L**2)
|
||||
else:
|
||||
|
||||
def _calc_biquadratic_parabola_point(lpt, L, R, ccw):
|
||||
x = lpt
|
||||
if x <= L / 2:
|
||||
y = math.pow(x, 4) / (6 * R * math.pow(L, 2))
|
||||
else:
|
||||
y = ((-1 * math.pow(x, 4)) / (6 * R * math.pow(L, 2))) \
|
||||
+ ((2 * math.pow(x, 3)) / (3 * R * L)) \
|
||||
- ((math.pow(x, 2)) / (2 * R)) \
|
||||
+ ((L * x) / (6 * R)) \
|
||||
- ((math.pow(L, 2) / (48 * R))
|
||||
yterm_1 = (-1 * x**4) / (6 * R * L**2)
|
||||
yterm_2 = (2 * x**3) / (3 * R * L)
|
||||
yterm_3 = x**2 / (2 * R)
|
||||
yterm_4 = (L * x) / (6 * R)
|
||||
yterm_5 = L**2 / (48 * R)
|
||||
|
||||
if not ccw:
|
||||
y = -y
|
||||
y = yterm_1 + yterm_2 - yterm_3 + yterm_4 - yterm_5
|
||||
|
||||
return gp_Pnt2d(x, y)
|
||||
if not ccw:
|
||||
y = -y
|
||||
|
||||
return gp_Pnt2d(x, y)
|
||||
|
||||
def _calc_bloss_curve_point(lpt, L, R, ccw):
|
||||
pass
|
||||
def _calc_bloss_curve_point(self, lpt, L, R, ccw):
|
||||
pass
|
||||
|
||||
def _calc_clothoid_curve_point(self, lpt, L, R, ccw):
|
||||
RL = R * L
|
||||
xterm_1 = 1
|
||||
xterm_2 = lpt**4 / (40 * RL**2)
|
||||
xterm_3 = lpt**8 / (3456 * RL**4)
|
||||
xterm_4 = lpt**12 / (599040 * RL**6)
|
||||
x = lpt * (xterm_1 - xterm_2 + xterm_3 - xterm_4)
|
||||
|
||||
def _calc_clothoid_curve_point(lpt, L, R, ccw):
|
||||
x = lpt * (1 - (math.pow(lpt, 4) / (40 * math.pow(R, 2) * math.pow(L, 2))) \
|
||||
+ (math.pow(lpt, 8) / 3456 * math.pow(R, 4) * math.pow(L, 4)))
|
||||
factor = lpt**3 / (6 * RL)
|
||||
yterm_1 = 1
|
||||
yterm_2 = lpt**4 / (56 * RL**2)
|
||||
yterm_3 = lpt**8 / (7040 * RL**4)
|
||||
yterm_4 = lpt**12 / (1612800 * RL**6)
|
||||
|
||||
y = ((math.pow(lpt, 3) / (6 * R * L)) \
|
||||
* (1 - (math.pow(lpt, 4) / (56 * math.pow(R, 2) \
|
||||
* math.pow(L, 2))) \
|
||||
+ (math.pow(lpt, 8) / 7040 * math.pow(R, 4) * math.pow(L, 4))))
|
||||
y = factor * (yterm_1 - yterm_2 + yterm_3 - yterm_4)
|
||||
|
||||
if not ccw:
|
||||
y = -y
|
||||
if not ccw:
|
||||
y = -y
|
||||
|
||||
return gp_Pnt2d(x, y)
|
||||
return gp_Pnt2d(x, y)
|
||||
|
||||
def _calc_cosine_curve_point(self, lpt, L, R, ccw):
|
||||
pi = math.pi
|
||||
psi_x = (pi * lpt) / L
|
||||
|
||||
def _calc_cosine_curve_point(lpt, L, R, ccw):
|
||||
pi = math.pi
|
||||
psi_x = (pi * lpt) / L
|
||||
terms = list()
|
||||
terms.append(math.pow(L, 2) / (8.0 * math.pow(pi, 2) * math.pow(R, 2)))
|
||||
terms.append(L / pi)
|
||||
terms.append(math.pow(psi_x, 3) / 3.0)
|
||||
terms.append(psi_x / 2.0)
|
||||
terms.append((math.sin(psi_x) * math.cos(psi_x) / 2.0)
|
||||
terms.append((psi_x * math.cos(psi_x))
|
||||
xterm_1 = (L**2) / (8.0 * pi**2 * R**2)
|
||||
xterm_2 = L / pi
|
||||
xterm_3 = psi_x**3 / (3.0)
|
||||
xterm_4 = psi_x / (2.0)
|
||||
xterm_5 = (math.sin(psi_x) * math.cos(psi_x)) / (2.0)
|
||||
xterm_6 = psi_x * math.cos(psi_x)
|
||||
|
||||
x = (lpt - terms[0] * terms[1] * ( terms[2] + terms[3] - terms[4] - (2.0 * terms[5])))
|
||||
x = lpt - xterm_1 * xterm_2 * ( xterm_3 + xterm_4 - xterm_5 - (2.0 * xterm_6))
|
||||
|
||||
# TODO: code for y - coordinate
|
||||
y = 0
|
||||
# TODO: code for y - coordinate
|
||||
y = 0
|
||||
|
||||
if not ccw:
|
||||
y = -y
|
||||
if not ccw:
|
||||
y = -y
|
||||
|
||||
return gp_Pnt2d(x, y)
|
||||
return gp_Pnt2d(x, y)
|
||||
|
||||
def _calc_cubic_parabola_point(self, lpt, L, R, ccw):
|
||||
|
||||
def _calc_cubic_parabola_point(lpt, L, R, ccw):
|
||||
x = lpt
|
||||
y = math.pow(x, 3) / (6 * R * L)
|
||||
if not ccw:
|
||||
y = -y
|
||||
|
||||
x = lpt
|
||||
y = math.pow(x, 3) / (6 * R * L)
|
||||
if not ccw:
|
||||
y = -y
|
||||
return gp_Pnt2d(x, y)
|
||||
|
||||
return gp_Pnt2d(x, y)
|
||||
def _calc_sine_curve_point(self, lpt, L, R, ccw):
|
||||
pass
|
||||
|
||||
def _calc_transition_curve_point(self, lpt, L, R, ccw, trans_type):
|
||||
|
||||
def _calc_sine_curve_point(lpt, L, R, ccw):
|
||||
pass
|
||||
if trans_type == "BIQUADRATICPARABOLA":
|
||||
return self._calc_cubic_parabola_point(lpt, L, R, ccw)
|
||||
elif trans_type == "BLOSSCURVE":
|
||||
# return _calc_bloss_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
elif trans_type == "CLOTHOIDCURVE":
|
||||
return self._calc_clothoid_curve_point(lpt, L, R, ccw)
|
||||
elif trans_type == "COSINECURVE":
|
||||
# return _calc_cosine_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
elif trans_type == "CUBICPARABOLA":
|
||||
return self._calc_cubic_parabola_point(lpt, L, R, ccw)
|
||||
elif trans_type == "SINECURVE":
|
||||
# return _calc_sine_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
else:
|
||||
raise ValueError(f"Invalid Transition Curve type '{trans_type}'.")
|
||||
|
||||
def to_wire(self, stroking_interval=5.0):
|
||||
"""convert IfcTransitionSegment2D to OCC wire
|
||||
|
||||
def _calc_transition_curve_point(lpt, L, R, ccw, trans_type):
|
||||
:param stroking_interval: maximum curve length between points to be calculated
|
||||
:type stroking_interval: float
|
||||
:return: OCC wire containing interpolated points
|
||||
"""
|
||||
points = list()
|
||||
|
||||
if trans_type == "BIQUADRATICPARABOLA":
|
||||
return _calc_cubic_parabola_point
|
||||
elif trans_type == "BLOSSCURVE":
|
||||
# return _calc_bloss_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
elif trans_type == "CLOTHOIDCURVE":
|
||||
return _calc_clothoid_curve_point(lpt, L, R, ccw)
|
||||
elif trans_type == "COSINECURVE":
|
||||
# return _calc_cosine_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
elif trans_type == "CUBICPARABOLA":
|
||||
return _calc_cubic_parabola_point(lpt, L, R, ccw)
|
||||
elif trans_type == "SINECURVE":
|
||||
# return _calc_sine_curve_point(lpt, L, R, ccw)
|
||||
raise ValueError(f"Transition Curve type '{trans_type}' not implemented yet.")
|
||||
else:
|
||||
raise ValueError(f"Invalid Transition Curve type '{trans_type}'.")
|
||||
L = self.SegmentLength
|
||||
R = self.EndRadius
|
||||
ccw = self.IsStartRadiusCCW
|
||||
trans_type = self.TransitionCurveType.name
|
||||
|
||||
num_intervals = math.ceil(L / stroking_interval)
|
||||
interval_dist = L / num_intervals
|
||||
lpt = 0.0 # length along the curve at the point to be calculated
|
||||
|
||||
def convert_IfcTransitionSegment2D(segment, stroking_interval=5.0):
|
||||
"""convert IfcTransitionSegment2D to OCC wire
|
||||
for _ in range(num_intervals):
|
||||
points.append(self._calc_transition_curve_point(
|
||||
lpt, L, R, ccw, trans_type
|
||||
))
|
||||
lpt += interval_dist
|
||||
|
||||
:param segment: ifc entity to be parsed into geometry
|
||||
:type segment: IfcTransitionSegment2D
|
||||
:param stroking_interval: maximum curve length between points to be calculated
|
||||
:type stroking_interval: float
|
||||
:return: OCC wire containing interpolated points
|
||||
"""
|
||||
points = list()
|
||||
|
||||
L = segment.SegmentLength
|
||||
R = segment.EndRadius
|
||||
ccw = segment.IsStartRadiusCCW
|
||||
trans_type = segment.TransitionCurveType.name
|
||||
|
||||
num_intervals = math.ceil(L / stroking_interval)
|
||||
interval_dist = L / num_intervals
|
||||
lpt = 0.0 # length along the curve at the point to be calculated
|
||||
|
||||
for _ in num_intervals:
|
||||
points.append(_calc_transition_curve_point(
|
||||
lpt, L, R, ccw, trans_type
|
||||
edges = list()
|
||||
for i in range(len(points) - 1):
|
||||
edges.append(BRepBuilderAPI_MakeEdge2d(
|
||||
points[i], points[i + 1]
|
||||
))
|
||||
lpt += interval_dist
|
||||
|
||||
e = BRepBuilderAPI_MakeEdge(points)
|
||||
|
||||
return BRepBuilderAPI_MakeWire(e)
|
||||
wire = BRepBuilderAPI_MakeWire()
|
||||
for e in edges:
|
||||
wire.Add(e.Edge())
|
||||
# return wire
|
||||
return points
|
||||
|
||||
Reference in New Issue
Block a user