Prevent stackoverflow on long alignments in adjustments

This commit is contained in:
Thomas Krijnen
2024-04-29 21:02:31 +02:00
parent e6cc500227
commit 710f19af08
5 changed files with 75 additions and 30 deletions
+5 -2
View File
@@ -31,6 +31,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
#else
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = inst->Segments();
#endif
current_segment_count_ = segments->size();
for (auto& segment : *segments) {
if (segment->as<IfcSchema::IfcCompositeCurveSegment>() && segment->as<IfcSchema::IfcCompositeCurveSegment>()->ParentCurve()->as<IfcSchema::IfcLine>()) {
@@ -71,14 +72,16 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
else 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::LOOP) {
if (crv && crv->kind() == taxonomy::LOOP) {
for (auto& s : taxonomy::cast<taxonomy::loop>(crv)->children) {
loop->children.push_back(s);
}
}
else if (crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
else if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
auto seg = taxonomy::cast<taxonomy::piecewise_function>(crv);
pwf->spans.insert(pwf->spans.end(), seg->spans.begin(), seg->spans.end());
} else if (!crv) {
return nullptr;
}
}
#endif
+65 -26
View File
@@ -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();
+2 -1
View File
@@ -31,12 +31,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
auto vertical = taxonomy::make<taxonomy::piecewise_function>(&settings_);
auto segments = inst->Segments();
current_segment_count_ = segments->size();
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) {
if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) {
auto seg = taxonomy::cast<taxonomy::piecewise_function>(crv);
vertical->spans.insert(vertical->spans.end(), seg->spans.begin(), seg->spans.end());
} else {
@@ -31,12 +31,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
auto cant = taxonomy::make<taxonomy::piecewise_function>(&settings_);
auto segments = inst->Segments();
current_segment_count_ = segments->size();
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) {
if (crv && 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 {
+1
View File
@@ -22,6 +22,7 @@ namespace geometry {
IfcParse::IfcFile* file_;
double length_unit_, angle_unit_;
std::string length_unit_name_;
size_t current_segment_count_;
std::map<uint32_t, ifcopenshell::geometry::taxonomy::ptr> cache_;