mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Updates alignment api
Fixes mapping of vertical alignment parabola business logic to geometry. Adds creation function of h and v alignment. Adds stationing referent at start of alignment. Fixes utility functions
This commit is contained in:
@@ -1,8 +1,16 @@
|
|||||||
#include "function_item_evaluator.h"
|
#include "function_item_evaluator.h"
|
||||||
#include "profile_helper.h"
|
#include "profile_helper.h"
|
||||||
|
|
||||||
|
#include <boost/math/quadrature/trapezoidal.hpp>
|
||||||
|
|
||||||
using namespace ifcopenshell::geometry;
|
using namespace ifcopenshell::geometry;
|
||||||
|
|
||||||
|
double ifcopenshell::geometry::polynomial_length(double A, double B, double C, double horizontal_length) {
|
||||||
|
auto fn = [A, B, C](double x) -> double { return sqrt(pow(B + 2 * C * x, 2.0) + 1.0); };
|
||||||
|
auto l = boost::math::quadrature::trapezoidal(fn, 0.0, horizontal_length);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct functor_fn_evaluator : public fn_evaluator {
|
struct functor_fn_evaluator : public fn_evaluator {
|
||||||
functor_fn_evaluator(taxonomy::functor_item::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings),
|
functor_fn_evaluator(taxonomy::functor_item::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings),
|
||||||
|
|||||||
@@ -7,6 +7,17 @@
|
|||||||
|
|
||||||
namespace ifcopenshell { namespace geometry {
|
namespace ifcopenshell { namespace geometry {
|
||||||
|
|
||||||
|
/// @brief Computes the curve length of a polynomial of the form y = A + Bx + Cx^2
|
||||||
|
/// This function is needed on the python side. To do this computation, a large library like scipy
|
||||||
|
/// is needed. That is too much overhead. For this reason, a simple function is here on the C++ side
|
||||||
|
/// that the python side can call
|
||||||
|
/// @param A constant term
|
||||||
|
/// @param B linear term
|
||||||
|
/// @param C quadradic term
|
||||||
|
/// @param horizontal_length length of the polynomal projected onto the horizontal axis
|
||||||
|
/// @return curve length
|
||||||
|
double polynomial_length(double A, double B, double C,double horizontal_length);
|
||||||
|
|
||||||
/// @brief Abstract class for evaluating a function_item. This class is specialized for each of the function_item types.
|
/// @brief Abstract class for evaluating a function_item. This class is specialized for each of the function_item types.
|
||||||
struct fn_evaluator {
|
struct fn_evaluator {
|
||||||
fn_evaluator(const ifcopenshell::geometry::Settings& settings) : settings_(settings) {
|
fn_evaluator(const ifcopenshell::geometry::Settings& settings) : settings_(settings) {
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import ifcopenshell.guid
|
|||||||
import ifcopenshell.template
|
import ifcopenshell.template
|
||||||
from ifcopenshell import entity_instance
|
from ifcopenshell import entity_instance
|
||||||
from ifcopenshell import ifcopenshell_wrapper
|
from ifcopenshell import ifcopenshell_wrapper
|
||||||
|
import ifcopenshell.util
|
||||||
|
import ifcopenshell.util.stationing
|
||||||
|
|
||||||
|
|
||||||
def evaluate_representation(shape_rep: entity_instance, dist_along: float) -> np.ndarray:
|
def evaluate_representation(shape_rep: entity_instance, dist_along: float) -> np.ndarray:
|
||||||
@@ -46,10 +48,10 @@ def evaluate_representation(shape_rep: entity_instance, dist_along: float) -> np
|
|||||||
# TODO: confirm point is not beyond limits of alignment
|
# TODO: confirm point is not beyond limits of alignment
|
||||||
|
|
||||||
s = ifcopenshell.geom.settings()
|
s = ifcopenshell.geom.settings()
|
||||||
piecewise_function = ifcopenshell_wrapper.map_shape(s, shape_rep.wrapped_data)
|
function_item = ifcopenshell_wrapper.map_shape(s, shape_rep.wrapped_data)
|
||||||
pwf_evaluator = ifcopenshell_wrapper.piecewise_function_evaluator(piecewise_function, s)
|
evaluator = ifcopenshell_wrapper.function_item_evaluator(s, function_item)
|
||||||
|
|
||||||
trans_matrix = pwf_evaluator.evaluate(dist_along)
|
trans_matrix = evaluator.evaluate(dist_along)
|
||||||
|
|
||||||
return np.array(trans_matrix, dtype=np.float64).T
|
return np.array(trans_matrix, dtype=np.float64).T
|
||||||
|
|
||||||
@@ -68,10 +70,10 @@ def evaluate_segment(segment: entity_instance, dist_along: float) -> np.ndarray:
|
|||||||
raise ValueError(f"Provided value {dist_along=} is beyond the end of the segment ({segment.SegmentLength}).")
|
raise ValueError(f"Provided value {dist_along=} is beyond the end of the segment ({segment.SegmentLength}).")
|
||||||
|
|
||||||
s = ifcopenshell.geom.settings()
|
s = ifcopenshell.geom.settings()
|
||||||
piecewise_function = ifcopenshell_wrapper.map_shape(s, segment.wrapped_data)
|
function_item = ifcopenshell_wrapper.map_shape(s, segment.wrapped_data)
|
||||||
pwf_evaluator = ifcopenshell_wrapper.piecewise_function_evaluator(piecewise_function, s)
|
evaluator = ifcopenshell_wrapper.function_item_evaluator(s, function_item)
|
||||||
|
|
||||||
trans_matrix = pwf_evaluator.evaluate(dist_along)
|
trans_matrix = evaluator.evaluate(dist_along)
|
||||||
|
|
||||||
return np.array(trans_matrix, dtype=np.float64).T
|
return np.array(trans_matrix, dtype=np.float64).T
|
||||||
|
|
||||||
@@ -90,7 +92,8 @@ def generate_vertices(rep_curve: entity_instance, distance_interval: float = 5.0
|
|||||||
raise ValueError("Alignment representation not found.")
|
raise ValueError("Alignment representation not found.")
|
||||||
|
|
||||||
s = ifcopenshell.geom.settings()
|
s = ifcopenshell.geom.settings()
|
||||||
s.set("PIECEWISE_STEP_PARAM", distance_interval)
|
s.set("piecewise-step-type",0) # 0 = step-size is maximum step size, 1 = step-size is mininimum number of steps
|
||||||
|
s.set("piecewise-step-size", distance_interval)
|
||||||
shape = ifcopenshell.geom.create_shape(s, rep_curve)
|
shape = ifcopenshell.geom.create_shape(s, rep_curve)
|
||||||
vertices = shape.verts
|
vertices = shape.verts
|
||||||
if len(vertices) == 0:
|
if len(vertices) == 0:
|
||||||
@@ -185,6 +188,129 @@ class IfcAlignmentHelper:
|
|||||||
alignment_segment.ObjectPlacement = global_placement
|
alignment_segment.ObjectPlacement = global_placement
|
||||||
alignment_segment.Representation = product
|
alignment_segment.Representation = product
|
||||||
|
|
||||||
|
def _map_alignment_vertical_segment(self, segment: entity_instance) -> Sequence[entity_instance]:
|
||||||
|
segment_type = segment.is_a().upper()
|
||||||
|
expected_type = "IFCALIGNMENTVERTICALSEGMENT"
|
||||||
|
if not segment_type == expected_type:
|
||||||
|
raise TypeError(f"Expected to see type '{expected_type}', instead received '{segment_type}'.")
|
||||||
|
|
||||||
|
start_distance_along = segment.StartDistAlong
|
||||||
|
horizontal_length = segment.HorizontalLength
|
||||||
|
start_height = segment.StartHeight
|
||||||
|
start_gradient = segment.StartGradient
|
||||||
|
end_gradient = segment.EndGradient
|
||||||
|
radius_of_curvature = segment.RadiusOfCurvature
|
||||||
|
|
||||||
|
if math.isclose(horizontal_length, 0):
|
||||||
|
# set transition value based on whether this is the final zero-length segment
|
||||||
|
transition = "DISCONTINUOUS"
|
||||||
|
else:
|
||||||
|
transition = "CONTSAMEGRADIENTSAMECURVATURE"
|
||||||
|
|
||||||
|
_type = segment.PredefinedType
|
||||||
|
|
||||||
|
match _type:
|
||||||
|
case "CONSTANTGRADIENT":
|
||||||
|
parent_curve = self._file.create_entity(
|
||||||
|
type="IfcLine",
|
||||||
|
Pnt=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(0.0,0.0),),
|
||||||
|
Dir=self._file.create_entity(type="IfcVector",
|
||||||
|
Orientation=self._file.create_entity(type="IfcDirection",DirectionRatios=(1.0,0.0),),
|
||||||
|
Magnitude=1.0,),
|
||||||
|
)
|
||||||
|
|
||||||
|
dx = math.cos(math.atan(start_gradient))
|
||||||
|
dy = math.sin(math.atan(start_gradient))
|
||||||
|
curve_segment_length = horizontal_length/dx
|
||||||
|
|
||||||
|
curve_segment = self._file.create_entity(
|
||||||
|
type="IfcCurveSegment",
|
||||||
|
Transition=transition,
|
||||||
|
Placement=self._file.create_entity(
|
||||||
|
type="IfcAxis2Placement2D",
|
||||||
|
Location=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(start_distance_along,start_height)),
|
||||||
|
RefDirection=self._file.createIfcDirection((dx,dy)),
|
||||||
|
),
|
||||||
|
SegmentStart=self._file.createIfcLengthMeasure(0.0),
|
||||||
|
SegmentLength=self._file.createIfcLengthMeasure(curve_segment_length),
|
||||||
|
ParentCurve=parent_curve,
|
||||||
|
)
|
||||||
|
result = (curve_segment, None)
|
||||||
|
|
||||||
|
case "PARABOLICARC":
|
||||||
|
A = start_height
|
||||||
|
B = start_gradient
|
||||||
|
C = (end_gradient - start_gradient)/(2.0*horizontal_length)
|
||||||
|
|
||||||
|
parent_curve = self._file.create_entity(
|
||||||
|
type="IfcPolynomialCurve",
|
||||||
|
Position=self._file.create_entity(
|
||||||
|
type="IfcAxis2Placement2D",
|
||||||
|
Location=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(0.0,0.0)),
|
||||||
|
RefDirection=self._file.createIfcDirection((1.0, 0.0),),
|
||||||
|
),
|
||||||
|
CoefficientsX=(0.0,1.0),
|
||||||
|
CoefficientsY=(A,B,C),
|
||||||
|
)
|
||||||
|
|
||||||
|
dx = math.cos(math.atan(start_gradient))
|
||||||
|
dy = math.sin(math.atan(start_gradient))
|
||||||
|
curve_segment_length = ifcopenshell_wrapper.polynomial_length(A,B,C,horizontal_length)
|
||||||
|
|
||||||
|
curve_segment = self._file.create_entity(
|
||||||
|
type="IfcCurveSegment",
|
||||||
|
Transition=transition,
|
||||||
|
Placement=self._file.create_entity(
|
||||||
|
type="IfcAxis2Placement2D",
|
||||||
|
Location=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(start_distance_along,start_height)),
|
||||||
|
RefDirection=self._file.createIfcDirection((dx,dy)),
|
||||||
|
),
|
||||||
|
SegmentStart=self._file.createIfcLengthMeasure(0.0),
|
||||||
|
SegmentLength=self._file.createIfcLengthMeasure(curve_segment_length),
|
||||||
|
ParentCurve=parent_curve,
|
||||||
|
)
|
||||||
|
result = (curve_segment, None)
|
||||||
|
|
||||||
|
case "CIRCULARARC":
|
||||||
|
start_angle = math.atan(start_gradient)
|
||||||
|
end_angle = math.atan(end_gradient)
|
||||||
|
if start_angle < end_angle:
|
||||||
|
radius = horizontal_length/(math.sin(end_angle) - math.sin(start_angle))
|
||||||
|
else:
|
||||||
|
radius = horizontal_length/(math.sin(start_angle) - math.sin(end_angle))
|
||||||
|
|
||||||
|
parent_curve = self._file.create_entity(
|
||||||
|
type="IfcCircle",
|
||||||
|
Position=self._file.create_entity(
|
||||||
|
type="IfcAxis2Placement2D",
|
||||||
|
Location=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(0.0,0.0)),
|
||||||
|
RefDirection=self._file.createIfcDirection((1.0, 0.0),),
|
||||||
|
),
|
||||||
|
Radius=radius,
|
||||||
|
)
|
||||||
|
|
||||||
|
segment_curve_length = radius*math.fabs(end_angle - start_angle)
|
||||||
|
|
||||||
|
curve_segment = self._file.create_entity(
|
||||||
|
type="IfcCurveSegment",
|
||||||
|
Transition=transition,
|
||||||
|
Placement=self._file.create_entity(
|
||||||
|
type="IfcAxis2Placement2D",
|
||||||
|
Location=self._file.create_entity(type="IfcCartesianPoint",Coordinates=(start_distance_along,start_height)),
|
||||||
|
RefDirection=self._file.createIfcDirection((1.0,0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SegmentStart=self._file.createIfcLengthMeasure(0.0),
|
||||||
|
SegmentLength=self._file.createIfcLengthMeasure(curve_segment_length),
|
||||||
|
ParentCurve=parent_curve,
|
||||||
|
)
|
||||||
|
result = (curve_segment, None)
|
||||||
|
|
||||||
|
case _:
|
||||||
|
result = (None, None)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def _map_alignment_horizontal_segment(self, segment: entity_instance) -> Sequence[entity_instance]:
|
def _map_alignment_horizontal_segment(self, segment: entity_instance) -> Sequence[entity_instance]:
|
||||||
segment_type = segment.is_a().upper()
|
segment_type = segment.is_a().upper()
|
||||||
expected_type = "IFCALIGNMENTHORIZONTALSEGMENT"
|
expected_type = "IFCALIGNMENTHORIZONTALSEGMENT"
|
||||||
@@ -550,10 +676,11 @@ class IfcAlignmentHelper:
|
|||||||
alignment.Representation = product_definition_shape
|
alignment.Representation = product_definition_shape
|
||||||
|
|
||||||
# create referent for start station
|
# create referent for start station
|
||||||
|
start_station_name = "Start Station ({})".format(ifcopenshell.util.stationing.station_as_string(start_station))
|
||||||
start_referent = self._file.createIfcReferent(
|
start_referent = self._file.createIfcReferent(
|
||||||
GlobalId=ifcopenshell.guid.new(),
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
OwnerHistory=None,
|
OwnerHistory=None,
|
||||||
Name="Start Station",
|
Name=start_station_name,
|
||||||
Description=None,
|
Description=None,
|
||||||
ObjectType=None,
|
ObjectType=None,
|
||||||
ObjectPlacement=self._file.createIfcLinearPlacement(
|
ObjectPlacement=self._file.createIfcLinearPlacement(
|
||||||
@@ -571,6 +698,8 @@ class IfcAlignmentHelper:
|
|||||||
Representation=None,
|
Representation=None,
|
||||||
PredefinedType="STATION",
|
PredefinedType="STATION",
|
||||||
)
|
)
|
||||||
|
pset_stationing = ifcopenshell.api.pset.add_pset(self._file,product=start_referent,name="Pset_Stationing")
|
||||||
|
ifcopenshell.api.pset.edit_pset(self._file,pset=pset_stationing,properties={"Station":start_station})
|
||||||
|
|
||||||
# nest the horizontal and the referent under the alignment
|
# nest the horizontal and the referent under the alignment
|
||||||
nesting_of_alignment = self._file.create_entity(
|
nesting_of_alignment = self._file.create_entity(
|
||||||
@@ -594,12 +723,11 @@ class IfcAlignmentHelper:
|
|||||||
|
|
||||||
return alignment
|
return alignment
|
||||||
|
|
||||||
def add_vertical_alignment(
|
def _create_vertical_alignment(
|
||||||
self,
|
self,
|
||||||
name: str,
|
composite_curve: entity_instance,
|
||||||
description: str,
|
|
||||||
vpoints: Sequence[Sequence[float]],
|
vpoints: Sequence[Sequence[float]],
|
||||||
vclengths: Sequence[Sequence[float]],
|
lengths: Sequence[float],
|
||||||
include_geometry: bool = True,
|
include_geometry: bool = True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -608,12 +736,353 @@ class IfcAlignmentHelper:
|
|||||||
@param name: value for Name attribute
|
@param name: value for Name attribute
|
||||||
@param description: value for Description attribute
|
@param description: value for Description attribute
|
||||||
@param vpoints: (distance_along, Z_height) pairs denoting the location of the vertical PIs, including start and end.
|
@param vpoints: (distance_along, Z_height) pairs denoting the location of the vertical PIs, including start and end.
|
||||||
@param vclengths: radii values to use for transition
|
@param vclengths: horizontal length of parabolic vertical curves
|
||||||
@param include_geometry: optionally create the alignment geometric representation as well as the semantic business logic
|
@param include_geometry: optionally create the alignment geometric representation as well as the semantic business logic
|
||||||
"""
|
"""
|
||||||
pass
|
vertical_segments = list() # business logic
|
||||||
|
vertical_curve_segments = list() # geometry
|
||||||
|
xPBG, yPBG = vpoints[0]
|
||||||
|
xPVI, yPVI = vpoints[1]
|
||||||
|
i = 1
|
||||||
|
for length in lengths:
|
||||||
|
# back gradient
|
||||||
|
dxBG = xPVI - xPBG
|
||||||
|
dyBG = yPVI - yPBG
|
||||||
|
start_slope = math.tan(math.atan2(dyBG,dxBG))
|
||||||
|
|
||||||
def add_alignment(
|
#forward gradient
|
||||||
|
i += 1
|
||||||
|
xPFG, yPFG = vpoints[i]
|
||||||
|
dxFG = xPFG - xPVI
|
||||||
|
dyFG = yPFG - yPVI
|
||||||
|
end_slope = math.tan(math.atan2(dyFG,dxFG))
|
||||||
|
|
||||||
|
xEVC = xPVI + length/2.0
|
||||||
|
yEVC = yPVI + end_slope * length/2.0
|
||||||
|
|
||||||
|
# create gradient
|
||||||
|
gradient_length = dxBG - length/2.0
|
||||||
|
design_parameters = self._file.create_entity(
|
||||||
|
type="IfcAlignmentVerticalSegment",
|
||||||
|
StartTag=None,
|
||||||
|
EndTag=None,
|
||||||
|
StartDistAlong=xPBG,
|
||||||
|
HorizontalLength=gradient_length,
|
||||||
|
StartHeight=yPBG,
|
||||||
|
StartGradient=start_slope,
|
||||||
|
EndGradient=start_slope,
|
||||||
|
RadiusOfCurvature=None,
|
||||||
|
PredefinedType="CONSTANTGRADIENT"
|
||||||
|
)
|
||||||
|
alignment_segment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentSegment",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=None,
|
||||||
|
Description=None,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
DesignParameters=design_parameters,
|
||||||
|
)
|
||||||
|
vertical_segments.append(alignment_segment)
|
||||||
|
|
||||||
|
if include_geometry:
|
||||||
|
vertical_curve_segments.append(self._map_alignment_vertical_segment(design_parameters)[0])
|
||||||
|
|
||||||
|
# create vertical curve
|
||||||
|
k = (end_slope - start_slope)/length
|
||||||
|
xBVC = xPVI - length/2.0
|
||||||
|
yBVC = yPVI - start_slope*length/2.0
|
||||||
|
|
||||||
|
design_parameters = self._file.create_entity(
|
||||||
|
type="IfcAlignmentVerticalSegment",
|
||||||
|
StartTag=None,
|
||||||
|
EndTag=None,
|
||||||
|
StartDistAlong=xBVC,
|
||||||
|
HorizontalLength=length,
|
||||||
|
StartHeight=yBVC,
|
||||||
|
StartGradient=start_slope,
|
||||||
|
EndGradient=end_slope,
|
||||||
|
RadiusOfCurvature=1/k,
|
||||||
|
PredefinedType="PARABOLICARC"
|
||||||
|
)
|
||||||
|
alignment_segment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentSegment",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=None,
|
||||||
|
Description=None,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
DesignParameters=design_parameters,
|
||||||
|
)
|
||||||
|
vertical_segments.append(alignment_segment)
|
||||||
|
|
||||||
|
if include_geometry:
|
||||||
|
vertical_curve_segments.append(self._map_alignment_vertical_segment(design_parameters)[0])
|
||||||
|
|
||||||
|
# start of next curve is end of this curve
|
||||||
|
xPBG = xEVC
|
||||||
|
yPBG = yEVC
|
||||||
|
xPVI = xPFG
|
||||||
|
yPVI = yPFG
|
||||||
|
|
||||||
|
|
||||||
|
# create last gradient run
|
||||||
|
dx = xPVI - xPBG
|
||||||
|
dy = yPVI - yPBG
|
||||||
|
slope = math.tan(math.atan2(dy,dx))
|
||||||
|
gradient_length = dx
|
||||||
|
|
||||||
|
design_parameters = self._file.create_entity(
|
||||||
|
type="IfcAlignmentVerticalSegment",
|
||||||
|
StartTag=None,
|
||||||
|
EndTag=None,
|
||||||
|
StartDistAlong=xPBG,
|
||||||
|
HorizontalLength=gradient_length,
|
||||||
|
StartHeight=yPBG,
|
||||||
|
StartGradient=slope,
|
||||||
|
EndGradient=slope,
|
||||||
|
RadiusOfCurvature=None,
|
||||||
|
PredefinedType="CONSTANTGRADIENT"
|
||||||
|
)
|
||||||
|
alignment_segment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentSegment",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=None,
|
||||||
|
Description=None,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
DesignParameters=design_parameters,
|
||||||
|
)
|
||||||
|
vertical_segments.append(alignment_segment)
|
||||||
|
|
||||||
|
if include_geometry:
|
||||||
|
vertical_curve_segments.append(self._map_alignment_vertical_segment(design_parameters)[0])
|
||||||
|
|
||||||
|
# create zero length terminator segment
|
||||||
|
design_parameters = self._file.create_entity(
|
||||||
|
type="IfcAlignmentVerticalSegment",
|
||||||
|
StartTag="VPOE",
|
||||||
|
EndTag="VPOE",
|
||||||
|
StartDistAlong=xPVI,
|
||||||
|
HorizontalLength=0.0,
|
||||||
|
StartHeight=yPVI,
|
||||||
|
StartGradient=slope,
|
||||||
|
EndGradient=slope,
|
||||||
|
RadiusOfCurvature=None,
|
||||||
|
PredefinedType="CONSTANTGRADIENT"
|
||||||
|
)
|
||||||
|
alignment_segment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentSegment",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=None,
|
||||||
|
Description=None,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
DesignParameters=design_parameters,
|
||||||
|
)
|
||||||
|
vertical_segments.append(alignment_segment)
|
||||||
|
|
||||||
|
if include_geometry:
|
||||||
|
vertical_curve_segments.append(self._map_alignment_vertical_segment(design_parameters)[0])
|
||||||
|
|
||||||
|
if include_geometry:
|
||||||
|
gradient_curve = self._file.create_entity(
|
||||||
|
type="IfcGradientCurve",
|
||||||
|
Segments=vertical_curve_segments,
|
||||||
|
SelfIntersect=False,
|
||||||
|
BaseCurve=composite_curve,
|
||||||
|
EndPoint=None
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gradient_curve = None
|
||||||
|
|
||||||
|
return vertical_segments, vertical_curve_segments, gradient_curve
|
||||||
|
|
||||||
|
def create_alignment_by_pi_method(
|
||||||
|
self,
|
||||||
|
alignment_name: str,
|
||||||
|
points: Sequence[Sequence[float]],
|
||||||
|
radii: Sequence[float],
|
||||||
|
vpoints: Sequence[Sequence[float]],
|
||||||
|
lengths: Sequence[float],
|
||||||
|
alignment_description: str = None,
|
||||||
|
start_station: float = 1000.0,
|
||||||
|
include_geometry: bool = True
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create an alignment using the PI layout method for both horizontal and vertical alignments.
|
||||||
|
|
||||||
|
@param alignment_name: value for Name attribute
|
||||||
|
@param alignment_description: value for Description attribute
|
||||||
|
@param points: (X,Y) pairs denoting the location of the horizontal PIs, including start and end
|
||||||
|
@param radii: radii values to use for transition
|
||||||
|
@param vpoints: (distance_along, Z_height) pairs denoting the location of the vertical PIs, including start and end.
|
||||||
|
@param lengths: parabolic vertical curve horizontal length values to use for transition
|
||||||
|
@param start_station: ??? NOT USED AT THIS TIME ???
|
||||||
|
@param include_geometry: optionally create the alignment geometric representation as well as the semantic business logic
|
||||||
|
"""
|
||||||
|
|
||||||
|
horizontal_segments, horizontal_curve_segments, composite_curve = self._create_horizontal_alignment(alignment_name,alignment_description,points,radii,include_geometry)
|
||||||
|
vertical_segments, vertical_curve_segments, gradient_curve = self._create_vertical_alignment(composite_curve,vpoints,lengths)
|
||||||
|
|
||||||
|
name_segments(prefix="H",segments=horizontal_segments)
|
||||||
|
name_segments(prefix="V",segments=vertical_segments)
|
||||||
|
|
||||||
|
# Create the horizontal alignment (IfcAlignmentHorizontal) and nest alignment segments
|
||||||
|
horizontal_alignment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentHorizontal",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=f"{alignment_name} - Horizontal",
|
||||||
|
Description=alignment_description,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
nests_horizontal_segments = self._file.create_entity(
|
||||||
|
type="IfcRelNests",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name="Nests horizontal alignment segments under horizontal alignment",
|
||||||
|
RelatingObject=horizontal_alignment,
|
||||||
|
RelatedObjects=horizontal_segments,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the vertical alignment (IfcAlignmentVertical) and nest alignment segments
|
||||||
|
vertical_alignment = self._file.create_entity(
|
||||||
|
type="IfcAlignmentVertical",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=f"{alignment_name} - Vertical",
|
||||||
|
Description=alignment_description,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=None,
|
||||||
|
Representation=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
nests_vertical_segments = self._file.create_entity(
|
||||||
|
type="IfcRelNests",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name="Nests vertical alignment segments under vertical alignment",
|
||||||
|
RelatingObject=vertical_alignment,
|
||||||
|
RelatedObjects=vertical_segments,
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the alignment
|
||||||
|
placement = self._file.createIfcLocalPlacement(
|
||||||
|
PlacementRelTo=None,
|
||||||
|
RelativePlacement=self._file.createIfcAxis2Placement2D(
|
||||||
|
Location=self._file.createIfcCartesianPoint(Coordinates=(0.0, 0.0))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
alignment = self._file.create_entity(
|
||||||
|
type="IfcAlignment",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=alignment_name,
|
||||||
|
Description=alignment_description,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=placement,
|
||||||
|
Representation=None,
|
||||||
|
PredefinedType=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
# create referent for start station
|
||||||
|
start_station_name = "Start Station ({})".format(ifcopenshell.util.stationing.station_as_string(start_station))
|
||||||
|
start_referent = self._file.createIfcReferent(
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name=start_station_name,
|
||||||
|
Description=None,
|
||||||
|
ObjectType=None,
|
||||||
|
ObjectPlacement=self._file.createIfcLinearPlacement(
|
||||||
|
RelativePlacement=self._file.createIfcAxis2PlacementLinear(
|
||||||
|
Location=self._file.createIfcPointByDistanceExpression(
|
||||||
|
DistanceAlong=self._file.createIfcLengthMeasure(0.0),
|
||||||
|
OffsetLateral=None,
|
||||||
|
OffsetVertical=None,
|
||||||
|
OffsetLongitudinal=None,
|
||||||
|
BasisCurve=composite_curve,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CartesianPosition=None,
|
||||||
|
),
|
||||||
|
Representation=None,
|
||||||
|
PredefinedType="STATION",
|
||||||
|
)
|
||||||
|
pset_stationing = ifcopenshell.api.pset.add_pset(self._file,product=start_referent,name="Pset_Stationing")
|
||||||
|
ifcopenshell.api.pset.edit_pset(self._file,pset=pset_stationing,properties={"Station":start_station})
|
||||||
|
|
||||||
|
# nest the horizontal, vertical and the referent under the alignment
|
||||||
|
nesting_of_alignment = self._file.create_entity(
|
||||||
|
type="IfcRelNests",
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name="Nests horizontal alignment, vertical alginment, and referents under overall alignment",
|
||||||
|
RelatingObject=alignment,
|
||||||
|
RelatedObjects=(horizontal_alignment, vertical_alignment, start_referent),
|
||||||
|
)
|
||||||
|
|
||||||
|
# aggregate the alignment under the project
|
||||||
|
project = self._file.by_type("IfcProject")[0]
|
||||||
|
alignment_within_project = self._file.createIfcRelAggregates(
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
OwnerHistory=None,
|
||||||
|
Name="Aggregates alignment under the project",
|
||||||
|
RelatingObject=project,
|
||||||
|
RelatedObjects=(alignment,),
|
||||||
|
)
|
||||||
|
|
||||||
|
# create geometric representation
|
||||||
|
if include_geometry:
|
||||||
|
# create the footprint representation
|
||||||
|
footprint_shape_representation = self._file.create_entity(
|
||||||
|
type="IfcShapeRepresentation",
|
||||||
|
ContextOfItems=self._axis_geom_subcontext,
|
||||||
|
RepresentationIdentifier="FootPrint",
|
||||||
|
RepresentationType="Curve2D",
|
||||||
|
Items=(composite_curve,),
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the Curve3D representation
|
||||||
|
axis3d_shape_representation = self._file.create_entity(
|
||||||
|
type="IfcShapeRepresentation",
|
||||||
|
ContextOfItems=self._axis_geom_subcontext,
|
||||||
|
RepresentationIdentifier="Axis",
|
||||||
|
RepresentationType="Curve3D",
|
||||||
|
Items=(gradient_curve,),
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the alignment product definition
|
||||||
|
product_definition_shape = self._file.create_entity(
|
||||||
|
type="IfcProductDefinitionShape",
|
||||||
|
Name="Alignment Product Definition Shape",
|
||||||
|
Description=None,
|
||||||
|
Representations=(footprint_shape_representation,axis3d_shape_representation,),
|
||||||
|
)
|
||||||
|
|
||||||
|
# create representations for each segment
|
||||||
|
self._create_segment_representations(placement, horizontal_curve_segments, horizontal_segments)
|
||||||
|
self._create_segment_representations(placement, vertical_curve_segments, vertical_segments)
|
||||||
|
|
||||||
|
# add the representation to the alignment
|
||||||
|
alignment.Representation = product_definition_shape
|
||||||
|
|
||||||
|
return alignment
|
||||||
|
|
||||||
|
|
||||||
|
def create_horizontal_alignment_by_pi_method(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
hpoints: Sequence[Sequence[float]],
|
hpoints: Sequence[Sequence[float]],
|
||||||
@@ -625,7 +1094,7 @@ class IfcAlignmentHelper:
|
|||||||
"""
|
"""
|
||||||
Create a new alignment with a horizontal alignment using the PI layout method
|
Create a new alignment with a horizontal alignment using the PI layout method
|
||||||
"""
|
"""
|
||||||
self._add_horizontal_alignment(
|
return self._add_horizontal_alignment(
|
||||||
alignment_name=name,
|
alignment_name=name,
|
||||||
points=hpoints,
|
points=hpoints,
|
||||||
radii=radii,
|
radii=radii,
|
||||||
@@ -642,7 +1111,19 @@ if __name__ == "__main__":
|
|||||||
import sys
|
import sys
|
||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
|
|
||||||
f = ifcopenshell.open(sys.argv[1])
|
f = ifcopenshell.file(schema="IFC4X3_ADD2")
|
||||||
|
project = f.create_entity(type="IfcProject",GlobalId=ifcopenshell.guid.new())
|
||||||
|
context = f.create_entity(type="IfcGeometricRepresentationContext")
|
||||||
|
|
||||||
|
points=[(0.,0.),(100.,0.),(200.,150.)]
|
||||||
|
radii=[(50.)]
|
||||||
|
|
||||||
|
helper = IfcAlignmentHelper(f)
|
||||||
|
helper.create_horizontal_alignment_by_pi_method(
|
||||||
|
name="MyAlignment",hpoints = points,radii = radii
|
||||||
|
)
|
||||||
|
|
||||||
|
#f = ifcopenshell.open(sys.argv[1])
|
||||||
print_structure(f.by_type("IfcAlignment")[0])
|
print_structure(f.by_type("IfcAlignment")[0])
|
||||||
|
|
||||||
al_hor_rep = f.by_type("IfcCompositeCurve")[0]
|
al_hor_rep = f.by_type("IfcCompositeCurve")[0]
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ Ifc4x3_add2::IfcAlignment* addHorizontalAlignment(IfcHierarchyHelper<Ifc4x3_add2
|
|||||||
|
|
||||||
placement = file.addLocalPlacement();
|
placement = file.addLocalPlacement();
|
||||||
|
|
||||||
// the alignment has two representations, a plan view footprint
|
// the alignment has a plan view footprint representation
|
||||||
typename aggregate_of<Ifc4x3_add2::IfcRepresentation>::ptr alignment_representations(new aggregate_of<Ifc4x3_add2::IfcRepresentation>());
|
typename aggregate_of<Ifc4x3_add2::IfcRepresentation>::ptr alignment_representations(new aggregate_of<Ifc4x3_add2::IfcRepresentation>());
|
||||||
alignment_representations->push(footprint_shape_representation); // 2D footprint
|
alignment_representations->push(footprint_shape_representation); // 2D footprint
|
||||||
|
|
||||||
@@ -258,7 +258,6 @@ std::tuple<typename aggregate_of<Ifc4x3_add2::IfcObjectDefinition>::ptr, typenam
|
|||||||
|
|
||||||
// create gradient
|
// create gradient
|
||||||
{
|
{
|
||||||
file.addDoublet<Ifc4x3_add2::IfcCartesianPoint>(xPBG, yPBG);
|
|
||||||
auto gradient_length = dxBG - length/2;
|
auto gradient_length = dxBG - length/2;
|
||||||
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, start_slope, start_slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, start_slope, start_slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||||
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||||
@@ -274,8 +273,6 @@ std::tuple<typename aggregate_of<Ifc4x3_add2::IfcObjectDefinition>::ptr, typenam
|
|||||||
double xBVC = xPVI - length / 2;
|
double xBVC = xPVI - length / 2;
|
||||||
double yBVC = yPVI - start_slope * length / 2;
|
double yBVC = yPVI - start_slope * length / 2;
|
||||||
|
|
||||||
file.addDoublet<Ifc4x3_add2::IfcCartesianPoint>(xBVC, yBVC);
|
|
||||||
|
|
||||||
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xBVC, length, yBVC, start_slope, end_slope, 1 / k, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xBVC, length, yBVC, start_slope, end_slope, 1 / k, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
||||||
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||||
vertical_segments->push(alignment_segment);
|
vertical_segments->push(alignment_segment);
|
||||||
@@ -296,7 +293,6 @@ std::tuple<typename aggregate_of<Ifc4x3_add2::IfcObjectDefinition>::ptr, typenam
|
|||||||
auto slope = tan(atan2(dy,dx));
|
auto slope = tan(atan2(dy,dx));
|
||||||
auto gradient_length = dx;
|
auto gradient_length = dx;
|
||||||
|
|
||||||
file.addDoublet<Ifc4x3_add2::IfcCartesianPoint>(xPBG, yPBG);
|
|
||||||
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||||
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||||
vertical_segments->push(alignment_segment);
|
vertical_segments->push(alignment_segment);
|
||||||
@@ -305,7 +301,6 @@ std::tuple<typename aggregate_of<Ifc4x3_add2::IfcObjectDefinition>::ptr, typenam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create zero length terminator segment
|
// create zero length terminator segment
|
||||||
file.addDoublet<Ifc4x3_add2::IfcCartesianPoint>(xPVI, yPVI);
|
|
||||||
design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPVI, 0.0, yPVI, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPVI, 0.0, yPVI, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||||
alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||||
vertical_segments->push(alignment_segment);
|
vertical_segments->push(alignment_segment);
|
||||||
@@ -358,14 +353,14 @@ Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper<Ifc4x3_add2>& file, c
|
|||||||
typename aggregate_of<Ifc4x3_add2::IfcRepresentationItem>::ptr alignment_representation_items(new aggregate_of<Ifc4x3_add2::IfcRepresentationItem>());
|
typename aggregate_of<Ifc4x3_add2::IfcRepresentationItem>::ptr alignment_representation_items(new aggregate_of<Ifc4x3_add2::IfcRepresentationItem>());
|
||||||
alignment_representation_items->push(composite_curve);
|
alignment_representation_items->push(composite_curve);
|
||||||
|
|
||||||
// create the footprint representation
|
|
||||||
auto footprint_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items);
|
|
||||||
file.addEntity(footprint_shape_representation);
|
|
||||||
|
|
||||||
// the gradient curve is a representation item
|
// the gradient curve is a representation item
|
||||||
typename aggregate_of<typename Ifc4x3_add2::IfcRepresentationItem>::ptr profile_representation_items(new aggregate_of<Ifc4x3_add2::IfcRepresentationItem>());
|
typename aggregate_of<typename Ifc4x3_add2::IfcRepresentationItem>::ptr profile_representation_items(new aggregate_of<Ifc4x3_add2::IfcRepresentationItem>());
|
||||||
profile_representation_items->push(gradient_curve);
|
profile_representation_items->push(gradient_curve);
|
||||||
|
|
||||||
|
// create footprint representation
|
||||||
|
auto footprint_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items);
|
||||||
|
file.addEntity(footprint_shape_representation);
|
||||||
|
|
||||||
// create the axis representation
|
// create the axis representation
|
||||||
auto axis3d_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("Axis"), std::string("Curve3D"), profile_representation_items);
|
auto axis3d_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("Axis"), std::string("Curve3D"), profile_representation_items);
|
||||||
file.addEntity(axis3d_shape_representation);
|
file.addEntity(axis3d_shape_representation);
|
||||||
@@ -375,10 +370,10 @@ Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper<Ifc4x3_add2>& file, c
|
|||||||
_createSegmentRepresentations(file, placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments);
|
_createSegmentRepresentations(file, placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments);
|
||||||
_createSegmentRepresentations(file, placement, axis_model_representation_subcontext, vertical_curve_segments, vertical_segments);
|
_createSegmentRepresentations(file, placement, axis_model_representation_subcontext, vertical_curve_segments, vertical_segments);
|
||||||
|
|
||||||
// the alignment has two representations, a plan view footprint and a 3d curve
|
// the alignment has a 3d curve representation
|
||||||
typename aggregate_of<typename Ifc4x3_add2::IfcRepresentation>::ptr alignment_representations(new aggregate_of<Ifc4x3_add2::IfcRepresentation>());
|
typename aggregate_of<typename Ifc4x3_add2::IfcRepresentation>::ptr alignment_representations(new aggregate_of<Ifc4x3_add2::IfcRepresentation>());
|
||||||
alignment_representations->push(footprint_shape_representation); // 2D footprint
|
alignment_representations->push(footprint_shape_representation); // 2D curve
|
||||||
alignment_representations->push(axis3d_shape_representation); // 3D curve
|
alignment_representations->push(axis3d_shape_representation); // 3D curve
|
||||||
|
|
||||||
// create the alignment product definition
|
// create the alignment product definition
|
||||||
product_definition_shape = new Ifc4x3_add2::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations);
|
product_definition_shape = new Ifc4x3_add2::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations);
|
||||||
|
|||||||
Reference in New Issue
Block a user