Skip to content

Commit cc71710

Browse files
authored
Decouples evaluation of a piecewise_function from the function itself (#5344)
1 parent 67a7e71 commit cc71710

17 files changed

Lines changed: 239 additions & 220 deletions

src/ifcgeom/AbstractKernel.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "../ifcgeom/IfcGeomElement.h"
44
#include "../ifcgeom/ConversionSettings.h"
55
#include "../ifcgeom/abstract_mapping.h"
6+
#include "../ifcgeom/piecewise_function_evaluator.h"
67

78
#ifdef IFOPSH_WITH_OPENCASCADE
89
#include "../ifcgeom/kernels/opencascade/OpenCascadeKernel.h"
@@ -220,7 +221,8 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonom
220221
}
221222

222223
bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs) {
223-
auto expl = item->evaluate();
224+
piecewise_function_evaluator evaluator(item);
225+
auto expl = evaluator.evaluate();
224226
expl->instance = item->instance;
225227
return convert(expl, cs);
226228
}

src/ifcgeom/mapping/IfcCompositeCurve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
9292
return loop;
9393
}
9494
else {
95-
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,pwfs,&settings_,inst);
95+
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0,pwfs,inst);
9696
return pwf;
9797
}
9898
}

src/ifcgeom/mapping/IfcCurveSegment.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
961961

962962
taxonomy::piecewise_function::spans_t spans;
963963
spans.emplace_back(fabs(length), fn);
964-
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,&settings_,inst);
964+
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,inst);
965965
return pwf;
966966
}
967967

src/ifcgeom/mapping/IfcFixedReferenceSweptAreaSolid.cpp

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
********************************************************************************/
1919

2020
#include "mapping.h"
21+
#include "../piecewise_function_evaluator.h"
2122
#define mapping POSTFIX_SCHEMA(mapping)
2223
using namespace ifcopenshell::geometry;
2324

@@ -34,6 +35,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
3435

3536
// @todo currently only the case is handled where directrix returns a piecewise_function
3637
if (auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir)) {
38+
piecewise_function_evaluator evaluator(pwf,&settings_);
3739
double start = 0;
3840
double end = pwf->length();
3941
#ifdef SCHEMA_HAS_IfcDirectrixCurveSweptAreaSolid
@@ -53,20 +55,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
5355
}
5456
}
5557
#endif
56-
auto curve_length = end - start;
57-
auto param_type = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepType>().get();
58-
auto param = settings_.get<ifcopenshell::geometry::settings::PiecewiseStepParam>().get();
59-
size_t num_steps = 0;
60-
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
61-
// parameter is max step size
62-
num_steps = (size_t) std::ceil(curve_length / param);
63-
} else {
64-
// parameter is minimum number of steps
65-
num_steps = (size_t) std::ceil(param);
66-
}
67-
for (size_t i = 0; i <= num_steps; ++i) {
68-
auto distalong = start + curve_length / num_steps * i;
69-
auto m4 = pwf->evaluate(distalong);
58+
auto evaluation_points = evaluator.evaluation_points();
59+
for (const auto& dist_along : evaluation_points) {
60+
auto m4 = evaluator.evaluate(dist_along);
7061

7162
/*
7263
std::stringstream ss;

src/ifcgeom/mapping/IfcGradientCurve.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
********************************************************************************/
1919

2020
#include "mapping.h"
21+
#include "../piecewise_function_evaluator.h"
2122
#define mapping POSTFIX_SCHEMA(mapping)
2223
using namespace ifcopenshell::geometry;
2324

@@ -55,7 +56,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
5556
double gradient_start = m(0, 3); // start of vertical (row 0, col 3) - "Distance Along" horizontal curve
5657

5758
// create the vertical pwf
58-
auto vertical = taxonomy::make<taxonomy::piecewise_function>(gradient_start, pwfs, &settings_);
59+
auto vertical = taxonomy::make<taxonomy::piecewise_function>(gradient_start, pwfs);
5960

6061
// Determine the valid domain of the PWF... the valid domain is where both
6162
// the base curve and gradient curves are defined
@@ -69,11 +70,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
6970
}
7071

7172
// define the callback function for the gradient curve
72-
auto composition = [horizontal, vertical](double u)->Eigen::Matrix4d {
73+
piecewise_function_evaluator horizontal_evaluator(horizontal, &settings_), vertical_evaluator(vertical, &settings_);
74+
auto composition = [horizontal_evaluator, vertical_evaluator,start=vertical->start()](double u) -> Eigen::Matrix4d {
7375
// u is distance from start of gradient curve (vertical)
7476
// add vertical->start() to u to get distance from start of horizontal
75-
auto xy = horizontal->evaluate(u + vertical->start());
76-
auto uz = vertical->evaluate(u);
77+
auto xy = horizontal_evaluator.evaluate(u + start);
78+
auto uz = vertical_evaluator.evaluate(u);
7779

7880
uz.col(3)(0) = 0.0; // x is distance along. zero it out so it doesn't add to the x from horizontal
7981
uz.col(1).swap(uz.col(2)); // uz is 2D in distance along - y plane, swap y and z so elevations become z
@@ -86,7 +88,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
8688

8789
taxonomy::piecewise_function::spans_t spans;
8890
spans.emplace_back(length, composition);
89-
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
91+
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
9092
return pwf;
9193
}
9294

src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "mapping.h"
2121
#include "../profile_helper.h"
22+
#include "../piecewise_function_evaluator.h"
2223
#define mapping POSTFIX_SCHEMA(mapping)
2324
using namespace ifcopenshell::geometry;
2425

@@ -144,11 +145,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
144145
offset_spans.emplace_back(l, fn);
145146
}
146147

147-
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans,&settings_);
148+
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans);
148149

149-
auto composition = [pw_curve, offsets](double u) -> Eigen::Matrix4d {
150-
auto p = pw_curve->evaluate(u);
151-
auto offset = offsets->evaluate(u);
150+
piecewise_function_evaluator pw_evaluator(pw_curve, &settings_), offsets_evaluator(offsets, &settings_);
151+
auto composition = [pw_evaluator, offsets_evaluator](double u) -> Eigen::Matrix4d {
152+
auto p = pw_evaluator.evaluate(u);
153+
auto offset = offsets_evaluator.evaluate(u);
152154
Eigen::Matrix4d m = p * offset;
153155
return m;
154156
};
@@ -157,7 +159,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
157159
// this may change depending on decisions in the bSI-IF
158160
taxonomy::piecewise_function::spans_t spans;
159161
spans.emplace_back(basis_curve_length, composition);
160-
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,&settings_,inst);
162+
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,inst);
161163
return pwf;
162164
}
163165

src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "mapping.h"
2121
#include "../profile_helper.h"
22+
#include "../piecewise_function_evaluator.h"
2223

2324
#define mapping POSTFIX_SCHEMA(mapping)
2425
using namespace ifcopenshell::geometry;
@@ -31,7 +32,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* i
3132
//auto item = map(basis_curve);
3233
//auto pw_curve = ifcopenshell::geometry::piecewise_from_item(item);
3334
auto pw_curve = taxonomy::dcast<taxonomy::piecewise_function>(map(inst->BasisCurve()));
34-
auto m = pw_curve->evaluate(u);
35+
piecewise_function_evaluator evaluator(pw_curve,&settings_);
36+
auto m = evaluator.evaluate(u);
3537

3638
auto o = m.col(3).head<3>();
3739
auto z = m.col(2).head<3>();

src/ifcgeom/mapping/IfcSectionedSolidHorizontal.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using namespace ifcopenshell::geometry;
2323

2424
#include "../../ifcgeom/profile_helper.h"
25+
#include "../piecewise_function_evaluator.h"
2526

2627
#include <boost/range/combine.hpp>
2728

@@ -114,7 +115,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
114115
// @todo currently only the case is handled where directrix returns a piecewise_function
115116
// @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a piecewise function
116117
if (pwf) {
117-
double start = std::max(0., cross_sections.front().dist_along);
118+
piecewise_function_evaluator evaluator(pwf, &settings_);
119+
double start = std::max(0., cross_sections.front().dist_along);
118120
double end = std::min(pwf->length(), cross_sections.back().dist_along);
119121

120122
if (end - start < 1.e-9) {
@@ -232,7 +234,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
232234
}
233235
}
234236

235-
auto m4 = pwf->evaluate(dist_along);
237+
auto m4 = evaluator.evaluate(dist_along);
236238
/* {
237239
std::wcout << "#" << pwf->instance->data().id() << " " << dist_along << ": " << m4.col(3).row(2).value() << std::endl;
238240
}*/

src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#define mapping POSTFIX_SCHEMA(mapping)
2222
using namespace ifcopenshell::geometry;
2323

24+
#include "../piecewise_function_evaluator.h"
25+
2426
#ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve
2527

2628
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* inst) {
@@ -53,7 +55,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
5355
const Eigen::Matrix4d& m = p->ccomponents();
5456
double cant_start = m(0, 3); // start of cant curve
5557

56-
auto cant = taxonomy::make<taxonomy::piecewise_function>(cant_start,pwfs,&settings_);
58+
auto cant = taxonomy::make<taxonomy::piecewise_function>(cant_start,pwfs);
5759

5860
// Determine the valid domain of the PWF... the valid domain is where
5961
// horizontal, gradient and cant curves are defined
@@ -68,11 +70,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
6870
}
6971

7072
// define the callback function for the segmented reference curve
71-
auto composition = [gradient, cant](double u)->Eigen::Matrix4d {
73+
piecewise_function_evaluator gradient_evaluator(gradient, &settings_), cant_evaluator(cant, &settings_);
74+
auto composition = [gradient_evaluator, cant_evaluator, start = cant->start()](double u) -> Eigen::Matrix4d {
7275
// u is distance from start of cant curve
7376
// add cant->start() to u to get the distance from start of gradient curve
74-
auto g = gradient->evaluate(u+cant->start());
75-
auto c = cant->evaluate(u);
77+
auto g = gradient_evaluator.evaluate(u + start);
78+
auto c = cant_evaluator.evaluate(u);
7679

7780
// Need to multiply g and c so the axis vectors
7881
// from cant have the correct rotation applied so
@@ -105,7 +108,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
105108

106109
taxonomy::piecewise_function::spans_t spans;
107110
spans.emplace_back(length, composition);
108-
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
111+
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, inst);
109112
return pwf;
110113
}
111114

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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

Comments
 (0)