|
| 1 | +#include "piecewise_function_evaluator.h" |
| 2 | +#include "profile_helper.h" |
| 3 | + |
| 4 | +using namespace ifcopenshell::geometry; |
| 5 | + |
| 6 | + |
| 7 | +piecewise_function_evaluator::piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, ifcopenshell::geometry::Settings* settings) : pwf_(pwf) { |
| 8 | + if (settings) { |
| 9 | + settings_ = *settings; |
| 10 | + } |
| 11 | +} |
| 12 | + |
| 13 | +std::vector<double> piecewise_function_evaluator::evaluation_points() const { |
| 14 | + if (!eval_points_.has_value()) { |
| 15 | + double curve_length = pwf_->length(); |
| 16 | + |
| 17 | + auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get(); |
| 18 | + auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get(); |
| 19 | + unsigned num_steps = 0; |
| 20 | + if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { |
| 21 | + // parameter is max step size |
| 22 | + num_steps = (unsigned)std::ceil(curve_length / param); |
| 23 | + } else { |
| 24 | + // parameter is minimum number of steps |
| 25 | + num_steps = (unsigned)std::ceil(param); |
| 26 | + } |
| 27 | + |
| 28 | + eval_points_ = evaluation_points(pwf_->start(), pwf_->start() + curve_length, num_steps); |
| 29 | + } |
| 30 | + return *eval_points_; |
| 31 | +} |
| 32 | + |
| 33 | +std::vector<double> piecewise_function_evaluator::evaluation_points(double ustart, double uend, unsigned nsteps) const { |
| 34 | + double curve_length = pwf_->length(); |
| 35 | + ustart = std::max(pwf_->start(), ustart); |
| 36 | + uend = std::min(uend, pwf_->start() + curve_length); |
| 37 | + |
| 38 | + nsteps = std::max(1u, nsteps); // never have fewer than 1 step |
| 39 | + |
| 40 | + auto resolution = (uend - ustart) / nsteps; |
| 41 | + |
| 42 | + std::vector<double> u_values; |
| 43 | + u_values.reserve(nsteps); |
| 44 | + |
| 45 | + for (unsigned i = 0; i <= nsteps; ++i) { |
| 46 | + auto u = resolution * i + ustart; |
| 47 | + u_values.push_back(u); |
| 48 | + } |
| 49 | + |
| 50 | + return u_values; |
| 51 | +} |
| 52 | + |
| 53 | +taxonomy::item::ptr piecewise_function_evaluator::evaluate() const { |
| 54 | + return evaluate(evaluation_points()); |
| 55 | +} |
| 56 | + |
| 57 | +taxonomy::item::ptr piecewise_function_evaluator::evaluate(double ustart, double uend, unsigned nsteps) const { |
| 58 | + return evaluate(evaluation_points(ustart, uend, nsteps)); |
| 59 | +} |
| 60 | + |
| 61 | +Eigen::Matrix4d piecewise_function_evaluator::evaluate(double u) const { |
| 62 | + // assume monotonic evaluation and store last evaluated segment |
| 63 | + if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) { |
| 64 | + // there isn't a current span or u is outside the range of the current span |
| 65 | + // get a new "current span" |
| 66 | + std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u); |
| 67 | + } |
| 68 | + |
| 69 | + u -= current_span_start_; // make u relative to start of span |
| 70 | + return (*current_span_fn_)(u); |
| 71 | +} |
| 72 | + |
| 73 | +taxonomy::item::ptr piecewise_function_evaluator::evaluate(const std::vector<double>& dist) const { |
| 74 | + std::vector<taxonomy::point3::ptr> polygon; |
| 75 | + polygon.reserve(dist.size()); |
| 76 | + for (auto& u : dist) { |
| 77 | + Eigen::Matrix4d m = evaluate(u); |
| 78 | + polygon.push_back(taxonomy::make<taxonomy::point3>(m(0, 3), m(1, 3), m(2, 3))); |
| 79 | + } |
| 80 | + |
| 81 | + return polygon_from_points(polygon); |
| 82 | +} |
| 83 | + |
| 84 | +std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> piecewise_function_evaluator::get_span(double u) const { |
| 85 | + // force u to be within bounds of the curve |
| 86 | + double s = pwf_->start(); |
| 87 | + double e = pwf_->end(); |
| 88 | + u = std::max(s, u); |
| 89 | + u = std::min(u, e); |
| 90 | + |
| 91 | + double span_start = s; |
| 92 | + for (auto& [length, fn] : pwf_->spans()) { |
| 93 | + double span_end = span_start + length; |
| 94 | + auto tolerance = settings_.get<ifcopenshell::geometry::settings::Precision>().get(); |
| 95 | + if (span_start <= u && u < span_end + tolerance) { |
| 96 | + return {span_start, span_end, &fn}; |
| 97 | + } |
| 98 | + span_start += length; |
| 99 | + } |
| 100 | + |
| 101 | + Logger::Error("piecewise_function_impl::get_span span not found."); |
| 102 | + return {0, 0, nullptr}; |
| 103 | +} |
0 commit comments