From 710f19af08ed10ed0a62ff5a8ae80ee14395bf96 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 29 Apr 2024 21:02:31 +0200 Subject: [PATCH] Prevent stackoverflow on long alignments in adjustments --- src/ifcgeom/mapping/IfcCompositeCurve.cpp | 7 +- src/ifcgeom/mapping/IfcCurveSegment.cpp | 91 +++++++++++++------ src/ifcgeom/mapping/IfcGradientCurve.cpp | 3 +- .../mapping/IfcSegmentedReferenceCurve.cpp | 3 +- src/ifcgeom/mapping/mapping.h | 1 + 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/src/ifcgeom/mapping/IfcCompositeCurve.cpp b/src/ifcgeom/mapping/IfcCompositeCurve.cpp index a94b2803af..bf26abc314 100644 --- a/src/ifcgeom/mapping/IfcCompositeCurve.cpp +++ b/src/ifcgeom/mapping/IfcCompositeCurve.cpp @@ -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() && segment->as()->ParentCurve()->as()) { @@ -71,14 +72,16 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) { else if (segment->as()) { // @todo check that we don't get a mixture of implicit and explicit definitions auto crv = map(segment->as()); - if (crv->kind() == taxonomy::LOOP) { + if (crv && crv->kind() == taxonomy::LOOP) { for (auto& s : taxonomy::cast(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(crv); pwf->spans.insert(pwf->spans.end(), seg->spans.begin(), seg->spans.end()); + } else if (!crv) { + return nullptr; } } #endif diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 93692dd59e..a469b60a4a 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -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(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(mapping_->map(inst_->Placement()))->ccomponents(); - length_ = fabs(*inst->SegmentLength()->as() * 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(mapping->map(next_inst)); - start_of_next_inst_ = next->evaluate(0.0); + auto next = taxonomy::cast(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(mapping->map(placement))->ccomponents(); + start_of_next_inst_ = taxonomy::cast(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> 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() || !inst->SegmentLength()->as()) { // @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() * length_unit; - length_ = *inst->SegmentLength()->as() * 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(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 Cant, std::function CantSlope) { geometry_adjuster_ = std::make_shared(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 Cant, std::function CantSlope) { geometry_adjuster_ = std::make_shared(mapping_, inst_, next_inst_); + geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64); auto cant_adjuster_ = std::make_shared(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(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(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(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(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(mapping_, inst_, next_inst_); + geometry_adjuster_->enable_adjustments(current_segment_count_ <= 64); auto cant_adjuster_ = std::make_shared(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(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>(std::ref(cse)); cse.compute_segment_end_point(); diff --git a/src/ifcgeom/mapping/IfcGradientCurve.cpp b/src/ifcgeom/mapping/IfcGradientCurve.cpp index c5ef0bd62e..e570ff6c6f 100644 --- a/src/ifcgeom/mapping/IfcGradientCurve.cpp +++ b/src/ifcgeom/mapping/IfcGradientCurve.cpp @@ -31,12 +31,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { auto vertical = taxonomy::make(&settings_); auto segments = inst->Segments(); + current_segment_count_ = segments->size(); for (auto& segment : *segments) { if (segment->as()) { // @todo check that we don't get a mixture of implicit and explicit definitions auto crv = map(segment->as()); - if (crv->kind() == taxonomy::PIECEWISE_FUNCTION) { + if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) { auto seg = taxonomy::cast(crv); vertical->spans.insert(vertical->spans.end(), seg->spans.begin(), seg->spans.end()); } else { diff --git a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp index 4f9fc76549..9df95a4d7a 100644 --- a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp +++ b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp @@ -31,12 +31,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins auto cant = taxonomy::make(&settings_); auto segments = inst->Segments(); + current_segment_count_ = segments->size(); for (auto& segment : *segments) { if (segment->as()) { // @todo check that we don't get a mixture of implicit and explicit definitions auto crv = map(segment->as()); - if (crv->kind() == taxonomy::PIECEWISE_FUNCTION) { + if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) { auto seg = taxonomy::cast(crv); cant->spans.insert(cant->spans.end(), seg->spans.begin(), seg->spans.end()); } else { diff --git a/src/ifcgeom/mapping/mapping.h b/src/ifcgeom/mapping/mapping.h index f030731748..fcf0bc5140 100644 --- a/src/ifcgeom/mapping/mapping.h +++ b/src/ifcgeom/mapping/mapping.h @@ -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 cache_;