diff --git a/src/ifcgeom/mapping/IfcCompositeCurve.cpp b/src/ifcgeom/mapping/IfcCompositeCurve.cpp index c70334b347..b425574bd9 100644 --- a/src/ifcgeom/mapping/IfcCompositeCurve.cpp +++ b/src/ifcgeom/mapping/IfcCompositeCurve.cpp @@ -92,7 +92,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) { return loop; } else { - auto pwf = taxonomy::make(pwfs,&settings_,inst); + auto pwf = taxonomy::make(0.0,pwfs,&settings_,inst); return pwf; } } diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 44b3d330b6..4e9cb9e1de 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -890,7 +890,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) { taxonomy::piecewise_function::spans spans; spans.emplace_back(fabs(length), fn); - auto pwf = taxonomy::make(spans,&settings_,inst); + auto pwf = taxonomy::make(0.0, spans,&settings_,inst); return pwf; } diff --git a/src/ifcgeom/mapping/IfcGradientCurve.cpp b/src/ifcgeom/mapping/IfcGradientCurve.cpp index c59286d3af..144f68d877 100644 --- a/src/ifcgeom/mapping/IfcGradientCurve.cpp +++ b/src/ifcgeom/mapping/IfcGradientCurve.cpp @@ -27,11 +27,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { if (!inst->BaseCurve()->as()) Logger::Warning("Expected IfcGradientCurve.BaseCurve to be IfcCompositeCurve", inst); // CT 4.1.7.1.1.2 - auto horizontal = taxonomy::cast(map(inst->BaseCurve())); - auto segments = inst->Segments(); - std::vector pwfs; + std::vector pwfs; for (auto& segment : *segments) { if (segment->as()) { @@ -48,11 +46,33 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { return nullptr; } } - - auto vertical = taxonomy::make(pwfs,&settings_); + // Get starting position of gradient curve, which is relative to the base curve + // The gradient curve can start before or after the start of the base curve + auto first_segment = *(segments->begin()); + auto p = taxonomy::cast(map(first_segment->as()->Placement())); + const Eigen::Matrix4d& m = p->ccomponents(); + double gradient_start = m(0, 3); // start of vertical (row 0, col 3) - "Distance Along" horizontal curve + + // create the vertical pwf + auto vertical = taxonomy::make(gradient_start, pwfs, &settings_); + + // Determine the valid domain of the PWF... the valid domain is where both + // the base curve and gradient curves are defined + auto horizontal = taxonomy::cast(map(inst->BaseCurve())); + double start = std::max(horizontal->start(),vertical->start()); + double end = std::min(horizontal->end(), vertical->end()); + double length = end - start; + + if (!(0 < length)) { + Logger::Error("IfcGradientCurve does not have a common domain with BaseCurve"); + } + + // define the callback function for the gradient curve auto composition = [horizontal, vertical](double u)->Eigen::Matrix4d { - auto xy = horizontal->evaluate(u); + // u is distance from start of gradient curve (vertical) + // add vertical->start() to u to get distance from start of horizontal + auto xy = horizontal->evaluate(u + vertical->start()); auto uz = vertical->evaluate(u); uz.col(3)(0) = 0.0; // x is distance along. zero it out so it doesn't add to the x from horizontal @@ -64,11 +84,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { return m; }; - double min_length = std::min(horizontal->length(), vertical->length()); - taxonomy::piecewise_function::spans spans; - spans.emplace_back(min_length, composition); - auto pwf = taxonomy::make(spans, &settings_, inst); + spans.emplace_back(length, composition); + auto pwf = taxonomy::make(start, spans, &settings_, inst); return pwf; } diff --git a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp index 46ea9eee34..ddc5621baa 100644 --- a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp +++ b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp @@ -38,8 +38,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst auto first_offset_value = *(offset_values->begin()); auto basis_curve = inst->BasisCurve(); - //auto pw_curve = ifcopenshell::geometry::piecewise_from_item(map(basis_curve)); auto pw_curve = taxonomy::dcast(map(basis_curve)); + + double start = pw_curve->start(); double basis_curve_length = pw_curve->length(); taxonomy::piecewise_function::spans offset_spans; @@ -143,7 +144,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst offset_spans.emplace_back(l, fn); } - auto offsets = taxonomy::make(offset_spans,&settings_); + auto offsets = taxonomy::make(start,offset_spans,&settings_); auto composition = [pw_curve, offsets](double u) -> Eigen::Matrix4d { auto p = pw_curve->evaluate(u); @@ -156,7 +157,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst // this may change depending on decisions in the bSI-IF taxonomy::piecewise_function::spans spans; spans.emplace_back(basis_curve_length, composition); - auto pwf = taxonomy::make(spans,&settings_,inst); + auto pwf = taxonomy::make(start,spans,&settings_,inst); return pwf; } diff --git a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp index bab1808601..1ca21e3690 100644 --- a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp +++ b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp @@ -27,8 +27,6 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins if (!inst->BaseCurve()->as()) Logger::Warning("Expected IfcSegmentedReferenceCurve.BaseCurve to be IfcGradient", inst); // CT 4.1.7.1.1.3 - auto gradient = taxonomy::cast(map(inst->BaseCurve())); - auto segments = inst->Segments(); std::vector pwfs; @@ -46,11 +44,34 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins Logger::Error("Unsupported"); return nullptr; } - } - auto cant = taxonomy::make(pwfs,&settings_); + } + // Get starting position of cant curve, relative to the gradient curve. + // The cant curve can start before or after the start of the gradient curve + auto first_segment = *(segments->begin()); + auto p = taxonomy::cast(map(first_segment->as()->Placement())); + const Eigen::Matrix4d& m = p->ccomponents(); + double cant_start = m(0, 3); // start of cant curve + + auto cant = taxonomy::make(cant_start,pwfs,&settings_); + + // Determine the valid domain of the PWF... the valid domain is where + // horizontal, gradient and cant curves are defined + auto gradient = taxonomy::cast(map(inst->BaseCurve())); + auto horizontal = taxonomy::cast(map(inst->BaseCurve()->as()->BaseCurve())); + double start = std::max(std::max(cant->start(), gradient->start()), horizontal->start()); + double end = std::min(std::min(cant->end(), gradient->end()), horizontal->end()); + double length = end - start; + + if (!(0 < length)) { + Logger::Error("IfcSegmentedReferenceCurve does not have a common domain with BaseCurve"); + } + + // define the callback function for the segmented reference curve auto composition = [gradient, cant](double u)->Eigen::Matrix4d { - auto g = gradient->evaluate(u); + // u is distance from start of cant curve + // add cant->start() to u to get the distance from start of gradient curve + auto g = gradient->evaluate(u+cant->start()); auto c = cant->evaluate(u); c.col(3)(0) = 0.0; // x is distance along. zero it out so it doesn't add to the x from gradient curve @@ -62,11 +83,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins return m; }; - double min_length = std::min(gradient->length(), cant->length()); - taxonomy::piecewise_function::spans spans; - spans.emplace_back(min_length, composition); - auto pwf = taxonomy::make(spans, &settings_, inst); + spans.emplace_back(length, composition); + auto pwf = taxonomy::make(start, spans, &settings_, inst); return pwf; } diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 0ed716677d..9a80afe7d1 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -462,59 +462,67 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box( return solid; } -ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate() const { - return evaluate2().first; -} +std::vector ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points() const { + if (!eval_points_.has_value()) { + double curve_length = length(); -std::pair> ifcopenshell::geometry::taxonomy::piecewise_function::evaluate2() const { - double curve_length = length(); + auto param_type = settings_ ? settings_->get().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE; + auto param = settings_ ? settings_->get().get() : 0.5; + unsigned num_steps = 0; + if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { + // parameter is max step size + num_steps = (unsigned)std::ceil(curve_length / param); + } else { + // parameter is minimum number of steps + num_steps = (unsigned)std::ceil(param); + } - std::vector polygon; - - auto param_type = settings_ ? settings_->get().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE; - auto param = settings_ ? settings_->get().get() : 0.5; - unsigned num_steps = 0; - if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { - // parameter is max step size - num_steps = (unsigned)std::ceil(curve_length / param); - } else { - // parameter is minimum number of steps - num_steps = (unsigned)std::ceil(param); + eval_points_ = evaluation_points(start_, start_ + curve_length, num_steps); } - - num_steps = std::max(1u, num_steps); // never have fewer than 1 step - - return evaluate2(0.0, curve_length, num_steps); + return *eval_points_; } -item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double ustart, double uend,unsigned nsteps) const { - return evaluate2(ustart, uend, nsteps).first; -} - -std::pair> ifcopenshell::geometry::taxonomy::piecewise_function::evaluate2(double ustart, double uend, unsigned nsteps) const { +std::vector ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points(double ustart, double uend, unsigned nsteps) const { double curve_length = length(); - ustart = std::max(0.0, ustart); - uend = std::min(uend, curve_length); + ustart = std::max(start_, ustart); + uend = std::min(uend, start_ + curve_length); + + nsteps = std::max(1u, nsteps); // never have fewer than 1 step auto resolution = (uend - ustart) / nsteps; - std::vector polygon; std::vector u_values; - polygon.reserve(nsteps); u_values.reserve(nsteps); for (unsigned i = 0; i <= nsteps; ++i) { auto u = resolution * i + ustart; u_values.push_back(u); + } + + return u_values; +} + +ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate() const { + return evaluate(evaluation_points()); +} + +item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double ustart, double uend,unsigned nsteps) const { + return evaluate(evaluation_points(ustart,uend,nsteps)); +} + +item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(const std::vector& dist) const { + std::vector polygon; + polygon.reserve(dist.size()); + for (auto& u : dist) { Eigen::Matrix4d m = evaluate(u); polygon.push_back(taxonomy::make(m.col(3)(0), m.col(3)(1), m.col(3)(2))); } - return {polygon_from_points(polygon), u_values}; + return polygon_from_points(polygon); } Eigen::Matrix4d ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double u) const { - // assume monotonic evaluation and store last evaluated segment + // assume monotonic evaluation and store last evaluated segment if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) { // there isn't a current span or u is outside the range of the current span // get a new "current span" @@ -527,18 +535,19 @@ Eigen::Matrix4d ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(d std::tuple*> ifcopenshell::geometry::taxonomy::piecewise_function::get_span(double u) const { // force u to be within bounds of the curve - double curve_length = length(); - u = std::max(0.0, u); - u = std::min(u, curve_length); + double s = start(); + double e = end(); + u = std::max(s, u); + u = std::min(u, e); - double start = 0; + double span_start = s; for (auto& [length, fn] : spans_) { + double span_end = span_start + length; auto tolerance = settings_ ? settings_->get().get() : 0.001; - if (u < length + tolerance) { - return {start, start+length, &fn} ; + if (span_start <= u && u < span_end + tolerance) { + return {span_start, span_end, &fn} ; } - start += length; - u -= length; + span_start += length; } Logger::Error("taxonomy::piecewise_function::get_span span not found."); diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index 4b227fd7ef..4fbbf636d2 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -356,10 +356,10 @@ typedef item const* ptr; using spans = std::vector>>; - piecewise_function(const spans& s, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : - implicit_item(instance), settings_(settings), spans_(s){}; - piecewise_function(const std::vector& pwfs, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : - implicit_item(instance), settings_(settings) + piecewise_function(double start,const spans& s, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : + implicit_item(instance), start_(start), settings_(settings), spans_(s){}; + piecewise_function(double start,const std::vector& pwfs, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : + implicit_item(instance), start_(start), settings_(settings) { for (auto& pwf : pwfs) { spans_.insert(spans_.end(), pwf->spans_.begin(), pwf->spans_.end()); @@ -372,6 +372,14 @@ typedef item const* ptr; bool is_empty() const { return spans_.empty(); } + double start() const { + return start_; + } + + double end() const { + return start_ + length(); + } + double length() const { if (!length_.has_value()) { length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; }); @@ -387,35 +395,43 @@ typedef item const* ptr; return boost::hash{}(v); } - item::ptr evaluate() const override; - std::pair> evaluate2() const; + /// @brief returns a vector of "distance along" points where the evaluate function computes loop points + std::vector evaluation_points() const; + + /// @brief returns a vector of "distance along" points between ustart and uend + /// @param ustart starting location + /// @param uend ending location + /// @param nsteps number of steps to evaluate + std::vector evaluation_points(double ustart, double uend, unsigned nsteps) const; + + /// @brief evaluates the piecewise function between start and end + /// evaluation point step size is taken from the settings object + item::ptr evaluate() const override; /// @brief evaluates the piecewise function between ustart and uend - /// @param ustart starting location - taken as 0.0 if before start - /// @param uend ending location - taken as length if beyond end + /// if ustart and uend are out of range, the range of values evaluated + /// are constrained to start_ and start_+length_ + /// @param ustart starting location + /// @param uend ending location /// @param nsteps number of steps to evaluate /// @return taxonomy::loop::ptr item::ptr evaluate(double ustart, double uend, unsigned nsteps) const; - /// @brief evaluates the piecewise function between ustart and uend - /// @param ustart starting location - taken as 0.0 if before start - /// @param uend ending location - taken as length if beyond end - /// @param nsteps number of steps to evaluate - /// @return taxonomy::loop::ptr and vector of u values - std::pair> evaluate2(double ustart, double uend, unsigned nsteps) const; - /// @brief evaluates the piecewise function at u - /// @param u u is constrained to be between 0 and length + /// @param u u is constrained to be between start_ and start_+length /// @return 4x4 placement matrix Eigen::Matrix4d evaluate(double u) const; private: - std::tuple*> get_span(double u) const; + item::ptr evaluate(const std::vector& dist) const; + std::tuple*> get_span(double u) const; + double start_ = 0.0; // starting value of the pwf spans spans_; mutable double current_span_start_ = 0; mutable double current_span_end_ = 0; mutable const std::function* current_span_fn_ = nullptr; mutable boost::optional length_; + mutable boost::optional> eval_points_; }; #ifdef TAXONOMY_USE_SHARED_PTR @@ -1341,7 +1357,7 @@ typedef item const* ptr; }; spans.emplace_back(l, fn); } - pwf_ = taxonomy::make(spans); + pwf_ = taxonomy::make(0.0,spans); loop->pwf = pwf_; } }