diff --git a/src/ifcgeom/AbstractKernel.cpp b/src/ifcgeom/AbstractKernel.cpp index 94fa531221..c5ecbc0684 100644 --- a/src/ifcgeom/AbstractKernel.cpp +++ b/src/ifcgeom/AbstractKernel.cpp @@ -3,7 +3,7 @@ #include "../ifcgeom/IfcGeomElement.h" #include "../ifcgeom/ConversionSettings.h" #include "../ifcgeom/abstract_mapping.h" -#include "../ifcgeom/piecewise_function_evaluator.h" +#include "../ifcgeom/function_item_evaluator.h" #ifdef IFOPSH_WITH_OPENCASCADE #include "../ifcgeom/kernels/opencascade/OpenCascadeKernel.h" @@ -71,6 +71,7 @@ bool is_valid_for_kernel(const ifcopenshell::geometry::kernels::AbstractKernel* return dynamic_cast(shp.Shape().get()) != nullptr; } #endif + return false; } class HybridKernel : public ifcopenshell::geometry::kernels::AbstractKernel { @@ -237,9 +238,44 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonom return r.size() > s; } -bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs) { - piecewise_function_evaluator evaluator(item); +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::function_item::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(),item); auto expl = evaluator.evaluate(); expl->instance = item->instance; return convert(expl, cs); } + +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::functor_item::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(), item); + auto expl = evaluator.evaluate(); + expl->instance = item->instance; + return convert(expl, cs); +} + +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(), item); + auto expl = evaluator.evaluate(); + expl->instance = item->instance; + return convert(expl, cs); +} + +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::gradient_function::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(), item); + auto expl = evaluator.evaluate(); + expl->instance = item->instance; + return convert(expl, cs); +} + +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::cant_function::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(), item); + auto expl = evaluator.evaluate(); + expl->instance = item->instance; + return convert(expl, cs); +} + +bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::offset_function::ptr item, IfcGeom::ConversionResults& cs) { + function_item_evaluator evaluator(settings(), item); + auto expl = evaluator.evaluate(); + expl->instance = item->instance; + return convert(expl, cs); +} diff --git a/src/ifcgeom/AbstractKernel.h b/src/ifcgeom/AbstractKernel.h index 460a2f0c53..e721a4e12f 100644 --- a/src/ifcgeom/AbstractKernel.h +++ b/src/ifcgeom/AbstractKernel.h @@ -76,7 +76,12 @@ namespace ifcopenshell { virtual bool convert_impl(const taxonomy::sweep_along_curve::ptr, IfcGeom::ConversionResults&) { throw not_implemented_error(); } virtual bool convert_impl(const taxonomy::loft::ptr, IfcGeom::ConversionResults&) { throw not_implemented_error(); } virtual bool convert_impl(const taxonomy::collection::ptr, IfcGeom::ConversionResults&); - virtual bool convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::function_item::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::functor_item::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::gradient_function::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::cant_function::ptr item, IfcGeom::ConversionResults& cs); + virtual bool convert_impl(const taxonomy::offset_function::ptr item, IfcGeom::ConversionResults& cs); /* virtual void set_offset(const std::array &p_offset); diff --git a/src/ifcgeom/ConversionSettings.cpp b/src/ifcgeom/ConversionSettings.cpp index 48bdff2643..44fdd50f63 100644 --- a/src/ifcgeom/ConversionSettings.cpp +++ b/src/ifcgeom/ConversionSettings.cpp @@ -49,7 +49,7 @@ std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, Ite return in; } -std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, PiecewiseStepMethod& ioo) { +std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, FunctionStepMethod& ioo) { std::string token; in >> token; boost::to_upper(token); diff --git a/src/ifcgeom/ConversionSettings.h b/src/ifcgeom/ConversionSettings.h index 33b75ab192..65b11ba394 100644 --- a/src/ifcgeom/ConversionSettings.h +++ b/src/ifcgeom/ConversionSettings.h @@ -365,22 +365,22 @@ namespace ifcopenshell { static constexpr bool defaultvalue = false; }; - enum PiecewiseStepMethod { + enum FunctionStepMethod { MAXSTEPSIZE, MINSTEPS }; - std::istream& operator>>(std::istream& in, PiecewiseStepMethod& ioo); + std::istream& operator>>(std::istream& in, FunctionStepMethod& ioo); - struct PiecewiseStepType : public SettingBase { - static constexpr const char* const name = "piecewise-step-type"; - static constexpr const char* const description = "Indicates the method used for defining step size when evaluating piecewise curves. Provides interpretation of piecewise-step-param"; - static constexpr PiecewiseStepMethod defaultvalue = MAXSTEPSIZE; + struct FunctionStepType : public SettingBase { + static constexpr const char* const name = "function-step-type"; + static constexpr const char* const description = "Indicates the method used for defining step size when evaluating function-based curves. Provides interpretation of function-step-param"; + static constexpr FunctionStepMethod defaultvalue = MAXSTEPSIZE; }; - struct PiecewiseStepParam : public SettingBase { - static constexpr const char* const name = "piecewise-step-param"; - static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating piecewise curves."; - static constexpr double defaultvalue = 0.5; // ceiling of this value is used when PiecewiseStepMethod is MinSteps + struct FunctionStepParam : public SettingBase { + static constexpr const char* const name = "function-step-param"; + static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating function-based curves."; + static constexpr double defaultvalue = 0.5; // ceiling of this value is used when FunctionStepMethod is MinSteps }; struct ModelOffset : public SettingBase> { @@ -413,7 +413,7 @@ namespace ifcopenshell { template class IFC_GEOM_API SettingsContainer { public: - typedef boost::variant, std::set, std::vector, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes, TriangulationMethod> value_variant_t; + typedef boost::variant, std::set, std::vector, IteratorOutputOptions, FunctionStepMethod, OutputDimensionalityTypes, TriangulationMethod> value_variant_t; private: settings_t settings; @@ -500,7 +500,7 @@ namespace ifcopenshell { }; class IFC_GEOM_API Settings : public SettingsContainer< - std::tuple + std::tuple > {}; } diff --git a/src/ifcgeom/function_item_evaluator.cpp b/src/ifcgeom/function_item_evaluator.cpp new file mode 100644 index 0000000000..9ca71b3450 --- /dev/null +++ b/src/ifcgeom/function_item_evaluator.cpp @@ -0,0 +1,270 @@ +#include "function_item_evaluator.h" +#include "profile_helper.h" + +using namespace ifcopenshell::geometry; + + +struct functor_fn_evaluator : public fn_evaluator { + functor_fn_evaluator(taxonomy::functor_item::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings), + fn_(fn) { + } + + fn_evaluator* clone() const override { return new functor_fn_evaluator(*this); } + double start() const override { return fn_->start(); } + double end() const override { return fn_->end(); } + + Eigen::Matrix4d evaluate(double u) const override { + return (*fn_)(u); + } + + taxonomy::functor_item::const_ptr fn_; +}; + +struct piecewise_fn_evaluator : public fn_evaluator { + piecewise_fn_evaluator(taxonomy::piecewise_function::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings), + fn_(fn) { + } + + fn_evaluator* clone() const override { return new piecewise_fn_evaluator(*this); } + double start() const override { return fn_->start(); } + double end() const override { return fn_->end(); } + + Eigen::Matrix4d evaluate(double u) const override { + // 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" + std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u); + } + + u -= current_span_start_; // make u relative to start of span + function_item_evaluator evaluator(settings_, current_span_fn_); + return evaluator.evaluate(u); + } + + std::tuple get_span(double u) const { + // force u to be within bounds of the curve + double s = fn_->start(); + double e = fn_->end(); + u = std::max(s, u); + u = std::min(u, e); + + double span_start = s; + for (auto& fn : fn_->spans()) { + double span_end = span_start + fn->length(); + auto tolerance = settings_.get().get(); + if (span_start <= u && u < span_end + tolerance) { + return {span_start, span_end, fn}; + } + span_start += fn->length(); + } + + Logger::Error("piecewise span not found."); + return {0, 0, nullptr}; + } + + taxonomy::piecewise_function::const_ptr fn_; + mutable double current_span_start_ = 0; + mutable double current_span_end_ = 0; + mutable taxonomy::function_item::const_ptr current_span_fn_ = nullptr; +}; + +struct gradient_fn_evaluator : public fn_evaluator { + gradient_fn_evaluator(taxonomy::gradient_function::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : + fn_evaluator(settings), + fn_(fn), + horizontal_evaluator_(settings, fn->get_horizontal()), + vertical_evaluator_(settings, fn->get_vertical()) + { + start_ = fn_->get_vertical()->start(); + } + + fn_evaluator* clone() const override { return new gradient_fn_evaluator(*this); } + double start() const override { return fn_->start(); } + double end() const override { return fn_->end(); } + + Eigen::Matrix4d evaluate(double u) const override { + // u is distance from start of vertical. + // add vertical->start() to u to get distance from start of horizontal + auto xy = horizontal_evaluator_.evaluate(u + start_); + auto uz = vertical_evaluator_.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 + uz.col(1).swap(uz.col(2)); // uz is 2D in distance along - y plane, swap y and z so elevations become z + uz.row(1).swap(uz.row(2)); + + Eigen::Matrix4d m; + m = xy * uz; // combine horizontal and vertical + return m; + } + + function_item_evaluator horizontal_evaluator_, vertical_evaluator_; + double start_; // start of vertical + taxonomy::gradient_function::const_ptr fn_; +}; + +struct cant_fn_evaluator : public fn_evaluator { + cant_fn_evaluator(taxonomy::cant_function::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings), + fn_(fn), + gradient_evaluator_(settings, fn->get_gradient()), + cant_evaluator_(settings, fn->get_cant()) { + start_ = fn_->get_cant()->start(); + } + + fn_evaluator* clone() const override { return new cant_fn_evaluator(*this); } + double start() const override { return fn_->start(); } + double end() const override { return fn_->end(); } + + Eigen::Matrix4d evaluate(double u) const override { + // 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_evaluator_.evaluate(u + start_); + auto c = cant_evaluator_.evaluate(u); + + // Need to multiply g and c so the axis vectors + // from cant have the correct rotation applied so + // they are relative to the gradient curve coordinate system + // + // However, the coordinate points don't need to have the rotations + // of g applied. Save off the x,y,z and cant values + auto x = g(0, 3); + auto y = g(1, 3); + auto z = g(2, 3); + auto s = c(1, 3); // superelevation + + // change column 3 to (0,0,0,1) + Eigen::Vector4d p(0, 0, 0, 1); + g.col(3) = p; + c.col(3) = p; + + // multiply g and c to get the axes in the correct orientation + Eigen::Matrix4d m = g * c; + + // reinstate the values for x and y. + // z is the gradient curve z value plus the superelevation + // that comes from the cant. + m(0, 3) = x; + m(1, 3) = y; + m(2, 3) = z + s; + + return m; + } + + function_item_evaluator gradient_evaluator_, cant_evaluator_; + double start_; // start of cant + taxonomy::cant_function::const_ptr fn_; +}; + +struct offset_fn_evaluator : public fn_evaluator { + offset_fn_evaluator(taxonomy::offset_function::const_ptr fn, const ifcopenshell::geometry::Settings& settings) : fn_evaluator(settings), + fn_(fn), + basis_evaluator_(settings, fn->get_basis()), + offset_evaluator_(settings, fn->get_offset()) { + } + + fn_evaluator* clone() const override { return new offset_fn_evaluator(*this); } + double start() const override { return fn_->start(); } + double end() const override { return fn_->end(); } + + Eigen::Matrix4d evaluate(double u) const override { + auto p = basis_evaluator_.evaluate(u); + auto offset = offset_evaluator_.evaluate(u); + Eigen::Matrix4d m = p * offset; + return m; + } + + function_item_evaluator basis_evaluator_, offset_evaluator_; + taxonomy::offset_function::const_ptr fn_; +}; + + + + +function_item_evaluator::function_item_evaluator(const ifcopenshell::geometry::Settings& settings,taxonomy::function_item::const_ptr fn) { + auto kind = fn->kind(); + if (kind == taxonomy::FUNCTOR_ITEM) { + fn_evaluator_ = new functor_fn_evaluator(std::dynamic_pointer_cast(fn),settings); + } else if (kind == taxonomy::PIECEWISE_FUNCTION) { + fn_evaluator_ = new piecewise_fn_evaluator(std::dynamic_pointer_cast(fn), settings); + } else if (kind == taxonomy::GRADIENT_FUNCTION) { + fn_evaluator_ = new gradient_fn_evaluator(std::dynamic_pointer_cast(fn), settings); + } else if (kind == taxonomy::CANT_FUNCTION) { + fn_evaluator_ = new cant_fn_evaluator(std::dynamic_pointer_cast(fn), settings); + } else if (kind == taxonomy::OFFSET_FUNCTION) { + fn_evaluator_ = new offset_fn_evaluator(std::dynamic_pointer_cast(fn), settings); + } else { + Logger::Error("Unexpected function type"); + } +} + +function_item_evaluator::function_item_evaluator(const function_item_evaluator& other) { + fn_evaluator_ = other.fn_evaluator_->clone(); + eval_points_ = other.eval_points_; +} + +function_item_evaluator::~function_item_evaluator() { + delete fn_evaluator_; +} + +std::vector function_item_evaluator::evaluation_points() const { + if (!eval_points_.has_value()) { + double curve_length = fn_evaluator_->length(); + + auto param_type = fn_evaluator_->settings_.get().get(); + auto param = fn_evaluator_->settings_.get().get(); + unsigned num_steps = 0; + if (param_type == ifcopenshell::geometry::settings::FunctionStepMethod::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(fn_evaluator_->start(), fn_evaluator_->end(), num_steps); + } + return *eval_points_; +} + +std::vector function_item_evaluator::evaluation_points(double ustart, double uend, unsigned nsteps) const { + double curve_length = fn_evaluator_->length(); + ustart = std::max(fn_evaluator_->start(), ustart); + uend = std::min(uend, fn_evaluator_->start() + curve_length); + + nsteps = std::max(1u, nsteps); // never have fewer than 1 step + + auto resolution = (uend - ustart) / nsteps; + + std::vector u_values; + u_values.reserve(nsteps); + + for (unsigned i = 0; i <= nsteps; ++i) { + auto u = resolution * i + ustart; + u_values.push_back(u); + } + + return u_values; +} + +taxonomy::item::ptr function_item_evaluator::evaluate() const { + return evaluate(evaluation_points()); +} + +taxonomy::item::ptr function_item_evaluator::evaluate(double ustart, double uend, unsigned nsteps) const { + return evaluate(evaluation_points(ustart, uend, nsteps)); +} + +taxonomy::item::ptr function_item_evaluator::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(0, 3), m(1, 3), m(2, 3))); + } + + return polygon_from_points(polygon); +} + +Eigen::Matrix4d function_item_evaluator::evaluate(double u) const { + return fn_evaluator_->evaluate(u); +} diff --git a/src/ifcgeom/piecewise_function_evaluator.h b/src/ifcgeom/function_item_evaluator.h similarity index 55% rename from src/ifcgeom/piecewise_function_evaluator.h rename to src/ifcgeom/function_item_evaluator.h index 3d91547268..0e29c6149c 100644 --- a/src/ifcgeom/piecewise_function_evaluator.h +++ b/src/ifcgeom/function_item_evaluator.h @@ -7,10 +7,29 @@ namespace ifcopenshell { namespace geometry { -/// @brief utility class to evaluate piecewise_function objects -class piecewise_function_evaluator { +/// @brief Abstract class for evaluating a function_item. This class is specialized for each of the function_item types. +struct fn_evaluator { + fn_evaluator(const ifcopenshell::geometry::Settings& settings) : settings_(settings) { + } + fn_evaluator(const fn_evaluator& other) = default; + virtual ~fn_evaluator() = default; + + virtual fn_evaluator* clone() const = 0; + + virtual Eigen::Matrix4d evaluate(double u) const = 0; + virtual double start() const = 0; + virtual double end() const = 0; + double length() const { return end() - start(); } + + ifcopenshell::geometry::Settings settings_; +}; + +/// @brief utility class to evaluate function_item objects. +class function_item_evaluator { public: - piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings=nullptr); + function_item_evaluator(const ifcopenshell::geometry::Settings& settings, taxonomy::function_item::const_ptr fn); + function_item_evaluator(const function_item_evaluator& other); + ~function_item_evaluator(); /// @brief returns a vector of "distance along" points where the evaluate function computes loop points std::vector evaluation_points() const; @@ -21,11 +40,11 @@ class piecewise_function_evaluator { /// @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 + /// @brief evaluates the function between start and end /// evaluation point step size is taken from the settings object taxonomy::item::ptr evaluate() const; - /// @brief evaluates the piecewise function between ustart and uend + /// @brief evaluates the function between ustart and uend /// if ustart and uend are out of range, the range of values evaluated /// are constrained to start_ and start_+length_ /// @param ustart starting location @@ -34,23 +53,16 @@ class piecewise_function_evaluator { /// @return taxonomy::loop::ptr taxonomy::item::ptr evaluate(double ustart, double uend, unsigned nsteps) const; - /// @brief evaluates the piecewise function at u + /// @brief evaluates the function at u /// @param u u is constrained to be between start_ and start_+length /// @return 4x4 placement matrix Eigen::Matrix4d evaluate(double u) const; private: taxonomy::item::ptr evaluate(const std::vector& dist) const; - std::tuple*> get_span(double u) const; + fn_evaluator* fn_evaluator_ = nullptr; - taxonomy::piecewise_function::const_ptr pwf_; - - ifcopenshell::geometry::Settings settings_; - - mutable double current_span_start_ = 0; - mutable double current_span_end_ = 0; - mutable const std::function* current_span_fn_ = nullptr; - mutable boost::optional> eval_points_; + mutable boost::optional> eval_points_; // cache evaluation points }; }} diff --git a/src/ifcgeom/infra_sweep_helper.cpp b/src/ifcgeom/infra_sweep_helper.cpp index 5456a14fc9..7559d4c9d6 100644 --- a/src/ifcgeom/infra_sweep_helper.cpp +++ b/src/ifcgeom/infra_sweep_helper.cpp @@ -1,6 +1,6 @@ #include "profile_helper.h" #include "infra_sweep_helper.h" -#include "piecewise_function_evaluator.h" +#include "function_item_evaluator.h" #include @@ -14,7 +14,7 @@ namespace { } } -taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& pwf, std::vector& cross_sections) +taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::function_item::ptr& fn, std::vector& cross_sections) { std::sort(cross_sections.begin(), cross_sections.end()); @@ -22,23 +22,23 @@ taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_, // @todo intialize as default loft->axis = nullptr; - // @todo currently only the case is handled where directrix returns a piecewise_function - // @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a piecewise function - if (pwf) { - piecewise_function_evaluator evaluator(pwf, &settings_); + // @todo currently only the case is handled where directrix returns a function_item + // @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a function_item function + if (fn) { + function_item_evaluator evaluator(settings_,fn); double start = std::max(0., cross_sections.front().dist_along); - double end = std::min(pwf->length(), cross_sections.back().dist_along); + double end = std::min(fn->length(), cross_sections.back().dist_along); if (end - start < 1.e-9) { - Logger::Warning("Empty sweep domain with start at " + std::to_string(cross_sections.front().dist_along) + " end at " + std::to_string(cross_sections.back().dist_along) + " and curve domain length " + std::to_string(pwf->length()), inst); + Logger::Warning("Empty sweep domain with start at " + std::to_string(cross_sections.front().dist_along) + " end at " + std::to_string(cross_sections.back().dist_along) + " and curve domain length " + std::to_string(fn->length()), inst); return nullptr; } auto curve_length = end - start; - auto param_type = settings_.get().get(); - auto param = settings_.get().get(); + auto param_type = settings_.get().get(); + auto param = settings_.get().get(); size_t num_steps = 0; - if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { + if (param_type == ifcopenshell::geometry::settings::FunctionStepMethod::MAXSTEPSIZE) { // parameter is max step size num_steps = (size_t)std::ceil(curve_length / param); } else { diff --git a/src/ifcgeom/infra_sweep_helper.h b/src/ifcgeom/infra_sweep_helper.h index 41b38e6c9a..351206dd03 100644 --- a/src/ifcgeom/infra_sweep_helper.h +++ b/src/ifcgeom/infra_sweep_helper.h @@ -18,7 +18,7 @@ namespace ifcopenshell { } }; - taxonomy::loft::ptr make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& directrix, std::vector& cross_sections); + taxonomy::loft::ptr make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::function_item::ptr& directrix, std::vector& cross_sections); } } diff --git a/src/ifcgeom/mapping/IfcCompositeCurve.cpp b/src/ifcgeom/mapping/IfcCompositeCurve.cpp index 568b861ad7..16f7f4f14e 100644 --- a/src/ifcgeom/mapping/IfcCompositeCurve.cpp +++ b/src/ifcgeom/mapping/IfcCompositeCurve.cpp @@ -23,7 +23,7 @@ using namespace ifcopenshell::geometry; taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) { auto loop = taxonomy::make(); - std::vector pwfs; + taxonomy::piecewise_function::spans_t spans; #ifdef SCHEMA_HAS_IfcSegment // 4x3 @@ -74,17 +74,18 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) { for (auto& s : taxonomy::cast(crv)->children) { loop->children.push_back(s); } - } - else if (crv && crv->kind() == taxonomy::PIECEWISE_FUNCTION) { - pwfs.push_back(taxonomy::cast(crv)); - } else if (!crv) { + } else if (auto fi = taxonomy::dcast(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) { + // crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM + // for this reason, a dynamic cast is used and if crv is a function_item it is added to the span + spans.push_back(fi); + } else if (!crv) { return nullptr; } } #endif } - if (pwfs.empty()) { + if (spans.empty()) { aggregate_of_instance::ptr profile = inst->file_->getInverse(inst->id(), &IfcSchema::IfcProfileDef::Class(), -1); const bool force_close = profile && profile->size() > 0; loop->closed = force_close; @@ -92,7 +93,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) { return loop; } else { - auto pwf = taxonomy::make(0.0,pwfs,inst); + auto pwf = taxonomy::make(0.0,spans,inst); return pwf; } } diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 0f10daf9e1..d2b48f8905 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -96,6 +96,87 @@ typedef boost::mpl::vector< #endif > curve_seg_types; + +struct parent_curve_function { + parent_curve_function() = default; + parent_curve_function(const parent_curve_function&) = default; + parent_curve_function(std::function fn) : fn_(fn) { + } + + parent_curve_function& operator=(std::function fn) { + fn_ = fn; + return *this; + } + + virtual Eigen::Matrix4d operator()(double u) const { return fn_(u); } + + private: + std::function fn_; +}; + +struct polynomial_parent_curve : public parent_curve_function { + using parent_curve_function::parent_curve_function; +}; + +struct line_parent_curve : public parent_curve_function { + using parent_curve_function::parent_curve_function; +}; + +struct circle_parent_curve : public parent_curve_function { + using parent_curve_function::parent_curve_function; +}; + +struct spiral_parent_curve : public parent_curve_function { + using parent_curve_function::parent_curve_function; +}; + +// this is the piecewise curve segment function for horizontal and vertical +struct curve_segment_function { + curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& remove_parent_curve_rotation, const Eigen::Matrix4d& remove_parent_curve_translation, std::shared_ptr parent_curve_fn) : + curve_segment_placement_(curve_segment_placement), + remove_parent_curve_rotation_(remove_parent_curve_rotation), + remove_parent_curve_translation_(remove_parent_curve_translation), + parent_curve_fn_(parent_curve_fn) { + } + + Eigen::Matrix4d operator()(double u) const { + Eigen::Matrix4d parent_curve_point = (*parent_curve_fn_)(u); + Eigen::Matrix4d curve_segment_point = curve_segment_placement_ * remove_parent_curve_rotation_ * remove_parent_curve_translation_ * parent_curve_point; + return curve_segment_point; + } + + private: + Eigen::Matrix4d curve_segment_placement_; + Eigen::Matrix4d remove_parent_curve_rotation_; + Eigen::Matrix4d remove_parent_curve_translation_; + std::shared_ptr parent_curve_fn_; +}; + +// this is the piecewise curve segment function for cant +struct cant_curve_segment_function { + cant_curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_start_point, std::shared_ptr parent_curve_fn) : + curve_segment_placement_(curve_segment_placement), + parent_curve_start_point_(parent_curve_start_point), + parent_curve_fn_(parent_curve_fn) { + } + + Eigen::Matrix4d operator()(double u) const { + // The parent curve function returns the cant rotation and superelevation for the parent curve. + // Subtract the parent_curve_start_point to get the incremental cant rotation and superelevation + // Add the incremental cant rotation and superelevation to curve_segment_placement to get the curve_segment_point + Eigen::Matrix4d parent_curve_point = (*parent_curve_fn_)(u); + Eigen::Matrix4d cant_increment = parent_curve_point - parent_curve_start_point_; + Eigen::Matrix4d curve_segment_point = curve_segment_placement_ + cant_increment; + return curve_segment_point; + } + + private: + Eigen::Matrix4d curve_segment_placement_; + Eigen::Matrix4d parent_curve_start_point_; + std::shared_ptr parent_curve_fn_; +}; + +// evaluates a IfcCurveSegment to set up the placement and parent curve function class curve_segment_evaluator { private: mapping* mapping_ = nullptr; @@ -108,24 +189,71 @@ class curve_segment_evaluator { double projected_length_; // for vertical segments, this is the length of curve projected onto the "Distance Along" axis - std::optional> parent_curve_fn_; // function for the parent curve. Function takes distances along, u, and returns the 4x4 position matrix - std::optional parent_curve_start_point_; // placement matrix for the parent curve + std::shared_ptr parent_curve_fn_; // function for the parent curve. Function takes distances along, u, and returns the 4x4 position matrix + std::optional parent_curve_start_point_; // placement matrix for the parent curve - std::optional placement_; // placement of this segment + std::optional curve_segment_placement_; // placement of this segment std::optional next_segment_placement_; // placement of the next segment 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, double length_unit) : mapping_(mapping), inst_(inst), length_unit_(length_unit), - segment_type_(segment_type), parent_curve_(inst->ParentCurve()) { + + auto composite_curves = inst->UsingCurves(); + + // Find the next segment after inst + const IfcSchema::IfcCurveSegment* next_inst = nullptr; + if (composite_curves) { + if (composite_curves->size() == 1) { + auto segments = (*composite_curves->begin())->as()->Segments(); + bool emit_next = false; + for (auto& s : *segments) { + if (emit_next) { + next_inst = s->as(); + break; + } + if (s == inst) { + emit_next = true; + } + } + } else { + Logger::Warning("IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the next segment."); + } + } + + bool is_horizontal = false; + bool is_vertical = false; + bool is_cant = false; + + if (composite_curves) { + for (auto& cc : *composite_curves) { + if (cc->as()) { + is_cant = true; + } else if (cc->as()) { + is_vertical = true; + } else { + is_horizontal = true; + } + } + } + + if ((is_horizontal + is_vertical + is_cant) != 1) { + // We have to choose the correct functor based on usage. We can't + // support multiple, because we don't know the caller at this point. + Logger::Error(std::runtime_error("multiple uses of IfcSegmentCurve not supported"), inst_); + } + + segment_type_ = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT; + + start_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentStart()) * length_unit; length_ = translate_if_param_value(inst->ParentCurve(), inst->SegmentLength()) * length_unit; if (inst) { - placement_ = taxonomy::cast(mapping_->map(inst->Placement()))->ccomponents(); + curve_segment_placement_ = taxonomy::cast(mapping_->map(inst->Placement()))->ccomponents(); } if (next_inst) { @@ -133,7 +261,6 @@ class curve_segment_evaluator { } else { // there is not a next segment, however IfcGradientCurve and IfcSegmentReferenceCurve have an // optional EndPoint which services the same purpose as the zero-length last segment. - auto composite_curves = inst->UsingCurves(); IfcSchema::IfcPlacement* end_point = nullptr; if (composite_curves->size() == 1) { auto& cc = *(composite_curves)->begin(); @@ -165,16 +292,47 @@ class curve_segment_evaluator { return (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_CANT) ? length_ : projected_length_; } - const std::optional>& parent_curve_function() const { - return parent_curve_fn_; - } + taxonomy::ptr get_segment_curve_function() { + if (!parent_curve_fn_ || !parent_curve_start_point_) { + Logger::Error(std::runtime_error(inst_->ParentCurve()->declaration().name() + " not implemented"), inst_); + } - const std::optional& parent_curve_start_point() const { - return parent_curve_start_point_; - } + auto length = fabs(this->length()); - const std::optional& segment_placement() const { - return placement_; + if (segment_type_ == ST_CANT) { + auto fn = cant_curve_segment_function(*curve_segment_placement_, *parent_curve_start_point_, parent_curve_fn_); + return taxonomy::make(length, fn); + } else { + // The parent curve function returns the 4x4 matrix for the parent curve. + // Subtract the parent curve start point (remove the translation and rotation) + // to get the incremental translation and rotation. Apply the incremental + // translation and rotation to the curve_segment_placement to get the curve_segment_point + + // Do a negative translation of the parent curve point relative to the start of the parent curve. + // This moves parent_curve_fn(u=0.0) to coordinate (0,0). + // This is done so the curve_segment_placement is applied relative to (0,0) + Eigen::Matrix4d remove_parent_curve_translation = Eigen::Matrix4d::Identity(); + remove_parent_curve_translation.col(3) = -1.0 * (*parent_curve_start_point_).col(3); + remove_parent_curve_translation(3, 3) = 1.0; + + // Do a rotation so that the tangent of the parent curve is in the direction (1,0) + // Example: if the parent curve IfcLine is at a 30 degree clockwise angle, this does + // a 30 degree counter-clockwise rotation + // Clockwise rotation matrix = [cos(angle) -sin(angle)] + // [sin(angle) cos(angle)] + // + // Counter-clockwise rotation = [ cos(angle) sin(angle)] + // [-sin(angle) cos(angle)] + // + // That's just a sign flip in positions (0,1) and (1,0) + Eigen::Matrix4d remove_parent_curve_rotation = (*parent_curve_start_point_); + remove_parent_curve_rotation(0, 1) *= -1.0; + remove_parent_curve_rotation(1, 0) *= -1.0; + remove_parent_curve_rotation.col(3) = Eigen::Vector4d(0, 0, 0, 1); // remove the parent curve placement point + + auto fn = curve_segment_function(*curve_segment_placement_, remove_parent_curve_rotation, remove_parent_curve_translation, parent_curve_fn_); + return taxonomy::make(length, fn); + } } void set_spiral_function(double s, std::function fnX, std::function fnY) { @@ -224,7 +382,7 @@ class curve_segment_evaluator { }; } - parent_curve_fn_ = [start=start_, s, convert_u, fnX, fnY](double u) { + parent_curve_fn_ = std::make_shared([start=start_, s, convert_u, fnX, fnY](double u) { u = convert_u(u+start); // integration limits, integrate from a to b @@ -241,20 +399,20 @@ class curve_segment_evaluator { m.col(1) = Eigen::Vector4d(-dy, dx, 0, 0); m.col(3) = Eigen::Vector4d(x, y, 0, 1); return m; - }; + }); } else if (segment_type_ == ST_CANT) { Logger::Error(std::runtime_error("Unexpected segment type encountered - cant is handled in set_cant_spiral_function - should never get here")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } else { Logger::Error(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } } // defines the parent_curve_fn_ functor for cant segments. void set_cant_spiral_function(std::function Superelevation, std::function SuperelevationSlope, std::function Cant) { - auto dy = (*placement_)(1, 2); // placement dy - auto dz = (*placement_)(2, 2); // placement dz + auto dy = (*curve_segment_placement_)(1, 2); // placement dy + auto dz = (*curve_segment_placement_)(2, 2); // placement dz auto start_angle = atan2(dz, dy); dy = (next_segment_placement_.has_value() ? (*next_segment_placement_)(1, 2) : 0.0); @@ -266,7 +424,7 @@ class curve_segment_evaluator { auto end_cant = Cant(/* start_ + */ length_); auto delta_cant = end_cant - start_cant; - parent_curve_fn_ = [start_angle,delta_angle,start_cant,delta_cant,Superelevation, SuperelevationSlope, Cant](double u) -> Eigen::Matrix4d { + parent_curve_fn_ = std::make_shared([start_angle,delta_angle,start_cant,delta_cant,Superelevation, SuperelevationSlope, Cant](double u) -> Eigen::Matrix4d { // departure of the curve segment from the base curve (superelevation) auto super_elevation = Superelevation(u); auto slope = SuperelevationSlope(u); @@ -292,7 +450,7 @@ class curve_segment_evaluator { m.col(2) = axis; m.col(3) = Eigen::Vector4d(u, super_elevation, 0.0, 1.0); return m; - }; + }); parent_curve_start_point_ = (*parent_curve_fn_)(0.0); } @@ -304,8 +462,8 @@ class curve_segment_evaluator { boost::optional> superelevation_fn; boost::optional> superelevation_slope_fn; - if (placement_.has_value() && next_segment_placement_.has_value()) { - double y1 = (*placement_)(1, 3); + if (curve_segment_placement_.has_value() && next_segment_placement_.has_value()) { + double y1 = (*curve_segment_placement_)(1, 3); double y2 = (*next_segment_placement_)(1, 3); // if y2-y1 = 0, the super elevation is constant @@ -390,10 +548,10 @@ class curve_segment_evaluator { set_cant_spiral_function(*super, *slope, cant); } else if (segment_type_ == ST_VERTICAL) { Logger::Error(std::runtime_error("IfcCosineSpiral cannot be used for vertical alignment")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } else { Logger::Error(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } } #endif @@ -447,10 +605,10 @@ class curve_segment_evaluator { set_cant_spiral_function(*super, *slope, cant); } else if (segment_type_ == ST_VERTICAL) { Logger::Error(std::runtime_error("IfcSineSpiral cannot be used for vertical alignment")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } else { Logger::Error(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } } #endif @@ -623,7 +781,7 @@ class curve_segment_evaluator { }; } - parent_curve_fn_ = [segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u) { + parent_curve_fn_ = std::make_shared([segment_type = segment_type_, R, pcCenterX, pcCenterY, start_angle, sign_l, convert_u](double u) { u = convert_u(u); // u is measured along the circle @@ -645,7 +803,7 @@ class curve_segment_evaluator { m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0); m.col(3) = Eigen::Vector4d(pcX, pcY, 0.0, 1.0); return m; - }; + }); if (segment_type_ == ST_HORIZONTAL) { parent_curve_start_point_ = (*parent_curve_fn_)(start_); @@ -675,10 +833,10 @@ class curve_segment_evaluator { } else if (segment_type_ == ST_CANT) { Logger::Warning(std::runtime_error("Use of IfcCircle for cant is not supported")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } else { Logger::Error(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } } @@ -706,12 +864,12 @@ class curve_segment_evaluator { auto pcDx = dr[0]; auto pcDy = dr[1]; - if (segment_type_ == ST_VERTICAL && placement_) { + if (segment_type_ == ST_VERTICAL && curve_segment_placement_) { // the general algorithm for mapping parent curve onto curve segment doesn't // exactly work for IfcLine. This is easily overcome by using the curve segment // placement for the IfcLine direction - pcDx = (*placement_)(0, 0); - pcDy = (*placement_)(1, 0); + pcDx = (*curve_segment_placement_)(0, 0); + pcDy = (*curve_segment_placement_)(1, 0); } if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL || segment_type_ == ST_CANT) { @@ -723,7 +881,7 @@ class curve_segment_evaluator { convert_u = [pcDx](double u) { return u/pcDx; }; } - parent_curve_fn_ = [pcX, pcY, pcDx, pcDy, convert_u](double u) { + parent_curve_fn_ = std::make_shared([pcX, pcY, pcDx, pcDy, convert_u](double u) { u = convert_u(u); auto x = pcX + pcDx * u; @@ -734,13 +892,13 @@ class curve_segment_evaluator { m.col(1) = Eigen::Vector4d(-pcDy, pcDx, 0, 0); m.col(3) = Eigen::Vector4d(x, y, 0.0, 1.0); return m; - }; + }); parent_curve_start_point_ = (*parent_curve_fn_)(start_); } else { Logger::Warning(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; - } + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); + } } void operator()(const IfcSchema::IfcPolynomialCurve* pc) { @@ -755,7 +913,7 @@ class curve_segment_evaluator { if (segment_type_ == ST_HORIZONTAL || segment_type_ == ST_VERTICAL) { projected_length_ = length_; - // There is one significant difference between IfcPolynomalCurve used for horizontal and vertical alignments. + // There is one significant difference between IfcPolynomialCurve used for horizontal and vertical alignments. // For horizontal alignment, u is the distance along the curve. For vertical alignment, u is the horizontal distance. // From 4.2.2.2.8 the polynomial curve equation is in the form of y = Ax^3 for horizontal parabolic transition segments. // To evaluate the horizontal function, the value of x that corresponds to the distance along the curve u is needed. @@ -775,7 +933,11 @@ class curve_segment_evaluator { for (; iter != end; iter++) { auto exp = std::distance(begin, iter); auto coeff = (*iter); - value += (double)exp * coeff * pow(lu, lu_exp--) * pow(x, exp - 1); + double a = (double)exp * coeff * pow(lu, lu_exp--); + double b = x ? pow(x, exp - 1) : 0; // when x and exp are zero? 0^-1 is infinite and the result is undefined + double v = a * b; + //double v = (double)exp * coeff * pow(lu, lu_exp--) * pow(x, exp - 1); + value += v; } return value; }; @@ -815,7 +977,7 @@ class curve_segment_evaluator { } // This functor evaluates the polynomial at a distance u along the curve - parent_curve_fn_ = [start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u) -> Eigen::Matrix4d { + parent_curve_fn_ = std::make_shared([start = start_, lu = length_unit_, coeffX, coeffY, convert_u](double u)->Eigen::Matrix4d { auto x = convert_u(u + start); // find x for u // evaluate the polynomial at x std::array*, 2> coefficients{&coeffX, &coeffY}; @@ -851,131 +1013,24 @@ class curve_segment_evaluator { m.col(1) = Eigen::Vector4d(-Dy, Dx, 0, 0); m.col(3) = Eigen::Vector4d(X, Y, 0.0, 1.0); return m; - }; + }); parent_curve_start_point_ = (*parent_curve_fn_)(0.0); // start is added to u in parent_curve_fn_, so use 0.0 here } else if (segment_type_ == ST_CANT) { Logger::Warning(std::runtime_error("Use of IfcPolynomialCurve for cant is not supported")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } else { Logger::Error(std::runtime_error("Unexpected segment type encountered")); - parent_curve_fn_ = [](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }; + parent_curve_fn_ = std::make_shared([](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); }); } } }; } // namespace taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) { - auto composite_curves = inst->UsingCurves(); - - // Find the next segment after inst - const IfcSchema::IfcCurveSegment* next_inst = nullptr; - if (composite_curves) { - if (composite_curves->size() == 1) { - auto segments = (*composite_curves->begin())->as()->Segments(); - bool emit_next = false; - for (auto& s : *segments) { - if (emit_next) { - next_inst = s->as(); - break; - } - if (s == inst) { - emit_next = true; - } - } - } else { - Logger::Warning("IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the next segment."); - } - } - - bool is_horizontal = false; - bool is_vertical = false; - bool is_cant = false; - - if (composite_curves) { - for (auto& cc : *composite_curves) { - if (cc->as()) { - is_cant = true; - } else if (cc->as()) { - is_vertical = true; - } else { - is_horizontal = true; - } - } - } - - if ((is_horizontal + is_vertical + is_cant) != 1) { - // We have to choose the correct functor based on usage. We can't - // support multiple, because we don't know the caller at this point. - return nullptr; - } - - 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, length_unit_); boost::mpl::for_each>(std::ref(cse)); - const auto& parent_curve_fn = cse.parent_curve_function(); - const auto& parent_curve_start_point = cse.parent_curve_start_point(); - - if (!parent_curve_fn || !parent_curve_start_point) { - Logger::Error(std::runtime_error(inst->ParentCurve()->declaration().name() + " not implemented"), inst); - } - - const auto& curve_segment_placement = cse.segment_placement(); - - std::function fn; - if (segment_type == ST_CANT) - { - fn = [curve_segment_placement, parent_curve_start_point, parent_curve_fn](double u) -> Eigen::Matrix4d { - // The parent curve function returns the cant rotation and superelevation for the parent curve. - // Subtract the parent_curve_start_point to get the incremental cant rotation and superelevation - // Add the incremental cant rotation and superelevation to curve_segment_placement to get the curve_segment_point - Eigen::Matrix4d parent_curve_point = (*parent_curve_fn)(u); - Eigen::Matrix4d cant_increment = parent_curve_point - (*parent_curve_start_point); - Eigen::Matrix4d curve_segment_point = (*curve_segment_placement) + cant_increment; - return curve_segment_point; - }; - } else { - // The parent curve function returns the 4x4 matrix for the parent curve. - // Subtract the parent curve start point (remove the translation and rotation) - // to get the incremental translation and rotation. Apply the incremental - // translation and rotation to the curve_segment_placement to get the curve_segment_point - - // Do a negative translation of the parent curve point relative to the start of the parent curve. - // This moves parent_curve_fn(u=0.0) to coordinate (0,0). - // This is done so the curve_segment_placement is applied relative to (0,0) - Eigen::Matrix4d remove_parent_curve_translation = Eigen::Matrix4d::Identity(); - remove_parent_curve_translation.col(3) = -1.0 * (*parent_curve_start_point).col(3); - remove_parent_curve_translation(3, 3) = 1.0; - - // Do a rotation so that the tangent of the parent curve is in the direction (1,0) - // Example: if the parent curve IfcLine is at a 30 degree clockwise angle, this does - // a 30 degree counter-clockwise rotation - // Clockwise rotation matrix = [cos(angle) -sin(angle)] - // [sin(angle) cos(angle)] - // - // Counter-clockwise rotation = [ cos(angle) sin(angle)] - // [-sin(angle) cos(angle)] - // - // That's just a sign flip in positions (0,1) and (1,0) - Eigen::Matrix4d remove_parent_curve_rotation = *parent_curve_start_point; - remove_parent_curve_rotation(0, 1) *= -1.0; - remove_parent_curve_rotation(1, 0) *= -1.0; - remove_parent_curve_rotation.col(3) = Eigen::Vector4d(0, 0, 0, 1); // remove the parent curve placement point - - fn = [curve_segment_placement, remove_parent_curve_rotation, remove_parent_curve_translation, parent_curve_fn](double u) -> Eigen::Matrix4d { - Eigen::Matrix4d parent_curve_point = (*parent_curve_fn)(u); - Eigen::Matrix4d curve_segment_point = (*curve_segment_placement) * remove_parent_curve_rotation * remove_parent_curve_translation * parent_curve_point; - return curve_segment_point; - }; - } - - auto length = cse.length(); - - taxonomy::piecewise_function::spans_t spans; - spans.emplace_back(fabs(length), fn); - auto pwf = taxonomy::make(0.0, spans,inst); - return pwf; + return cse.get_segment_curve_function(); } #endif diff --git a/src/ifcgeom/mapping/IfcFixedReferenceSweptAreaSolid.cpp b/src/ifcgeom/mapping/IfcFixedReferenceSweptAreaSolid.cpp index 79632b9f01..d7b15859d3 100644 --- a/src/ifcgeom/mapping/IfcFixedReferenceSweptAreaSolid.cpp +++ b/src/ifcgeom/mapping/IfcFixedReferenceSweptAreaSolid.cpp @@ -18,7 +18,7 @@ ********************************************************************************/ #include "mapping.h" -#include "../piecewise_function_evaluator.h" +#include "../function_item_evaluator.h" #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; @@ -34,10 +34,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid loft->axis = nullptr; // @todo currently only the case is handled where directrix returns a piecewise_function - if (auto pwf = taxonomy::dcast(dir)) { - piecewise_function_evaluator evaluator(pwf,&settings_); + if (auto fn = taxonomy::dcast(dir)) { + function_item_evaluator evaluator(settings_,fn); double start = 0; - double end = pwf->length(); + double end = fn->length(); #ifdef SCHEMA_HAS_IfcDirectrixCurveSweptAreaSolid // IfcDirectrixCurveSweptAreaSolid introduced in 4.3 changed attribute type // from optional IfcParamValue to optional IfcCurveMeasureSelect. diff --git a/src/ifcgeom/mapping/IfcGradientCurve.cpp b/src/ifcgeom/mapping/IfcGradientCurve.cpp index 6217b81a0e..0d6572cd73 100644 --- a/src/ifcgeom/mapping/IfcGradientCurve.cpp +++ b/src/ifcgeom/mapping/IfcGradientCurve.cpp @@ -18,7 +18,7 @@ ********************************************************************************/ #include "mapping.h" -#include "../piecewise_function_evaluator.h" +#include "../function_item_evaluator.h" #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; @@ -30,15 +30,17 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { auto segments = inst->Segments(); - std::vector pwfs; + taxonomy::piecewise_function::spans_t spans; 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 && crv->kind() == taxonomy::PIECEWISE_FUNCTION) { - pwfs.push_back(taxonomy::cast(crv)); - } else { + if (auto fi = taxonomy::dcast(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) { + // crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM + // for this reason, a dynamic cast is used and if crv is a function_item it is added to the span + spans.push_back(fi); + } else { Logger::Error("Unsupported"); return nullptr; } @@ -56,40 +58,21 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { 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); + auto vertical = taxonomy::make(gradient_start, spans); - // Determine the valid domain of the PWF... the valid domain is where both - // the base curve and gradient curves are defined + // create the horizontal pwf 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)) { + // create the composite gradient curve function + auto gradient_function = taxonomy::make(horizontal, vertical, inst); + + // check to see if there is valid overlap of the horizontal and vertical domains + if (!(0 < gradient_function->length())) { Logger::Error("IfcGradientCurve does not have a common domain with BaseCurve"); + gradient_function = nullptr; // not valid } - // define the callback function for the gradient curve - piecewise_function_evaluator horizontal_evaluator(horizontal, &settings_), vertical_evaluator(vertical, &settings_); - auto composition = [horizontal_evaluator, vertical_evaluator,start=vertical->start()](double u) -> Eigen::Matrix4d { - // u is distance from start of gradient curve (vertical) - // add vertical->start() to u to get distance from start of horizontal - auto xy = horizontal_evaluator.evaluate(u + start); - auto uz = vertical_evaluator.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 - uz.col(1).swap(uz.col(2)); // uz is 2D in distance along - y plane, swap y and z so elevations become z - uz.row(1).swap(uz.row(2)); - - Eigen::Matrix4d m; - m = xy * uz; // combine horizontal and vertical - return m; - }; - - taxonomy::piecewise_function::spans_t spans; - spans.emplace_back(length, composition); - auto pwf = taxonomy::make(start, spans, inst); - return pwf; + return gradient_function; } #endif \ No newline at end of file diff --git a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp index 1ed4d05e8b..df790d2738 100644 --- a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp +++ b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp @@ -19,7 +19,7 @@ #include "mapping.h" #include "../profile_helper.h" -#include "../piecewise_function_evaluator.h" +#include "../function_item_evaluator.h" #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; @@ -39,10 +39,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst auto first_offset_value = *(offset_values->begin()); auto basis_curve = inst->BasisCurve(); - auto pw_curve = taxonomy::dcast(map(basis_curve)); + auto curve = taxonomy::dcast(map(basis_curve)); - double start = pw_curve->start(); - double basis_curve_length = pw_curve->length(); + double start = curve->start(); + double basis_curve_length = curve->length(); taxonomy::piecewise_function::spans_t offset_spans; @@ -70,7 +70,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst m.col(3)(1) = py; m.col(3)(2) = pz; return m; }; - offset_spans.emplace_back(first_distance, fn); + offset_spans.emplace_back(taxonomy::make(first_distance, fn)); } auto iter = offset_values->begin(); @@ -114,7 +114,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst m.col(3)(2) = (l == 0.0 ? zp : (zp + (zn - zp) * u / l)); return m; }; - offset_spans.emplace_back(l, fn); + offset_spans.emplace_back(taxonomy::make(l, fn)); } // at this point, next == end and prev == end-1 @@ -142,25 +142,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst m.col(3)(2) = pz; return m; }; - offset_spans.emplace_back(l, fn); + offset_spans.emplace_back(taxonomy::make(l, fn)); } auto offsets = taxonomy::make(start,offset_spans); - piecewise_function_evaluator pw_evaluator(pw_curve, &settings_), offsets_evaluator(offsets, &settings_); - auto composition = [pw_evaluator, offsets_evaluator](double u) -> Eigen::Matrix4d { - auto p = pw_evaluator.evaluate(u); - auto offset = offsets_evaluator.evaluate(u); - Eigen::Matrix4d m = p * offset; - return m; - }; - - // current implementation assumes that composition is equal to the full length of basis curve - // this may change depending on decisions in the bSI-IF - taxonomy::piecewise_function::spans_t spans; - spans.emplace_back(basis_curve_length, composition); - auto pwf = taxonomy::make(start,spans,inst); - return pwf; + auto fn = taxonomy::make(curve, offsets); + return fn; } #endif \ No newline at end of file diff --git a/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp b/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp index 6097344eb3..180226fb82 100644 --- a/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp +++ b/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp @@ -19,7 +19,7 @@ #include "mapping.h" #include "../profile_helper.h" -#include "../piecewise_function_evaluator.h" +#include "../function_item_evaluator.h" #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; @@ -28,11 +28,15 @@ using namespace ifcopenshell::geometry; taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* inst) { auto u = (*inst->DistanceAlong()->as()) * length_unit_; - //auto basis_curve = inst->BasisCurve(); - //auto item = map(basis_curve); - //auto pw_curve = ifcopenshell::geometry::piecewise_from_item(item); - auto pw_curve = taxonomy::dcast(map(inst->BasisCurve())); - piecewise_function_evaluator evaluator(pw_curve,&settings_); + auto basis_curve = map(inst->BasisCurve()); + taxonomy::function_item::ptr curve = taxonomy::dcast(basis_curve); + if (!curve) { + // if the basis curve is not a function_item, the cast it to piecewise_function. the casting operator + // calls loop_to_piecewise_function_upgrade and will convert loops to a piecewise function + curve = taxonomy::dcast(basis_curve); + } + + function_item_evaluator evaluator(settings_,curve); auto m = evaluator.evaluate(u); auto o = m.col(3).head<3>(); diff --git a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp index d1ab88fbe5..37a7af5b9f 100644 --- a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp +++ b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp @@ -21,7 +21,7 @@ #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; -#include "../piecewise_function_evaluator.h" +#include "../function_item_evaluator.h" #ifdef SCHEMA_HAS_IfcSegmentedReferenceCurve @@ -31,14 +31,16 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins auto segments = inst->Segments(); - std::vector pwfs; + taxonomy::piecewise_function::spans_t spans; 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 && crv->kind() == taxonomy::PIECEWISE_FUNCTION) { - pwfs.push_back(taxonomy::cast(crv)); - } else { + if (auto fi = taxonomy::dcast(crv); crv && fi /*crv->kind() == taxonomy::FUNCTION_ITEM*/) { + // crv->kind() is polymorphic and the kind of the actual function_item is returned. PWF can have spans of any FUNCTION_ITEM + // for this reason, a dynamic cast is used and if crv is a function_item it is added to the span + spans.push_back(fi); + } else { Logger::Error("Unsupported"); return nullptr; } @@ -55,61 +57,15 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins const Eigen::Matrix4d& m = p->ccomponents(); double cant_start = m(0, 3); // start of cant curve - auto cant = taxonomy::make(cant_start,pwfs); + auto cant = taxonomy::make(cant_start,spans); + auto gradient = taxonomy::cast(map(inst->BaseCurve())); - // 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 - piecewise_function_evaluator gradient_evaluator(gradient, &settings_), cant_evaluator(cant, &settings_); - auto composition = [gradient_evaluator, cant_evaluator, start = cant->start()](double u) -> Eigen::Matrix4d { - // 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_evaluator.evaluate(u + start); - auto c = cant_evaluator.evaluate(u); - - // Need to multiply g and c so the axis vectors - // from cant have the correct rotation applied so - // they are relative to the gradient curve coordinate system - // - // However, the coordinate points don't need to have the rotations - // of g applied. Save off the x,y,z and cant values - auto x = g(0, 3); - auto y = g(1, 3); - auto z = g(2, 3); - auto s = c(1, 3); // superelevation - - // change column 3 to (0,0,0,1) - Eigen::Vector4d p(0, 0, 0, 1); - g.col(3) = p; - c.col(3) = p; - - // multiply g and c to get the axes in the correct orientation - Eigen::Matrix4d m = g * c; - - // reinstate the values for x and y. - // z is the gradient curve z value plus the superelevation - // that comes from the cant. - m(0, 3) = x; - m(1, 3) = y; - m(2, 3) = z + s; - - return m; - }; - - taxonomy::piecewise_function::spans_t spans; - spans.emplace_back(length, composition); - auto pwf = taxonomy::make(start, spans, inst); - return pwf; + auto cant_function = taxonomy::make(gradient, cant, inst); + if (!(0 < cant_function->length())) { + Logger::Error("IfcSegmentedReferenceCurve does not have a common domain with BaseCurve"); + cant_function = nullptr; + } + return cant_function; } #endif \ No newline at end of file diff --git a/src/ifcgeom/piecewise_function_evaluator.cpp b/src/ifcgeom/piecewise_function_evaluator.cpp deleted file mode 100644 index b85b165f21..0000000000 --- a/src/ifcgeom/piecewise_function_evaluator.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "piecewise_function_evaluator.h" -#include "profile_helper.h" - -using namespace ifcopenshell::geometry; - - -piecewise_function_evaluator::piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings) : pwf_(pwf) { - if (settings) { - settings_ = *settings; - } -} - -std::vector piecewise_function_evaluator::evaluation_points() const { - if (!eval_points_.has_value()) { - double curve_length = pwf_->length(); - - auto param_type = settings_.get().get(); - auto param = settings_.get().get(); - 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(pwf_->start(), pwf_->start() + curve_length, num_steps); - } - return *eval_points_; -} - -std::vector piecewise_function_evaluator::evaluation_points(double ustart, double uend, unsigned nsteps) const { - double curve_length = pwf_->length(); - ustart = std::max(pwf_->start(), ustart); - uend = std::min(uend, pwf_->start() + curve_length); - - nsteps = std::max(1u, nsteps); // never have fewer than 1 step - - auto resolution = (uend - ustart) / nsteps; - - std::vector u_values; - u_values.reserve(nsteps); - - for (unsigned i = 0; i <= nsteps; ++i) { - auto u = resolution * i + ustart; - u_values.push_back(u); - } - - return u_values; -} - -taxonomy::item::ptr piecewise_function_evaluator::evaluate() const { - return evaluate(evaluation_points()); -} - -taxonomy::item::ptr piecewise_function_evaluator::evaluate(double ustart, double uend, unsigned nsteps) const { - return evaluate(evaluation_points(ustart, uend, nsteps)); -} - -Eigen::Matrix4d piecewise_function_evaluator::evaluate(double u) const { - // 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" - std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u); - } - - u -= current_span_start_; // make u relative to start of span - return (*current_span_fn_)(u); -} - -taxonomy::item::ptr piecewise_function_evaluator::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(0, 3), m(1, 3), m(2, 3))); - } - - return polygon_from_points(polygon); -} - -std::tuple*> piecewise_function_evaluator::get_span(double u) const { - // force u to be within bounds of the curve - double s = pwf_->start(); - double e = pwf_->end(); - u = std::max(s, u); - u = std::min(u, e); - - double span_start = s; - for (auto& [length, fn] : pwf_->spans()) { - double span_end = span_start + length; - auto tolerance = settings_.get().get(); - if (span_start <= u && u < span_end + tolerance) { - return {span_start, span_end, &fn}; - } - span_start += length; - } - - Logger::Error("piecewise_function_impl::get_span span not found."); - return {0, 0, nullptr}; -} diff --git a/src/ifcgeom/piecewise_function_impl.cpp b/src/ifcgeom/piecewise_function_impl.cpp deleted file mode 100644 index 6fa29fb649..0000000000 --- a/src/ifcgeom/piecewise_function_impl.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "piecewise_function_impl.h" -#include "profile_helper.h" - -namespace ifcopenshell { - -namespace geometry { - -namespace taxonomy { - -piecewise_function_impl::piecewise_function_impl(double start, const spans_t& s) : start_(start), spans_(s) { -} - -piecewise_function_impl::piecewise_function_impl(double start, const std::vector& pwfs) : start_(start) { - for (auto& pwf : pwfs) { - spans_.insert(spans_.end(), pwf->spans().begin(), pwf->spans().end()); - } -} - -const piecewise_function_impl::spans_t& piecewise_function_impl::spans() const { return spans_; } - -bool piecewise_function_impl::is_empty() const { return spans_.empty(); } - -double piecewise_function_impl::start() const { - return start_; -} - -double piecewise_function_impl::end() const { - return start_ + length(); -} - -double piecewise_function_impl::length() const { - return std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; }); - - // this is a secondary option where we only compute length once and cache it. - // mutex is needed to prevent interruption of the accumulation if there is multi-threading - // skipping this detail for now and just adding up the span lengths every time - //if (!length_.has_value()) { - // length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; }); - //} - //return *length_; -} - -piecewise_function_impl* piecewise_function_impl::clone_() const { return new piecewise_function_impl(*this); } - -} // namespace taxonomy - -} // namespace geometry - -} // namespace ifcopenshell diff --git a/src/ifcgeom/piecewise_function_impl.h b/src/ifcgeom/piecewise_function_impl.h deleted file mode 100644 index daf8636feb..0000000000 --- a/src/ifcgeom/piecewise_function_impl.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef PIECEWISE_FUNCTION_IMPL -#define PIECEWISE_FUNCTION_IMPL - -#include "taxonomy.h" - -namespace ifcopenshell { - -namespace geometry { - -namespace taxonomy { - -struct piecewise_function_impl { - using spans_t = std::vector>>; - - piecewise_function_impl(double start, const spans_t& s); - piecewise_function_impl(double start, const std::vector& pwfs); - piecewise_function_impl(piecewise_function_impl&&) = default; - piecewise_function_impl(const piecewise_function_impl&) = default; - - const spans_t& spans() const; - bool is_empty() const; - double start() const; - double end() const; - double length() const; - piecewise_function_impl* clone_() const; - - private: - double start_ = 0.0; // starting value of the pwf - spans_t spans_; - - //mutable boost::optional length_; // used for length() method -}; - -} // namespace taxonomy -} // namespace geometry -} // namespace ifcopenshell -#endif PIECEWISE_FUNCTION_IMPL \ No newline at end of file diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 6f5e6249c7..b2d48478f3 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -1,7 +1,7 @@ #include "../ifcparse/IfcLogger.h" #include "taxonomy.h" #include "profile_helper.h" -#include "piecewise_function_impl.h" +#include "function_item_evaluator.h" using namespace ifcopenshell::geometry::taxonomy; @@ -163,10 +163,30 @@ namespace { throw std::runtime_error("not implemented"); } + bool compare(const function_item&, const function_item&) { + throw std::runtime_error("not implemented"); + } + + bool compare(const functor_item&, const functor_item&) { + throw std::runtime_error("not implemented"); + } + bool compare(const piecewise_function&, const piecewise_function&) { throw std::runtime_error("not implemented"); } + bool compare(const gradient_function&, const gradient_function&) { + throw std::runtime_error("not implemented"); + } + + bool compare(const cant_function&, const cant_function&) { + throw std::runtime_error("not implemented"); + } + + bool compare(const offset_function&, const offset_function&) { + throw std::runtime_error("not implemented"); + } + bool compare(const style& a, const style& b) { const int order[5] = { less_to_order(a.name, b.name), @@ -464,27 +484,58 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box( } /////////////////// -piecewise_function::piecewise_function(double start, const spans_t& s, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) { - impl_ = new piecewise_function_impl(start, s); +piecewise_function::piecewise_function(double start, const spans_t& s, const IfcUtil::IfcBaseInterface* instance) : function_item(instance), start_(start), spans_(s) { } -piecewise_function::piecewise_function(double start, const std::vector& pwfs, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) { - impl_ = new piecewise_function_impl(start, pwfs); +piecewise_function::piecewise_function(double start, const std::vector& pwfs, const IfcUtil::IfcBaseInterface* instance) : function_item(instance), start_(start) { + for (auto& pwf : pwfs) { + spans_.insert(spans_.end(), pwf->spans().begin(), pwf->spans().end()); + } }; -piecewise_function::piecewise_function(const piecewise_function& other) : implicit_item(other) { - impl_ = other.impl_->clone_(); +const piecewise_function::spans_t& piecewise_function::spans() const { return spans_; } +bool piecewise_function::is_empty() const { return spans_.empty(); } +double piecewise_function::start() const { return start_; } +double piecewise_function::end() const { return start_ + length(); } +double piecewise_function::length() const { + return std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s->length(); }); + + // this is a secondary option where we only compute length once and cache it. + // mutex is needed to prevent interruption of the accumulation if there is multi-threading + // skipping this detail for now and just adding up the span lengths every time + //if (!length_.has_value()) { + // length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s->length(); }); + //} + //return *length_; } -piecewise_function::~piecewise_function() { - delete impl_; -} -const piecewise_function::spans_t& piecewise_function::spans() const { return impl_->spans(); } -bool piecewise_function::is_empty() const { return impl_->is_empty(); } -double piecewise_function::start() const { return impl_->start(); } -double piecewise_function::end() const { return impl_->end(); } -double piecewise_function::length() const { return impl_->length(); } +gradient_function::gradient_function(piecewise_function::const_ptr horizontal, piecewise_function::const_ptr vertical, const IfcUtil::IfcBaseInterface* instance) : + function_item(instance), horizontal_(horizontal), vertical_(vertical) { +} +double gradient_function::start() const { return std::max(horizontal_->start(), vertical_->start()); } +double gradient_function::end() const { return std::min(horizontal_->end(), vertical_->end()); } +piecewise_function::const_ptr gradient_function::get_horizontal() const { return horizontal_; } +piecewise_function::const_ptr gradient_function::get_vertical() const { return vertical_; } + + +cant_function::cant_function(gradient_function::const_ptr gradient, piecewise_function::const_ptr cant, const IfcUtil::IfcBaseInterface* instance) : + function_item(instance), gradient_(gradient), cant_(cant) { +} +double cant_function::start() const { return std::max(gradient_->start(), cant_->start()); } +double cant_function::end() const { return std::min(gradient_->end(), cant_->end()); } +gradient_function::const_ptr cant_function::get_gradient() const { return gradient_; } +piecewise_function::const_ptr cant_function::get_cant() const { return cant_; } + + +offset_function::offset_function(function_item::const_ptr basis, piecewise_function::const_ptr offset, const IfcUtil::IfcBaseInterface* instance) : function_item(instance), + basis_(basis), + offset_(offset) { +} +double offset_function::start() const { return basis_->start(); } +double offset_function::end() const { return basis_->end(); } +function_item::const_ptr offset_function::get_basis() const { return basis_; } +piecewise_function::const_ptr offset_function::get_offset() const { return offset_; } ifcopenshell::geometry::taxonomy::collection::ptr ifcopenshell::geometry::flatten(const taxonomy::collection::ptr& deep) { @@ -499,7 +550,39 @@ const std::string& ifcopenshell::geometry::taxonomy::kind_to_string(kinds k) { using namespace std::string_literals; static std::string values[] = { - "matrix4"s, "point3"s, "direction3"s, "line"s, "circle"s, "ellipse"s, "bspline_curve"s, "offset_curve"s, "plane"s, "cylinder"s, "sphere"s, "torus"s, "bspline_surface"s, "edge"s, "loop"s, "face"s, "shell"s, "solid"s, "loft"s, "extrusion"s, "revolve"s, "sweep_along_curve"s, "node"s, "collection"s, "boolean_result"s, "piecewise_function"s, "colour"s, "style"s, + "matrix4"s, + "point3"s, + "direction3"s, + "line"s, + "circle"s, + "ellipse"s, + "bspline_curve"s, + "offset_curve"s, + "plane"s, + "cylinder"s, + "sphere"s, + "torus"s, + "bspline_surface"s, + "edge"s, + "loop"s, + "face"s, + "shell"s, + "solid"s, + "loft"s, + "extrusion"s, + "revolve"s, + "sweep_along_curve"s, + "node"s, + "collection"s, + "boolean_result"s, + "function_item"s, + "functor_item"s, + "piecewise_function"s, + "gradient_function"s, + "cant_function"s, + "offset_function"s, + "colour"s, + "style"s, }; return values[k]; @@ -743,7 +826,7 @@ boost::optional ifcopenshell::geometry::taxonomy::loop_ axis = refDirection.cross(Y).normalized(); return make(o, axis, refDirection)->components(); }; - spans.emplace_back(l, fn); + spans.emplace_back(taxonomy::make(l, fn)); } pwf_ = make(0.0,spans); loop_->pwf = pwf_; diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index 2c119c9e4c..f109863fdf 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -93,7 +93,50 @@ typedef item const* ptr; topology_error(const char* const s) : std::runtime_error(s) {} }; - enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE_CURVE, OFFSET_CURVE, PLANE, CYLINDER, SPHERE, TORUS, BSPLINE_SURFACE, EDGE, LOOP, FACE, SHELL, SOLID, LOFT, EXTRUSION, REVOLVE, SWEEP_ALONG_CURVE, NODE, COLLECTION, BOOLEAN_RESULT, PIECEWISE_FUNCTION, COLOUR, STYLE }; + // Implementer note: If you add a new item type, be sure to do the following + // 1) Add a new kind to this list + // 2) Update the values array used by kind_to_string() + // 3) Update the KindsTuple with the class name of the new item + // 4) Add compare function (see compare functions in taxonomy.cpp starting around line 9) + // 4) Update Python bindings + // a) update list of assign_repr in IfcGeomWrapper.i + // b) update inheritance list in IfcGeomWrapper.i (around line 120 in the file) + // c) update item_to_pyobject function definition in type_conversion.i + enum kinds { + MATRIX4, + POINT3, + DIRECTION3, + LINE, + CIRCLE, + ELLIPSE, + BSPLINE_CURVE, + OFFSET_CURVE, + PLANE, + CYLINDER, + SPHERE, + TORUS, + BSPLINE_SURFACE, + EDGE, + LOOP, + FACE, + SHELL, + SOLID, + LOFT, + EXTRUSION, + REVOLVE, + SWEEP_ALONG_CURVE, + NODE, + COLLECTION, + BOOLEAN_RESULT, + FUNCTION_ITEM, + FUNCTOR_ITEM, + PIECEWISE_FUNCTION, + GRADIENT_FUNCTION, + CANT_FUNCTION, + OFFSET_FUNCTION, + COLOUR, + STYLE + }; const std::string& kind_to_string(kinds k); @@ -358,25 +401,73 @@ typedef item const* ptr; using geom_item::geom_item; }; - struct piecewise_function_impl; // forward declaration - struct piecewise_function : public implicit_item { + struct function_item : public implicit_item { + DECLARE_PTR(function_item) + + function_item(const IfcUtil::IfcBaseInterface* instance = nullptr) : implicit_item(instance) {} + function_item(function_item&&) = default; + function_item(const function_item&) = default; + + virtual ~function_item() = default; + virtual double start() const = 0; + virtual double end() const = 0; + virtual double length() const { + return end() - start(); + } + + virtual kinds kind() const { return FUNCTION_ITEM; } + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(FUNCTION_ITEM), 0); + return boost::hash{}(v); + }; + }; + + struct functor_item : public function_item { + DECLARE_PTR(functor_item) + + functor_item(double length, std::function fn, const IfcUtil::IfcBaseInterface* instance = nullptr) : function_item(instance), + length_(length), fn_(fn) {} + functor_item(functor_item&&) = default; + functor_item(const functor_item&) = default; + virtual ~functor_item() = default; + + double start() const override { return 0.0; } + double end() const override { return length_; } + + Eigen::Matrix4d operator()(double u) const { return fn_(u); } + + functor_item* clone_() const override { return new functor_item(*this); } + virtual kinds kind() const { return FUNCTOR_ITEM; } + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(FUNCTOR_ITEM), 0); + return boost::hash{}(v); + } + + private: + double length_; + std::function fn_; + }; + + struct piecewise_function : public function_item { DECLARE_PTR(piecewise_function) - using spans_t = std::vector>>; + using spans_t = std::vector; piecewise_function(double start, const spans_t& s, const IfcUtil::IfcBaseInterface* instance = nullptr); piecewise_function(double start, const std::vector& pwfs, const IfcUtil::IfcBaseInterface* instance = nullptr); piecewise_function(piecewise_function&&) = default; - piecewise_function(const piecewise_function&); - virtual ~piecewise_function(); + piecewise_function(const piecewise_function&) = default; + virtual ~piecewise_function() = default; const spans_t& spans() const; + size_t span_count() const {return spans_.size();} + function_item::const_ptr span_fn(size_t i) { return spans_[i]; } bool is_empty() const; - double start() const; - double end() const; - double length() const; + double start() const override; + double end() const override; + double length() const override; - virtual piecewise_function* clone_() const { return new piecewise_function(*this); } + piecewise_function* clone_() const override { return new piecewise_function(*this); } virtual kinds kind() const { return PIECEWISE_FUNCTION; } virtual size_t calc_hash() const { @@ -385,11 +476,89 @@ typedef item const* ptr; } private: - // note: it would be better if this were a std::unique_ptr, but that requires having the full definition - // of piecewise_function_impl in this header file, which defeats the purpose of the PIMPL idiom. - // if this is a std::unique_ptr, then the _ifcopenshell_wrapper library doesn't compile - piecewise_function_impl* impl_ = nullptr; - }; + double start_ = 0.0; // starting value of the pwf + spans_t spans_; + }; + + struct gradient_function : public function_item { + DECLARE_PTR(gradient_function) + gradient_function(piecewise_function::const_ptr horizontal, piecewise_function::const_ptr vertical, const IfcUtil::IfcBaseInterface* instance = nullptr); + gradient_function(gradient_function&&) = default; + gradient_function(const gradient_function&) = default; + virtual ~gradient_function() = default; + + virtual double start() const override; + virtual double end() const override; + + piecewise_function::const_ptr get_horizontal() const; + piecewise_function::const_ptr get_vertical() const; + + gradient_function* clone_() const override { return new gradient_function(*this); } + + virtual kinds kind() const { return GRADIENT_FUNCTION; } + + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(GRADIENT_FUNCTION), 0); + return boost::hash{}(v); + } + + private: + piecewise_function::const_ptr horizontal_, vertical_; + }; + + struct cant_function : public function_item { + DECLARE_PTR(cant_function) + cant_function(gradient_function::const_ptr gradient, piecewise_function::const_ptr cant, const IfcUtil::IfcBaseInterface* instance = nullptr); + cant_function(cant_function&&) = default; + cant_function(const cant_function&) = default; + virtual ~cant_function() = default; + + virtual double start() const override; + virtual double end() const override; + + gradient_function::const_ptr get_gradient() const; + piecewise_function::const_ptr get_cant() const; + + cant_function* clone_() const override { return new cant_function(*this); } + + virtual kinds kind() const { return CANT_FUNCTION; } + + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(CANT_FUNCTION), 0); + return boost::hash{}(v); + } + + private: + gradient_function::const_ptr gradient_; + piecewise_function::const_ptr cant_; + }; + + struct offset_function : public function_item { + DECLARE_PTR(offset_function) + offset_function(function_item::const_ptr basis, piecewise_function::const_ptr offset, const IfcUtil::IfcBaseInterface* instance = nullptr); + offset_function(offset_function&&) = default; + offset_function(const offset_function&) = default; + virtual ~offset_function() = default; + + virtual double start() const override; + virtual double end() const override; + + function_item::const_ptr get_basis() const; + piecewise_function::const_ptr get_offset() const; + + offset_function* clone_() const override { return new offset_function(*this); } + + virtual kinds kind() const { return OFFSET_FUNCTION; } + + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(OFFSET_FUNCTION), 0); + return boost::hash{}(v); + } + + private: + function_item::const_ptr basis_; + piecewise_function::const_ptr offset_; + }; #ifdef TAXONOMY_USE_SHARED_PTR typedef std::shared_ptr ptr; @@ -1041,7 +1210,7 @@ typedef item const* ptr; }; namespace impl { - typedef std::tuple KindsTuple; + typedef std::tuple KindsTuple; typedef std::tuple CurvesTuple; typedef std::tuple SurfacesTuple; typedef std::tuple UpgradesTuple; diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 8d52625e6e..eff6bd7a3e 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -135,7 +135,12 @@ std::pair vector_to_buffer(const T& t) { if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__matrix4_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__node_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__offset_curve_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__function_item_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__functor_item_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__piecewise_function_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__gradient_function_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__cant_function_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__offset_function_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__plane_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__point3_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__revolve_t); @@ -199,7 +204,12 @@ namespace { %shared_ptr(ifcopenshell::geometry::taxonomy::boolean_result); %shared_ptr(ifcopenshell::geometry::taxonomy::item); %shared_ptr(ifcopenshell::geometry::taxonomy::implicit_item); +%shared_ptr(ifcopenshell::geometry::taxonomy::function_item); +%shared_ptr(ifcopenshell::geometry::taxonomy::functor_item); %shared_ptr(ifcopenshell::geometry::taxonomy::piecewise_function); +%shared_ptr(ifcopenshell::geometry::taxonomy::gradient_function); +%shared_ptr(ifcopenshell::geometry::taxonomy::cant_function); +%shared_ptr(ifcopenshell::geometry::taxonomy::offset_function); %shared_ptr(ifcopenshell::geometry::taxonomy::less_functor); %shared_ptr(ifcopenshell::geometry::taxonomy::eigen_base); %shared_ptr(ifcopenshell::geometry::taxonomy::matrix4); @@ -246,7 +256,7 @@ namespace { %include "../ifcgeom/Iterator.h" %include "../ifcgeom/GeometrySerializer.h" %include "../ifcgeom/taxonomy.h" -%include "../ifcgeom/piecewise_function_evaluator.h" +%include "../ifcgeom/function_item_evaluator.h" %include "../serializers/SvgSerializer.h" %include "../serializers/HdfSerializer.h" @@ -358,7 +368,7 @@ assign_matrix_access(revolve); void set_(const std::string& name, ifcopenshell::geometry::settings::IteratorOutputOptions val) { return $self->set(name, val); } - void set_(const std::string& name, ifcopenshell::geometry::settings::PiecewiseStepMethod val) { + void set_(const std::string& name, ifcopenshell::geometry::settings::FunctionStepMethod val) { return $self->set(name, val); } void set_(const std::string& name, ifcopenshell::geometry::settings::OutputDimensionalityTypes val) { @@ -1291,7 +1301,12 @@ assign_repr(ifcopenshell::geometry::taxonomy::loop) assign_repr(ifcopenshell::geometry::taxonomy::matrix4) assign_repr(ifcopenshell::geometry::taxonomy::node) assign_repr(ifcopenshell::geometry::taxonomy::offset_curve) +assign_repr(ifcopenshell::geometry::taxonomy::function_item) +assign_repr(ifcopenshell::geometry::taxonomy::functor_item) assign_repr(ifcopenshell::geometry::taxonomy::piecewise_function) +assign_repr(ifcopenshell::geometry::taxonomy::gradient_function) +assign_repr(ifcopenshell::geometry::taxonomy::cant_function) +assign_repr(ifcopenshell::geometry::taxonomy::offset_function) assign_repr(ifcopenshell::geometry::taxonomy::plane) assign_repr(ifcopenshell::geometry::taxonomy::point3) assign_repr(ifcopenshell::geometry::taxonomy::revolve) diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index cc6ce90808..b6f344da1a 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -171,7 +171,7 @@ %{ #include "../ifcgeom/Iterator.h" #include "../ifcgeom/taxonomy.h" - #include "../ifcgeom/piecewise_function_evaluator.h" + #include "../ifcgeom/function_item_evaluator.h" #ifdef IFOPSH_WITH_OPENCASCADE #include "../ifcgeom/Serialization/Serialization.h" #include "../ifcgeom/kernels/opencascade/IfcGeomTree.h" @@ -265,7 +265,7 @@ constexpr bool is_std_vector_vector_v = is_std_vector_vector::value; %module ifcopenshell_wrapper %{ #include "../ifcgeom/Converter.h" #include "../ifcgeom/taxonomy.h" - #include "../ifcgeom/piecewise_function_evaluator.h" + #include "../ifcgeom/function_item_evaluator.h" #ifdef IFOPSH_WITH_OPENCASCADE #include "../ifcgeom/Serialization/Serialization.h" #include "../ifcgeom/kernels/opencascade/IfcGeomTree.h" diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index 6c56cc631b..292a2e0c11 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -261,7 +261,12 @@ PyObject* item_to_pyobject(const ifcopenshell::geometry::taxonomy::item::ptr& i) else if (kind == MATRIX4) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__matrix4_t, 0 | SWIG_POINTER_OWN); } else if (kind == NODE) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__node_t, 0 | SWIG_POINTER_OWN); } else if (kind == OFFSET_CURVE) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__offset_curve_t, 0 | SWIG_POINTER_OWN); } + else if (kind == FUNCTION_ITEM) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__function_item_t, 0 | SWIG_POINTER_OWN); } + else if (kind == FUNCTOR_ITEM) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__functor_item_t, 0 | SWIG_POINTER_OWN); } else if (kind == PIECEWISE_FUNCTION) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__piecewise_function_t, 0 | SWIG_POINTER_OWN); } + else if (kind == GRADIENT_FUNCTION) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__gradient_function_t, 0 | SWIG_POINTER_OWN); } + else if (kind == CANT_FUNCTION) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__cant_function_t, 0 | SWIG_POINTER_OWN); } + else if (kind == OFFSET_FUNCTION) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__offset_function_t, 0 | SWIG_POINTER_OWN); } else if (kind == PLANE) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__plane_t, 0 | SWIG_POINTER_OWN); } else if (kind == POINT3) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__point3_t, 0 | SWIG_POINTER_OWN); } else if (kind == REVOLVE) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(std::static_pointer_cast(i))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__revolve_t, 0 | SWIG_POINTER_OWN); }