diff --git a/src/ifcopenshell-python/.gitignore b/src/ifcopenshell-python/.gitignore new file mode 100644 index 0000000000..3d0dbe4467 --- /dev/null +++ b/src/ifcopenshell-python/.gitignore @@ -0,0 +1 @@ +tests/ \ No newline at end of file diff --git a/src/ifcopenshell-python/ifcopenshell/transition_curve.py b/src/ifcopenshell-python/ifcopenshell/transition_curve.py index 4aab6e6bb7..b06f135f69 100644 --- a/src/ifcopenshell-python/ifcopenshell/transition_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/transition_curve.py @@ -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