mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
Implements IfcSegmentedReferenceCurve and first cut at cant geometry
This commit is contained in:
@@ -40,14 +40,20 @@ auto compute_adjustment = [](double u, const T& a, const T& b, double l) -> doub
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
enum segment_type_t {
|
||||||
|
ST_HORIZONTAL,
|
||||||
|
ST_VERTICAL,
|
||||||
|
ST_CANT
|
||||||
|
};
|
||||||
|
|
||||||
// @todo use std::numbers::pi when upgrading to C++ 20
|
// @todo use std::numbers::pi when upgrading to C++ 20
|
||||||
static const double PI = boost::math::constants::pi<double>();
|
static const double PI = boost::math::constants::pi<double>();
|
||||||
|
|
||||||
// Current implementation uses the same segment_geometry_adjuster for all ParentCurve types.
|
// Current implementation uses the same segment_geometry_adjuster for all ParentCurve types.
|
||||||
// Comment/Uncomment to change the type of segment geometry adjuster
|
// Comment/Uncomment to change the type of segment geometry adjuster
|
||||||
// Future implementations could use specialized adjusters based on ParentCurve type
|
// Future implementations could use specialized adjusters based on ParentCurve type
|
||||||
//#define GEOMETRY_ADJUSTER segment_geometry_adjuster
|
#define GEOMETRY_ADJUSTER segment_geometry_adjuster
|
||||||
#define GEOMETRY_ADJUSTER linear_segment_geometry_adjuster
|
//#define GEOMETRY_ADJUSTER linear_segment_geometry_adjuster
|
||||||
|
|
||||||
// Curve segments are evaluated using a parametric function over the curve length, u
|
// Curve segments are evaluated using a parametric function over the curve length, u
|
||||||
// IfcCurveSegment.TransitionCode defines how the end of a segment connects to the next segment.
|
// IfcCurveSegment.TransitionCode defines how the end of a segment connects to the next segment.
|
||||||
@@ -73,13 +79,13 @@ static const double PI = boost::math::constants::pi<double>();
|
|||||||
// function to specialize the refinement of the placement at u.
|
// function to specialize the refinement of the placement at u.
|
||||||
class segment_geometry_adjuster {
|
class segment_geometry_adjuster {
|
||||||
public:
|
public:
|
||||||
segment_geometry_adjuster(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst) :
|
segment_geometry_adjuster(mapping* mapping, segment_type_t segment_type,const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst) :
|
||||||
end_of_inst_(Eigen::Matrix4d::Identity()),
|
end_of_inst_(Eigen::Matrix4d::Identity()),
|
||||||
start_of_next_inst_(Eigen::Matrix4d::Identity()),
|
start_of_next_inst_(Eigen::Matrix4d::Identity()),
|
||||||
transition_code_(inst->Transition())
|
transition_code_(inst->Transition())
|
||||||
{
|
{
|
||||||
|
|
||||||
transformation_matrix_ = taxonomy::cast<taxonomy::matrix4>(mapping->map(inst->Placement()))->ccomponents();
|
transformation_matrix_ = taxonomy::cast<taxonomy::matrix4>(mapping->map(inst->Placement()))->ccomponents();
|
||||||
|
|
||||||
length_ = fabs(*inst->SegmentLength()->as<IfcSchema::IfcLengthMeasure>() * mapping->get_length_unit());
|
length_ = fabs(*inst->SegmentLength()->as<IfcSchema::IfcLengthMeasure>() * mapping->get_length_unit());
|
||||||
|
|
||||||
if (next_inst) {
|
if (next_inst) {
|
||||||
@@ -90,6 +96,28 @@ class segment_geometry_adjuster {
|
|||||||
// need to traverse the IfcCurveSegment objects in reverse order to avoid recursion.
|
// need to traverse the IfcCurveSegment objects in reverse order to avoid recursion.
|
||||||
auto next = taxonomy::cast<taxonomy::piecewise_function>(mapping->map(next_inst));
|
auto next = taxonomy::cast<taxonomy::piecewise_function>(mapping->map(next_inst));
|
||||||
start_of_next_inst_ = next->evaluate(0.0);
|
start_of_next_inst_ = next->evaluate(0.0);
|
||||||
|
} else {
|
||||||
|
// there is not a next segment, however IfcGradientCurve and IfcSegmentedRefernceCurve
|
||||||
|
// have an optional EndPoint attribute that serves the same purpose as the zero-length
|
||||||
|
// "next segment" at the end of the curve. The Ifc specification is a little redundant
|
||||||
|
// in that the "zero length" segment is required thereby negating the need for EndPoint
|
||||||
|
// but some implementations use the EndPoint instead of the "zero length" segment
|
||||||
|
//
|
||||||
|
// Get the parent of this segment. If it is a IfcGradientCurve or IfcSegmentedRefernceCurve
|
||||||
|
// look for the optional EndPoint attribute
|
||||||
|
auto curves = inst->UsingCurves();
|
||||||
|
auto curve = *curves->begin();
|
||||||
|
const IfcSchema::IfcPlacement* placement = nullptr;
|
||||||
|
if (curve->as<IfcSchema::IfcSegmentedReferenceCurve>()) {
|
||||||
|
auto s = curve->as<IfcSchema::IfcSegmentedReferenceCurve>();
|
||||||
|
placement = s->EndPoint();
|
||||||
|
} else if (curve->as<IfcSchema::IfcGradientCurve>()) {
|
||||||
|
auto s = curve->as<IfcSchema::IfcGradientCurve>();
|
||||||
|
placement = s->EndPoint();
|
||||||
|
}
|
||||||
|
if (placement) {
|
||||||
|
start_of_next_inst_ = taxonomy::cast<taxonomy::matrix4>(mapping->map(placement))->ccomponents();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +158,6 @@ class segment_geometry_adjuster {
|
|||||||
IfcSchema::IfcTransitionCode::Value get_transition_code() const { return transition_code_; }
|
IfcSchema::IfcTransitionCode::Value get_transition_code() const { return transition_code_; }
|
||||||
double get_length() const { return length_; }
|
double get_length() const { return length_; }
|
||||||
|
|
||||||
private:
|
|
||||||
bool adjustments_ = true;
|
bool adjustments_ = true;
|
||||||
Eigen::Matrix4d transformation_matrix_;
|
Eigen::Matrix4d transformation_matrix_;
|
||||||
Eigen::Matrix4d end_of_inst_;
|
Eigen::Matrix4d end_of_inst_;
|
||||||
@@ -191,6 +218,77 @@ class linear_segment_geometry_adjuster : public segment_geometry_adjuster {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// specializes segment_geometry_adjuster for cant segments.
|
||||||
|
// The specification for IfcSegmentedReferenceCurve provides the requirements for
|
||||||
|
// how the cant deviates from the base curve and how the cant transitions over
|
||||||
|
// the length of an IfcCurveSegment. The exact requirements are unclear. For this
|
||||||
|
// reason, the following implementation may not conform with the IFC specification.
|
||||||
|
//
|
||||||
|
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcSegmentedReferenceCurve.htm
|
||||||
|
//
|
||||||
|
// The treatment of cant geometry is as follows in this class:
|
||||||
|
// 1) Superelevation (depression or elevation) from the axis of the base curve.
|
||||||
|
// From 8.9.3.62
|
||||||
|
// "A deviating explicit position of a curve segment (IfcCurveSegment.Placement) from the axis of the base
|
||||||
|
// curve produces a superelevation i.e. depression or elevation from the axis of the base curve."
|
||||||
|
//
|
||||||
|
// Nothing in the specification indicates that the deviation from the axis of the base curve is to be interpolated.
|
||||||
|
// However, this would result in the cant elevation deviation being constant along each segment and there would
|
||||||
|
// potentially be abrupt changes in elevation at segment boundaries.
|
||||||
|
//
|
||||||
|
// To address this, the cant at a point along a segment is interpolated between IfcCurveSegment.Placement.Location.Y for placement
|
||||||
|
// at the start of the current segment and the start of the next segment. If there is not a next segment, the optional
|
||||||
|
// IfcSegmentedReferenceCurve.EndPoint attribute is used if present.
|
||||||
|
//
|
||||||
|
// For simplicity in matrix operations, the Location.Z values are also interpolated. Though, they can reasonably be
|
||||||
|
// expected to be 0.0 because cant is, in part, a vertical deviation from the IfcGradientCurve basis.
|
||||||
|
//
|
||||||
|
// 2) Determination of Axis and RefDirection
|
||||||
|
// From 8.9.3.62
|
||||||
|
// "The superelevation rate of change is directly proportionate to the curve segment parent curve curvature gradient
|
||||||
|
// equation (IfcCurveSegment.ParentCurve) in the linear parameter space of the base curve. If no deviation in the position
|
||||||
|
// of the curve segment to the base curve axis is specified, the axes (Axis and RefDirection) directions of IfcAxis2Placement
|
||||||
|
// are interpolated between the initial curve segment placement and the placement of the subsequent curve segment."
|
||||||
|
//
|
||||||
|
// This seems to say that the type of the IfcCurveSegment.ParentCurve is related to the rate of change of the Axis and RefDirection
|
||||||
|
// vectors along the length of the segment. The rate of change is understood to be equal to the derivative of the curvature of
|
||||||
|
// the IfcCurve subtype.
|
||||||
|
//
|
||||||
|
// However, if the IfcCureSegment.Placement does not deviate from the basic curve (which occurs with a deviation of 0.0), ignore
|
||||||
|
// the IfcCurveSegment.ParentCurve type and linearly interpolate the Axis and RefDirection vectors from the stat of this and
|
||||||
|
// the next segment.
|
||||||
|
//
|
||||||
|
// For now, the derivative of the curvature of the IfcCurve subtype is difficult to implement and example models from the IFC spec
|
||||||
|
// always use IfcAxis2Placement3D with Axis and RefDirection specified, the basic interpolation is used, ignoring the IfcCurve type.
|
||||||
|
//
|
||||||
|
// This implementation will be revised as the understanding of IfcSegmentedRefereneCurve improves.
|
||||||
|
class cant_adjuster : public segment_geometry_adjuster {
|
||||||
|
public:
|
||||||
|
using segment_geometry_adjuster::segment_geometry_adjuster;
|
||||||
|
|
||||||
|
virtual void transform_and_adjust(double u, Eigen::Matrix4d& p) const {
|
||||||
|
// don't call parent class version
|
||||||
|
auto& start_this = get_start_of_segment();
|
||||||
|
auto& start_next = get_start_of_next_segment();
|
||||||
|
auto l = get_length();
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
p.col(i) = start_this.col(i) + (start_next.col(i) - start_this.col(i)) * u / l;
|
||||||
|
if (i < 3) {
|
||||||
|
p.col(i).normalize();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// when cant results are combined with the gradient curve
|
||||||
|
// the x-locate will be added which effective doubles them
|
||||||
|
// for this reason, set x location to 0
|
||||||
|
p.col(3)(0) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const Eigen::Matrix4d& get_start_of_segment() const { return transformation_matrix_; }
|
||||||
|
};
|
||||||
|
|
||||||
typedef boost::mpl::vector<
|
typedef boost::mpl::vector<
|
||||||
IfcSchema::IfcLine
|
IfcSchema::IfcLine
|
||||||
#ifdef SCHEMA_HAS_IfcClothoid
|
#ifdef SCHEMA_HAS_IfcClothoid
|
||||||
@@ -204,10 +302,6 @@ typedef boost::mpl::vector<
|
|||||||
, IfcSchema::IfcPolynomialCurve
|
, IfcSchema::IfcPolynomialCurve
|
||||||
> curve_seg_types;
|
> curve_seg_types;
|
||||||
|
|
||||||
enum segment_type_t {
|
|
||||||
ST_HORIZONTAL, ST_VERTICAL, ST_CANT
|
|
||||||
};
|
|
||||||
|
|
||||||
class curve_segment_evaluator {
|
class curve_segment_evaluator {
|
||||||
private:
|
private:
|
||||||
mapping* mapping_;
|
mapping* mapping_;
|
||||||
@@ -283,14 +377,11 @@ class curve_segment_evaluator {
|
|||||||
L = fabs(start_); // start_ and length_ are in opposite directions so start_ is furthest from the origin
|
L = fabs(start_); // start_ and length_ are in opposite directions so start_ is furthest from the origin
|
||||||
}
|
}
|
||||||
|
|
||||||
auto transformation_matrix = taxonomy::cast<taxonomy::matrix4>(mapping_->map(c->Position()))->ccomponents();
|
|
||||||
|
|
||||||
auto start = start_;
|
|
||||||
|
|
||||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
|
||||||
|
|
||||||
if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL) {
|
if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL) {
|
||||||
|
auto start = start_;
|
||||||
auto segment_type = segment_type_;
|
auto segment_type = segment_type_;
|
||||||
|
auto transformation_matrix = taxonomy::cast<taxonomy::matrix4>(mapping_->map(c->Position()))->ccomponents();
|
||||||
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
eval_ = [L, start, s, signX, fnX, signY, fnY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
eval_ = [L, start, s, signX, fnX, signY, fnY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||||
|
|
||||||
u += start;
|
u += start;
|
||||||
@@ -332,11 +423,13 @@ class curve_segment_evaluator {
|
|||||||
Eigen::Matrix4d result = transformation_matrix * m;
|
Eigen::Matrix4d result = transformation_matrix * m;
|
||||||
return geometry_adjuster->transform_and_adjust(u,result);
|
return geometry_adjuster->transform_and_adjust(u,result);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (segment_type_ == ST_CANT) {
|
else if (segment_type_ == ST_CANT) {
|
||||||
eval_ = [geometry_adjuster = this->geometry_adjuster](double u) {
|
auto cant_adjuster_ = std::make_shared<cant_adjuster>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
Eigen::Matrix4d result;
|
eval_ = [cant_adjuster_](double u) {
|
||||||
return geometry_adjuster->transform_and_adjust(u, result);
|
Eigen::Matrix4d result = Eigen::Matrix4d::Identity();
|
||||||
|
cant_adjuster_->transform_and_adjust(u, result);
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -393,25 +486,27 @@ class curve_segment_evaluator {
|
|||||||
#ifdef SCHEMA_HAS_IfcClothoid
|
#ifdef SCHEMA_HAS_IfcClothoid
|
||||||
// Then initialize Function(double) -> Vector3, by means of IfcCurve subtypes
|
// Then initialize Function(double) -> Vector3, by means of IfcCurve subtypes
|
||||||
void operator()(const IfcSchema::IfcClothoid* c) {
|
void operator()(const IfcSchema::IfcClothoid* c) {
|
||||||
// see https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcClothoid.htm
|
|
||||||
// also see, https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Partial_Templates/Geometry/Curve_Segment_Geometry/Clothoid_Transition_Segment/content.html,
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
// which defines the clothoid constant as sqrt(L) and L is the length measured from the inflection point
|
// see https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcClothoid.htm
|
||||||
auto A = c->ClothoidConstant();
|
// also see, https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Partial_Templates/Geometry/Curve_Segment_Geometry/Clothoid_Transition_Segment/content.html,
|
||||||
|
// which defines the clothoid constant as sqrt(L) and L is the length measured from the inflection point
|
||||||
|
auto A = c->ClothoidConstant();
|
||||||
auto s = fabs(A * sqrt(PI));
|
auto s = fabs(A * sqrt(PI));
|
||||||
|
|
||||||
// the integration is for the +X, +Y quadrant - need to adjust the signs of the resulting X and Y values
|
// the integration is for the +X, +Y quadrant - need to adjust the signs of the resulting X and Y values
|
||||||
// so that the results are in the correct quadrant.
|
// so that the results are in the correct quadrant.
|
||||||
// A > 0 and u > 0 -> +X, +Y
|
// A > 0 and u > 0 -> +X, +Y
|
||||||
// A < 0 and u > 0 -> +X, -Y
|
// A < 0 and u > 0 -> +X, -Y
|
||||||
// A > 0 and u < 0 -> -X, -Y
|
// A > 0 and u < 0 -> -X, -Y
|
||||||
// A < 0 and u < 0 -> -X, +Y
|
// A < 0 and u < 0 -> -X, +Y
|
||||||
// X depends only on u, Y depends on u and A.
|
// X depends only on u, Y depends on u and A.
|
||||||
auto sign_x = [](double t) {return sign(t); };
|
auto sign_x = [](double t) { return sign(t); };
|
||||||
auto sign_y = [A](double t) {return sign(t) == sign(A) ? 1.0 : -1.0; };
|
auto sign_y = [A](double t) { return sign(t) == sign(A) ? 1.0 : -1.0; };
|
||||||
auto fn_x = [A,s](double t)->double {return s * cos(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
auto fn_x = [A, s](double t) -> double { return s * cos(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
||||||
auto fn_y = [A,s](double t)->double {return s * sin(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
auto fn_y = [A, s](double t) -> double { return s * sin(PI * fabs(A) * t * t / (2 * fabs(A))); };
|
||||||
|
|
||||||
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y);
|
set_spiral_function(mapping_, c, s, sign_x, fn_x, sign_y, fn_y);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -453,7 +548,7 @@ class curve_segment_evaluator {
|
|||||||
|
|
||||||
auto segment_type = segment_type_;
|
auto segment_type = segment_type_;
|
||||||
|
|
||||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
|
|
||||||
eval_ = [R, start, sign_l, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u)
|
eval_ = [R, start, sign_l, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u)
|
||||||
{
|
{
|
||||||
@@ -465,7 +560,7 @@ class curve_segment_evaluator {
|
|||||||
auto x = R * dx;
|
auto x = R * dx;
|
||||||
auto y = R * dy;
|
auto y = R * dy;
|
||||||
|
|
||||||
Eigen::Matrix4d m;
|
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||||
if (segment_type == ST_HORIZONTAL) {
|
if (segment_type == ST_HORIZONTAL) {
|
||||||
// rotate about the Z-axis
|
// rotate about the Z-axis
|
||||||
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
||||||
@@ -552,7 +647,7 @@ class curve_segment_evaluator {
|
|||||||
auto x = segment_type == ST_HORIZONTAL ? p1x + u * dx : u;
|
auto x = segment_type == ST_HORIZONTAL ? p1x + u * dx : u;
|
||||||
auto y = p1y + u * dy;
|
auto y = p1y + u * dy;
|
||||||
|
|
||||||
Eigen::Matrix4d m;
|
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||||
if (segment_type == ST_HORIZONTAL) {
|
if (segment_type == ST_HORIZONTAL) {
|
||||||
// rotate about the Z-axis
|
// rotate about the Z-axis
|
||||||
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
||||||
@@ -581,7 +676,7 @@ class curve_segment_evaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
|
|
||||||
eval_ = [fns, geometry_adjuster = this->geometry_adjuster](double u) {
|
eval_ = [fns, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||||
auto iter = std::find_if(fns.cbegin(), fns.cend(), [=](const auto& fn)
|
auto iter = std::find_if(fns.cbegin(), fns.cend(), [=](const auto& fn)
|
||||||
@@ -610,22 +705,22 @@ class curve_segment_evaluator {
|
|||||||
auto dx = dr[0] / m;
|
auto dx = dr[0] / m;
|
||||||
auto dy = dr[1] / m;
|
auto dy = dr[1] / m;
|
||||||
|
|
||||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
if (segment_type_ == ST_HORIZONTAL) {
|
if (segment_type_ == ST_HORIZONTAL) {
|
||||||
|
|
||||||
eval_ = [px, py, dx, dy, geometry_adjuster=this->geometry_adjuster](double u) {
|
eval_ = [px, py, dx, dy, geometry_adjuster=this->geometry_adjuster](double u) {
|
||||||
auto x = px + u * dx;
|
auto x = px + u * dx;
|
||||||
auto y = py + u * dy;
|
auto y = py + u * dy;
|
||||||
|
|
||||||
Eigen::Matrix4d m;
|
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||||
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
m.col(0) = Eigen::Vector4d(dx, dy, 0, 0); // vector tangent to the curve, in the direction of the curve
|
||||||
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0); // vector perpendicular to the curve, towards the left when looking from start to end along the curve (this is used for IfcAxis2PlacementLinear.RefDirection when it is not provided)
|
m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0); // vector perpendicular to the curve, towards the left when looking from start to end along the curve (this is used for IfcAxis2PlacementLinear.RefDirection when it is not provided)
|
||||||
m.col(2) = Eigen::Vector4d(0, 0, 1.0, 0); // cross product of x and y and will always be up (this is used for IfcAxis2PlacementLinear.Axis when it is not provided)
|
m.col(2) = Eigen::Vector4d(0, 0, 1.0, 0); // cross product of x and y and will always be up (this is used for IfcAxis2PlacementLinear.Axis when it is not provided)
|
||||||
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
|
m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0);
|
||||||
return geometry_adjuster->transform_and_adjust(u, m);
|
return geometry_adjuster->transform_and_adjust(u, m);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (segment_type_ == ST_VERTICAL || segment_type_ == ST_CANT) {
|
else if (segment_type_ == ST_VERTICAL) {
|
||||||
|
|
||||||
eval_ = [py, dx, dy, geometry_adjuster = this->geometry_adjuster](double u) {
|
eval_ = [py, dx, dy, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcGradientCurve.htm
|
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcGradientCurve.htm
|
||||||
@@ -639,7 +734,7 @@ class curve_segment_evaluator {
|
|||||||
// y = py + u * dy/dx = py + u * (dr[1]/m)*(m/dr[0]) = py + u * 0.05
|
// y = py + u * dy/dx = py + u * (dr[1]/m)*(m/dr[0]) = py + u * 0.05
|
||||||
auto y = py + u * dy/dx;
|
auto y = py + u * dy/dx;
|
||||||
|
|
||||||
Eigen::Matrix4d m;
|
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||||
m.col(0) = Eigen::Vector4d(dx, 0, dy, 0);
|
m.col(0) = Eigen::Vector4d(dx, 0, dy, 0);
|
||||||
m.col(1) = Eigen::Vector4d(0, 1, 0, 0);
|
m.col(1) = Eigen::Vector4d(0, 1, 0, 0);
|
||||||
m.col(2) = Eigen::Vector4d(-dy, 0, dx, 0);
|
m.col(2) = Eigen::Vector4d(-dy, 0, dx, 0);
|
||||||
@@ -647,6 +742,14 @@ class curve_segment_evaluator {
|
|||||||
return geometry_adjuster->transform_and_adjust(u, m);
|
return geometry_adjuster->transform_and_adjust(u, m);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (segment_type_ == ST_CANT) {
|
||||||
|
auto cant_adjuster_ = std::make_shared<cant_adjuster>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
|
eval_ = [cant_adjuster_](double u) {
|
||||||
|
Eigen::Matrix4d result = Eigen::Matrix4d::Identity();
|
||||||
|
cant_adjuster_->transform_and_adjust(u, result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Logger::Error(std::runtime_error("Unexpected segment type encountered"), l);
|
Logger::Error(std::runtime_error("Unexpected segment type encountered"), l);
|
||||||
}
|
}
|
||||||
@@ -664,7 +767,7 @@ class curve_segment_evaluator {
|
|||||||
|
|
||||||
auto segment_type = segment_type_;
|
auto segment_type = segment_type_;
|
||||||
|
|
||||||
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
geometry_adjuster = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, segment_type_, inst_, next_inst_);
|
||||||
|
|
||||||
|
|
||||||
eval_ = [coeffX, coeffY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
eval_ = [coeffX, coeffY, transformation_matrix, segment_type, geometry_adjuster = this->geometry_adjuster](double u) {
|
||||||
@@ -733,7 +836,7 @@ class curve_segment_evaluator {
|
|||||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
||||||
// Find the next segment after inst
|
// Find the next segment after inst
|
||||||
const IfcSchema::IfcCurveSegment* next_inst = nullptr;
|
const IfcSchema::IfcCurveSegment* next_inst = nullptr;
|
||||||
auto composite_curves = inst->data().getInverse(&IfcSchema::IfcCompositeCurve::Class(), 0);
|
auto composite_curves = inst->UsingCurves();
|
||||||
if (composite_curves) {
|
if (composite_curves) {
|
||||||
if (composite_curves->size() == 1) {
|
if (composite_curves->size() == 1) {
|
||||||
auto segments = (*composite_curves->begin())->as<IfcSchema::IfcCompositeCurve>()->Segments();
|
auto segments = (*composite_curves->begin())->as<IfcSchema::IfcCompositeCurve>()->Segments();
|
||||||
@@ -757,19 +860,16 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
|||||||
bool is_vertical = false;
|
bool is_vertical = false;
|
||||||
bool is_cant = false;
|
bool is_cant = false;
|
||||||
|
|
||||||
{
|
if (composite_curves) {
|
||||||
aggregate_of_instance::ptr segment_owners = inst->data().getInverse(&IfcSchema::IfcCompositeCurve::Class(), 0);
|
for (auto& cc : *composite_curves) {
|
||||||
if (segment_owners) {
|
if (cc->as<IfcSchema::IfcSegmentedReferenceCurve>()) {
|
||||||
for (auto& cc : *segment_owners) {
|
is_cant = true;
|
||||||
if (cc->as<IfcSchema::IfcSegmentedReferenceCurve>()) {
|
}
|
||||||
is_cant = true;
|
else if (cc->as<IfcSchema::IfcGradientCurve>()) {
|
||||||
}
|
is_vertical = true;
|
||||||
else if (cc->as<IfcSchema::IfcGradientCurve>()) {
|
}
|
||||||
is_vertical = true;
|
else {
|
||||||
}
|
is_horizontal = true;
|
||||||
else {
|
|
||||||
is_horizontal = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/********************************************************************************
|
||||||
|
* *
|
||||||
|
* This file is part of IfcOpenShell. *
|
||||||
|
* *
|
||||||
|
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the Lesser GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3.0 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 *
|
||||||
|
* Lesser GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the Lesser GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
* *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include "mapping.h"
|
||||||
|
#define mapping POSTFIX_SCHEMA(mapping)
|
||||||
|
using namespace ifcopenshell::geometry;
|
||||||
|
|
||||||
|
#ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve
|
||||||
|
|
||||||
|
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* inst) {
|
||||||
|
if (!inst->BaseCurve()->as<IfcSchema::IfcGradientCurve>())
|
||||||
|
Logger::Warning("Expected IfcSegmentedReferenceCurve.BaseCurve to be IfcGradient", inst); // CT 4.1.7.1.1.3
|
||||||
|
|
||||||
|
auto gradient = taxonomy::cast<taxonomy::piecewise_function>(map(inst->BaseCurve()));
|
||||||
|
auto cant = taxonomy::make<taxonomy::piecewise_function>();
|
||||||
|
|
||||||
|
auto segments = inst->Segments();
|
||||||
|
|
||||||
|
for (auto& segment : *segments) {
|
||||||
|
if (segment->as<IfcSchema::IfcCurveSegment>()) {
|
||||||
|
// @todo check that we don't get a mixture of implicit and explicit definitions
|
||||||
|
auto crv = map(segment->as<IfcSchema::IfcCurveSegment>());
|
||||||
|
if (crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
|
||||||
|
auto seg = taxonomy::cast<taxonomy::piecewise_function>(crv);
|
||||||
|
cant->spans.insert(cant->spans.end(), seg->spans.begin(), seg->spans.end());
|
||||||
|
} else {
|
||||||
|
Logger::Error("Unsupported");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger::Error("Unsupported");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto composition = [gradient, cant](double u)->Eigen::Matrix4d {
|
||||||
|
auto xyz = gradient->evaluate(u);
|
||||||
|
auto c = cant->evaluate(u);
|
||||||
|
c.col(3)(0) = 0;
|
||||||
|
std::swap(c.col(3)(1),c.col(3)(2));
|
||||||
|
Eigen::Matrix4d m;
|
||||||
|
m = xyz * c;
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<taxonomy::piecewise_function::ptr, 2> both = { gradient , cant };
|
||||||
|
// @todo: rb - this constrains the range of u to the minimum of gradient and cant
|
||||||
|
// we discussed using the maximum for the range of us and then using std::numeric_limits<double>::NAN
|
||||||
|
// for values of u that gradient or cant cannot be computed.
|
||||||
|
// Review and decide what to do.
|
||||||
|
double min_length = std::numeric_limits<double>::infinity();
|
||||||
|
for (auto i = 0; i < 2; ++i) {
|
||||||
|
double l = 0;
|
||||||
|
for (auto& s : both[i]->spans) {
|
||||||
|
l += s.first;
|
||||||
|
}
|
||||||
|
if (l < min_length) {
|
||||||
|
min_length = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pwf = taxonomy::make<taxonomy::piecewise_function>();
|
||||||
|
pwf->spans.emplace_back( min_length, composition );
|
||||||
|
pwf->instance = inst;
|
||||||
|
return pwf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -113,6 +113,9 @@ BIND(IfcEdge);
|
|||||||
BIND(IfcEdgeLoop);
|
BIND(IfcEdgeLoop);
|
||||||
BIND(IfcPolyline);
|
BIND(IfcPolyline);
|
||||||
BIND(IfcPolyLoop);
|
BIND(IfcPolyLoop);
|
||||||
|
#ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve
|
||||||
|
BIND(IfcSegmentedReferenceCurve);
|
||||||
|
#endif
|
||||||
#ifdef SCHEMA_HAS_IfcGradientCurve
|
#ifdef SCHEMA_HAS_IfcGradientCurve
|
||||||
BIND(IfcGradientCurve);
|
BIND(IfcGradientCurve);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user