mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 00:23:14 +00:00
Prevent stackoverflow on long alignments in adjustments
This commit is contained in:
@@ -109,23 +109,36 @@ namespace {
|
||||
// function to specialize the refinement of the placement at u.
|
||||
class segment_geometry_adjuster {
|
||||
public:
|
||||
segment_geometry_adjuster(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst) :
|
||||
end_of_inst_(Eigen::Matrix4d::Identity()),
|
||||
start_of_next_inst_(Eigen::Matrix4d::Identity()),
|
||||
transition_code_(inst->Transition())
|
||||
{
|
||||
transformation_matrix_ = taxonomy::cast<taxonomy::matrix4>(mapping->map(inst->Placement()))->ccomponents();
|
||||
segment_geometry_adjuster(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst) :
|
||||
mapping_(mapping),
|
||||
inst_(inst),
|
||||
next_inst_(next_inst),
|
||||
end_of_inst_(Eigen::Matrix4d::Identity()),
|
||||
start_of_next_inst_(Eigen::Matrix4d::Identity()),
|
||||
transition_code_(inst->Transition())
|
||||
{
|
||||
transformation_matrix_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(inst_->Placement()))->ccomponents();
|
||||
|
||||
length_ = fabs(*inst->SegmentLength()->as<IfcSchema::IfcLengthMeasure>() * mapping->get_length_unit());
|
||||
length_ = fabs(translate_if_param_value(inst_->ParentCurve(), inst_->SegmentLength()) * mapping_->get_length_unit());
|
||||
}
|
||||
|
||||
if (next_inst) {
|
||||
void init() {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_inst_) {
|
||||
// if there is a next segment, get the coordinates at the start.
|
||||
// Note that mapping->map(next_inst) causes mapping to occur recursively
|
||||
// through all of the curve segments until the end of curve is reached.
|
||||
// Mapping of IfcCompositeCurve, IfcGradientCurve, and IfcSegmentedReferenceCurve may
|
||||
// need to traverse the IfcCurveSegment objects in reverse order to avoid recursion.
|
||||
auto next = taxonomy::cast<taxonomy::piecewise_function>(mapping->map(next_inst));
|
||||
start_of_next_inst_ = next->evaluate(0.0);
|
||||
auto next = taxonomy::cast<taxonomy::piecewise_function>(mapping_->map(next_inst_));
|
||||
if (next == nullptr) {
|
||||
valid_ = false;
|
||||
} else {
|
||||
start_of_next_inst_ = next->evaluate(0.0);
|
||||
}
|
||||
} else {
|
||||
// there is not a next segment, however IfcGradientCurve and IfcSegmentedReferenceCurve
|
||||
// have an optional EndPoint attribute that serves the same purpose as the zero-length
|
||||
@@ -135,7 +148,7 @@ class segment_geometry_adjuster {
|
||||
//
|
||||
// Get the parent of this segment. If it is a IfcGradientCurve or IfcSegmentedReferenceCurve
|
||||
// look for the optional EndPoint attribute
|
||||
auto curves = inst->UsingCurves();
|
||||
auto curves = inst_->UsingCurves();
|
||||
if (curves && curves->size()) {
|
||||
auto curve = *curves->begin();
|
||||
const IfcSchema::IfcPlacement* placement = nullptr;
|
||||
@@ -147,7 +160,7 @@ class segment_geometry_adjuster {
|
||||
placement = s->EndPoint();
|
||||
}
|
||||
if (placement) {
|
||||
start_of_next_inst_ = taxonomy::cast<taxonomy::matrix4>(mapping->map(placement))->ccomponents();
|
||||
start_of_next_inst_ = taxonomy::cast<taxonomy::matrix4>(mapping_->map(placement))->ccomponents();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,15 +176,20 @@ class segment_geometry_adjuster {
|
||||
// and provided to the curve_segment_adjustor through this method
|
||||
void set_segment_end_point(const Eigen::Matrix4d& end_of_inst) {
|
||||
end_of_inst_ = end_of_inst;
|
||||
init_adjustments();
|
||||
// @todo figure out when to call this now that we defer
|
||||
// calling init() to circumvent stackoverflow on high
|
||||
// recursion amount on long alignments
|
||||
// Currently empty function.
|
||||
// init_adjustments();
|
||||
}
|
||||
|
||||
// Transforms the ParentCurve geometry with the IfcCurveSegment.Placement and
|
||||
// applies geometric adjustments to the geometry, if enabled
|
||||
virtual Eigen::Matrix4d transform_and_adjust(double u, const Eigen::Matrix4d& parent_curve_point) const {
|
||||
virtual Eigen::Matrix4d transform_and_adjust(double u, const Eigen::Matrix4d& parent_curve_point) {
|
||||
// transform the parent curve's value into the segment curve's coordinate system
|
||||
Eigen::Matrix4d segment_curve_point = transformation_matrix_ * parent_curve_point;
|
||||
if (adjustments_) {
|
||||
if (adjustments_ && valid_) {
|
||||
init();
|
||||
apply_adjustments(u, segment_curve_point);
|
||||
}
|
||||
return segment_curve_point;
|
||||
@@ -193,12 +211,18 @@ class segment_geometry_adjuster {
|
||||
IfcSchema::IfcTransitionCode::Value get_transition_code() const { return transition_code_; }
|
||||
double get_length() const { return length_; }
|
||||
|
||||
mapping* mapping_;
|
||||
const IfcSchema::IfcCurveSegment* inst_;
|
||||
const IfcSchema::IfcCurveSegment* next_inst_;
|
||||
|
||||
bool adjustments_ = true;
|
||||
Eigen::Matrix4d transformation_matrix_;
|
||||
Eigen::Matrix4d end_of_inst_;
|
||||
Eigen::Matrix4d start_of_next_inst_;
|
||||
double length_;
|
||||
IfcSchema::IfcTransitionCode::Value transition_code_;
|
||||
bool valid_ = true;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// This class refines the geometric adjustment along the segment by dividing the
|
||||
@@ -258,7 +282,7 @@ class cant_adjuster : public GEOMETRY_ADJUSTER {
|
||||
public:
|
||||
using GEOMETRY_ADJUSTER::GEOMETRY_ADJUSTER;
|
||||
|
||||
Eigen::Matrix4d transform_and_adjust(double u, const Eigen::Matrix4d& parent_curve_point) const override {
|
||||
Eigen::Matrix4d transform_and_adjust(double u, const Eigen::Matrix4d& parent_curve_point) override {
|
||||
// Consider a line connection two rails. The upwards vector normal to that line is used to define
|
||||
// the cant tilt. For no tilt, the vector is upwards so the tilt angle is PI/2.
|
||||
// If the left rail is higher than the right angle, the tilt is clockwise and the tilt angle is less than PI/2
|
||||
@@ -359,6 +383,7 @@ class curve_segment_evaluator {
|
||||
double length_; // length along the curve, as provided from the IfcCurveSegment
|
||||
segment_type_t segment_type_;
|
||||
const IfcSchema::IfcCurve* parent_curve_ = nullptr;
|
||||
size_t current_segment_count_;
|
||||
|
||||
double projected_length_; // for vertical segments, this is the length of curve projected onto the "Distance Along" axis
|
||||
|
||||
@@ -367,21 +392,24 @@ class curve_segment_evaluator {
|
||||
std::optional<std::function<Eigen::Matrix4d(double)>> eval_; // function for the curve. Function takes distances along, u, and returns the 4x4 position matrix
|
||||
|
||||
public:
|
||||
curve_segment_evaluator(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst, double length_unit, segment_type_t segment_type)
|
||||
curve_segment_evaluator(mapping* mapping, const IfcSchema::IfcCurveSegment* inst, const IfcSchema::IfcCurveSegment* next_inst, double length_unit, segment_type_t segment_type, size_t current_segment_count)
|
||||
: mapping_(mapping),
|
||||
inst_(inst),
|
||||
next_inst_(next_inst),
|
||||
length_unit_(length_unit),
|
||||
segment_type_(segment_type),
|
||||
parent_curve_(inst->ParentCurve()) {
|
||||
|
||||
inst_(inst),
|
||||
next_inst_(next_inst),
|
||||
length_unit_(length_unit),
|
||||
segment_type_(segment_type),
|
||||
parent_curve_(inst->ParentCurve()),
|
||||
current_segment_count_(current_segment_count)
|
||||
{
|
||||
/*
|
||||
if (!inst->SegmentStart()->as<IfcSchema::IfcLengthMeasure>() || !inst->SegmentLength()->as<IfcSchema::IfcLengthMeasure>()) {
|
||||
// @nb Parameter values are forbidden in the specification until parametrization is provided for all spirals
|
||||
throw std::runtime_error("Unsupported curve measure type");
|
||||
}
|
||||
*/
|
||||
|
||||
start_ = *inst->SegmentStart()->as<IfcSchema::IfcLengthMeasure>() * length_unit;
|
||||
length_ = *inst->SegmentLength()->as<IfcSchema::IfcLengthMeasure>() * length_unit;
|
||||
start_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentStart()) * length_unit;
|
||||
length_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentLength()) * length_unit;
|
||||
}
|
||||
|
||||
void compute_segment_end_point()
|
||||
@@ -457,6 +485,7 @@ class curve_segment_evaluator {
|
||||
}
|
||||
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
eval_ = [start, s, pcX, pcY, pcDx, pcDy, pcStartX, pcStartY, pcStartDx, pcStartDy, fnX, fnY, geometry_adjuster = geometry_adjuster_](double u) {
|
||||
|
||||
u += start;
|
||||
@@ -569,6 +598,7 @@ class curve_segment_evaluator {
|
||||
// CantSlope returns the slope of the Cant function at u. CantSlope(u) is the derivative of Cant(u)
|
||||
void set_cant_spiral_function(mapping* mapping_, std::function<double(double)> Cant, std::function<double(double)> CantSlope) {
|
||||
geometry_adjuster_ = std::make_shared<cant_adjuster>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
eval_ = [geometry_adjuster = geometry_adjuster_, Cant, CantSlope](double u) -> Eigen::Matrix4d {
|
||||
auto cant = Cant(u);
|
||||
auto slope = CantSlope(u);
|
||||
@@ -595,7 +625,9 @@ class curve_segment_evaluator {
|
||||
// match those from the bSI Railway Room unit tests
|
||||
void set_clothoid_cant_spiral_function(mapping* mapping_, std::function<double(double)> Cant, std::function<double(double)> CantSlope) {
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
auto cant_adjuster_ = std::make_shared<cant_adjuster>(mapping_, inst_, next_inst_);
|
||||
cant_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
eval_ = [geometry_adjuster = geometry_adjuster_,cant_adjuster=cant_adjuster_, Cant, CantSlope](double u) -> Eigen::Matrix4d {
|
||||
auto cant = Cant(u);
|
||||
auto slope = CantSlope(u);
|
||||
@@ -856,6 +888,7 @@ class curve_segment_evaluator {
|
||||
auto sign_l = sign(length_);
|
||||
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
|
||||
projected_length_ = length_;
|
||||
eval_ = [R, pcCenterX, pcCenterY, pcStartX, pcStartY, pc_axis_angle, start_angle, sign_l, segment_type=segment_type_, geometry_adjuster = geometry_adjuster_](double u)
|
||||
@@ -1013,6 +1046,7 @@ class curve_segment_evaluator {
|
||||
}
|
||||
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
|
||||
projected_length_ = length_;
|
||||
|
||||
@@ -1045,6 +1079,7 @@ class curve_segment_evaluator {
|
||||
|
||||
if (segment_type_ == ST_HORIZONTAL) {
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
|
||||
auto s = l->Pnt();
|
||||
auto c = s->Coordinates();
|
||||
@@ -1089,6 +1124,7 @@ class curve_segment_evaluator {
|
||||
}
|
||||
else if (segment_type_ == ST_VERTICAL) {
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
|
||||
auto s = l->Pnt();
|
||||
auto c = s->Coordinates();
|
||||
@@ -1123,7 +1159,9 @@ class curve_segment_evaluator {
|
||||
}
|
||||
else if (segment_type_ == ST_CANT) {
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
auto cant_adjuster_ = std::make_shared<cant_adjuster>(mapping_, inst_, next_inst_);
|
||||
cant_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
eval_ = [geometry_adjuster = geometry_adjuster_,cant_adjuster=cant_adjuster_](double u) {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
return geometry_adjuster->transform_and_adjust(u, cant_adjuster->transform_and_adjust(u,m));
|
||||
@@ -1146,6 +1184,7 @@ class curve_segment_evaluator {
|
||||
auto length_unit = length_unit_;
|
||||
|
||||
geometry_adjuster_ = std::make_shared<GEOMETRY_ADJUSTER>(mapping_, inst_, next_inst_);
|
||||
geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64);
|
||||
|
||||
if (segment_type_ == ST_HORIZONTAL) {
|
||||
// @rb need to work on this - u is distance along curve, this differs from vertical where u = x
|
||||
@@ -1343,7 +1382,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
||||
|
||||
auto segment_type = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT;
|
||||
|
||||
curve_segment_evaluator cse(this, inst, next_inst, length_unit_, segment_type);
|
||||
curve_segment_evaluator cse(this, inst, next_inst, length_unit_, segment_type, current_segment_count_);
|
||||
boost::mpl::for_each<curve_seg_types, boost::type<boost::mpl::_>>(std::ref(cse));
|
||||
cse.compute_segment_end_point();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user