diff --git a/src/ifcgeom/infra_sweep_helper.cpp b/src/ifcgeom/infra_sweep_helper.cpp new file mode 100644 index 0000000000..5456a14fc9 --- /dev/null +++ b/src/ifcgeom/infra_sweep_helper.cpp @@ -0,0 +1,203 @@ +#include "profile_helper.h" +#include "infra_sweep_helper.h" +#include "piecewise_function_evaluator.h" + +#include + +using namespace ifcopenshell::geometry; + +namespace { + // std::lerp when upgrading to C++ 20 + template + T lerp(const T& a, const T& b, double t) { + return a + t * (b - a); + } +} + +taxonomy::loft::ptr ifcopenshell::geometry::make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& pwf, std::vector& cross_sections) +{ + std::sort(cross_sections.begin(), cross_sections.end()); + + auto loft = taxonomy::make(); + // @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_); + double start = std::max(0., cross_sections.front().dist_along); + double end = std::min(pwf->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); + return nullptr; + } + + auto curve_length = end - start; + auto param_type = settings_.get().get(); + auto param = settings_.get().get(); + size_t num_steps = 0; + if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { + // parameter is max step size + num_steps = (size_t)std::ceil(curve_length / param); + } else { + // parameter is minimum number of steps + num_steps = (size_t)std::ceil(param); + } + std::vector longitudes; + for (auto& x : cross_sections) { + longitudes.push_back(x.dist_along); + } + longitudes.push_back(std::numeric_limits::infinity()); + auto profile_index = longitudes.begin(); + for (size_t i = 0; i <= num_steps; ++i) { + auto dist_along = start + curve_length / num_steps * i; + while (dist_along > *(profile_index + 1)) { + profile_index++; + if (profile_index == longitudes.end()) { + // @todo handle this? + } + } + + auto relative_dist_along = (dist_along - *profile_index) / (*(profile_index + 1) - *profile_index); + const auto& profile_a = cross_sections[std::distance(longitudes.begin(), profile_index)].section_geometry; + const auto& offset_a = cross_sections[std::distance(longitudes.begin(), profile_index)].offset; + + taxonomy::geom_item::ptr interpolated = nullptr; + + // Only interpolate if: + // - there is a profile ahead of us, and + // - we're not exactly at the location of the current profile or whether there is an offset involved. + bool should_interpolate = + (profile_index + 1 < longitudes.end()) && + (relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.); + + if (should_interpolate) { + taxonomy::geom_item::ptr profile_b; + Eigen::Vector3d offset_b; + if ((profile_index + 1 < longitudes.end())) { + profile_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].section_geometry; + offset_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].offset; + } else { + profile_b = profile_a; + offset_b = offset_a; + } + + // Only interpolate if the profiles are different or either of the offsets is non-zero + bool should_interpolate2 = + (profile_a->instance != profile_b->instance) || + (offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0.); + + if (should_interpolate2) { + + std::vector loops_a, loops_b; + + if (profile_a->kind() == taxonomy::FACE) { + interpolated = taxonomy::make(); + + auto profile_a_f = std::static_pointer_cast(profile_a); + auto profile_b_f = std::static_pointer_cast(profile_b); + + if (profile_a_f->children.size() != profile_b_f->children.size()) { + Logger::Warning("Mismatching number of face boundaries: " + + std::to_string(profile_a_f->children.size()) + " vs " + + std::to_string(profile_b_f->children.size()), + inst + ); + return nullptr; + } + loops_a = profile_a_f->children; + loops_b = profile_b_f->children; + } else { + loops_a = { std::static_pointer_cast(profile_a) }; + loops_b = { std::static_pointer_cast(profile_b) }; + interpolated = taxonomy::make(); + } + + // @todo should_interpolate should also be informed based by different face matrices. + if (profile_a->matrix || profile_b->matrix) { + interpolated->matrix = taxonomy::make(); + Eigen::Matrix4d m4a = Eigen::Matrix4d::Identity(); + Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity(); + if (profile_a->matrix) { + m4a = profile_a->matrix->ccomponents(); + } + if (profile_b->matrix) { + m4b = profile_b->matrix->ccomponents(); + } + interpolated->matrix->components() = lerp(m4a, m4b, relative_dist_along); + } + + auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along); + taxonomy::loop::ptr w1, w2; + taxonomy::edge::ptr e1, e2; + for (auto tmp_ : boost::combine(loops_a, loops_b)) { + boost::tie(w1, w2) = tmp_; + if (w1->children.size() != w2->children.size()) { + Logger::Warning("Mismatching number of edges: " + + std::to_string(w1->children.size()) + " vs " + + std::to_string(w2->children.size()), + inst + ); + return nullptr; + } + std::vector points; + for (auto tmp__ : boost::combine(w1->children, w2->children)) { + boost::tie(e1, e2) = tmp__; + auto& p1 = boost::get(e1->start); + auto& p2 = boost::get(e2->start); + + auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval(); + points.push_back(taxonomy::make(p3)); + } + if (!points.empty()) { + // close polygon by referencing first point + // @todo add a closed=true|false to polygon_from_points()? + points.push_back(points.front()); + } + + auto interpolated_loop = polygon_from_points(points); + if (interpolated->kind() == taxonomy::FACE) { + std::static_pointer_cast(interpolated)->children.push_back(interpolated_loop); + } else { + std::static_pointer_cast(interpolated)->children = interpolated_loop->children; + } + } + } + } + + auto m4 = evaluator.evaluate(dist_along); + /* { + std::wcout << "#" << pwf->instance->data().id() << " " << dist_along << ": " << m4.col(3).row(2).value() << std::endl; + }*/ + + Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity(); + m4b.col(0).head<3>() = m4.col(1).head<3>().normalized(); + m4b.col(1).head<3>() = m4.col(2).head<3>().normalized(); + m4b.col(2).head<3>() = m4.col(0).head<3>().normalized(); + m4b.col(3).head<3>() = m4.col(3).head<3>(); + + if (interpolated) { + loft->children.push_back(interpolated); + } else { + if (profile_a->kind() == taxonomy::FACE) { + loft->children.push_back(std::static_pointer_cast(taxonomy::item::ptr(profile_a->clone_()))); + } else { + loft->children.push_back(std::static_pointer_cast(taxonomy::item::ptr(profile_a->clone_()))); + } + if (profile_a->matrix) { + loft->children.back()->matrix = taxonomy::matrix4::ptr(profile_a->matrix->clone_()); + } + } + if (!loft->children.back()->matrix) { + // @todo should this not be initialized by default? matrix4 already has a 'lazy identity' mechanism. + loft->children.back()->matrix = taxonomy::make(); + } + auto m = (m4b * loft->children.back()->matrix->ccomponents()).eval(); + loft->children.back()->matrix->components() = m; + } + } + + return loft; +} diff --git a/src/ifcgeom/infra_sweep_helper.h b/src/ifcgeom/infra_sweep_helper.h new file mode 100644 index 0000000000..41b38e6c9a --- /dev/null +++ b/src/ifcgeom/infra_sweep_helper.h @@ -0,0 +1,26 @@ +#ifndef LINEAR_SWEEP_HELPER_H +#define LINEAR_SWEEP_HELPER_H + +#include "taxonomy.h" +#include "ConversionSettings.h" + +namespace ifcopenshell { + + namespace geometry { + + struct cross_section { + double dist_along; + taxonomy::geom_item::ptr section_geometry; + Eigen::Vector3d offset; + + bool operator <(const cross_section& other) const { + return dist_along < other.dist_along; + } + }; + + taxonomy::loft::ptr make_loft(const Settings& settings_, const IfcUtil::IfcBaseClass* inst, const taxonomy::piecewise_function::ptr& directrix, std::vector& cross_sections); + } + +} + +#endif \ No newline at end of file diff --git a/src/ifcgeom/kernels/opencascade/loft.cpp b/src/ifcgeom/kernels/opencascade/loft.cpp index b418675fbd..70e174fe01 100644 --- a/src/ifcgeom/kernels/opencascade/loft.cpp +++ b/src/ifcgeom/kernels/opencascade/loft.cpp @@ -48,26 +48,43 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re for (auto it = loft->children.begin(); it < loft->children.end() - 1; ++it) { auto jt = it + 1; - std::array fa = { *it, *jt }; + std::array fa = { *it, *jt }; std::array shps; std::array ws; for (int i = 0; i < 2; ++i) { - if (!convert(fa[i], shps[i])) { - return false; + if (fa[i]->kind() == taxonomy::FACE) { + if (!convert(std::static_pointer_cast(fa[i]), shps[i])) { + return false; + } } - if (shps[i].ShapeType() != TopAbs_FACE) { + if (fa[i]->kind() == taxonomy::LOOP) { + TopoDS_Wire w; + if (!convert(std::static_pointer_cast(fa[i]), w)) { + return false; + } + shps[i] = w; + } + if (shps[i].ShapeType() != TopAbs_FACE && shps[i].ShapeType() != TopAbs_WIRE) { return false; } // @todo this is only outer wire - ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i])); + if (shps[i].ShapeType() == TopAbs_FACE) { + ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i])); + } else { + ws[i] = TopoDS::Wire(shps[i]); + } } - if (it == loft->children.begin()) { - // faces.Append(shps[0]); - BB.Add(comp, shps[0]); - } - if (jt == loft->children.end() - 1) { - // faces.Append(shps[1]); - BB.Add(comp, shps[1]); + if (shps[0].ShapeType() == TopAbs_FACE) { + // When processing a sectioned *surface* there are no + // begin and end caps that need to be added. + if (it == loft->children.begin()) { + // faces.Append(shps[0]); + BB.Add(comp, shps[0]); + } + if (jt == loft->children.end() - 1) { + // faces.Append(shps[1]); + BB.Add(comp, shps[1]); + } } BRepTools_WireExplorer a(ws[0]); BRepTools_WireExplorer b(ws[1]); diff --git a/src/ifcgeom/mapping/IfcSectionedSolidHorizontal.cpp b/src/ifcgeom/mapping/IfcSectionedSolidHorizontal.cpp index 95b750f82f..14ee9899ad 100644 --- a/src/ifcgeom/mapping/IfcSectionedSolidHorizontal.cpp +++ b/src/ifcgeom/mapping/IfcSectionedSolidHorizontal.cpp @@ -22,29 +22,10 @@ using namespace ifcopenshell::geometry; #include "../../ifcgeom/profile_helper.h" -#include "../piecewise_function_evaluator.h" - -#include +#include "../../ifcgeom/infra_sweep_helper.h" #ifdef SCHEMA_HAS_IfcSectionedSolidHorizontal -namespace { - // std::lerp when upgrading to C++ 20 - template - T lerp(const T& a, const T& b, double t) { - return a + t * (b - a); - } - - struct cross_section { - double dist_along; - taxonomy::face::ptr section_geometry; - Eigen::Vector3d offset; - - bool operator <(const cross_section& other) const { - return dist_along < other.dist_along; - } - }; -} taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* inst) { std::vector cross_sections; @@ -106,163 +87,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in } } - std::sort(cross_sections.begin(), cross_sections.end()); - - auto loft = taxonomy::make(); - // @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_); - double start = std::max(0., cross_sections.front().dist_along); - double end = std::min(pwf->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); - return nullptr; - } - - auto curve_length = end - start; - auto param_type = settings_.get().get(); - auto param = settings_.get().get(); - size_t num_steps = 0; - if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { - // parameter is max step size - num_steps = (size_t) std::ceil(curve_length / param); - } else { - // parameter is minimum number of steps - num_steps = (size_t) std::ceil(param); - } - std::vector longitudes; - for (auto& x : cross_sections) { - longitudes.push_back(x.dist_along); - } - longitudes.push_back(std::numeric_limits::infinity()); - auto profile_index = longitudes.begin(); - for (size_t i = 0; i <= num_steps; ++i) { - auto dist_along = start + curve_length / num_steps * i; - while (dist_along > *(profile_index+1)) { - profile_index++; - if (profile_index == longitudes.end()) { - // @todo handle this? - } - } - - auto relative_dist_along = (dist_along - *profile_index) / (*(profile_index+1) - *profile_index); - const auto& profile_a = cross_sections[std::distance(longitudes.begin(), profile_index)].section_geometry; - const auto& offset_a = cross_sections[std::distance(longitudes.begin(), profile_index)].offset; - - taxonomy::face::ptr interpolated = nullptr; - - // Only interpolate if: - // - there is a profile ahead of us, and - // - we're not exactly at the location of the current profile or whether there is an offset involved. - bool should_interpolate = - (profile_index + 1 < longitudes.end()) && - (relative_dist_along >= 1.e-9 || offset_a.cwiseAbs().maxCoeff() > 0.); - - if (should_interpolate) { - taxonomy::face::ptr profile_b; - Eigen::Vector3d offset_b; - if ((profile_index + 1 < longitudes.end())) { - profile_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].section_geometry; - offset_b = cross_sections[std::distance(longitudes.begin(), profile_index) + 1].offset; - } else { - profile_b = profile_a; - offset_b = offset_a; - } - - // Only interpolate if the profiles are different or either of the offsets is non-zero - bool should_interpolate2 = - (profile_a->instance != profile_b->instance) || - (offset_a.cwiseAbs().maxCoeff() > 0. || offset_b.cwiseAbs().maxCoeff() > 0.); - - if (should_interpolate2) { - if (profile_a->children.size() != profile_b->children.size()) { - Logger::Warning("Mismatching number of face boundaries: " + - std::to_string(profile_a->children.size()) + " vs " + - std::to_string(profile_b->children.size()), - inst - ); - return nullptr; - } - interpolated = taxonomy::make(); - // @todo should_interpolate should also be informed based by different face matrices. - if (profile_a->matrix || profile_b->matrix) { - interpolated->matrix = taxonomy::make(); - Eigen::Matrix4d m4a = Eigen::Matrix4d::Identity(); - Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity(); - if (profile_a->matrix) { - m4a = profile_a->matrix->ccomponents(); - } - if (profile_b->matrix) { - m4b = profile_b->matrix->ccomponents(); - } - interpolated->matrix->components() = lerp(m4a, m4b, relative_dist_along); - } - auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along); - taxonomy::loop::ptr w1, w2; - taxonomy::edge::ptr e1, e2; - for (auto tmp_ : boost::combine(profile_a->children, profile_b->children)) { - boost::tie(w1, w2) = tmp_; - if (w1->children.size() != w2->children.size()) { - Logger::Warning("Mismatching number of edges for face boundary: " + - std::to_string(w1->children.size()) + " vs " + - std::to_string(w2->children.size()), - inst - ); - return nullptr; - } - std::vector points; - for (auto tmp__ : boost::combine(w1->children, w2->children)) { - boost::tie(e1, e2) = tmp__; - auto& p1 = boost::get(e1->start); - auto& p2 = boost::get(e2->start); - - auto p3 = (lerp(p1->ccomponents(), p2->ccomponents(), relative_dist_along) + interpolated_offset).eval(); - points.push_back(taxonomy::make(p3)); - } - if (!points.empty()) { - // close polygon by referencing first point - // @todo add a closed=true|false to polygon_from_points()? - points.push_back(points.front()); - } - interpolated->children.push_back(polygon_from_points(points)); - } - } - } - - auto m4 = evaluator.evaluate(dist_along); - /* { - std::wcout << "#" << pwf->instance->data().id() << " " << dist_along << ": " << m4.col(3).row(2).value() << std::endl; - }*/ - - Eigen::Matrix4d m4b = Eigen::Matrix4d::Identity(); - m4b.col(0).head<3>() = m4.col(1).head<3>().normalized(); - m4b.col(1).head<3>() = m4.col(2).head<3>().normalized(); - m4b.col(2).head<3>() = m4.col(0).head<3>().normalized(); - m4b.col(3).head<3>() = m4.col(3).head<3>(); - - if (interpolated) { - loft->children.push_back(interpolated); - } else { - loft->children.push_back(taxonomy::face::ptr(profile_a->clone_())); - if (profile_a->matrix) { - loft->children.back()->matrix = taxonomy::matrix4::ptr(profile_a->matrix->clone_()); - } - } - if (!loft->children.back()->matrix) { - // @todo should this not be initialized by default? matrix4 already has a 'lazy identity' mechanism. - loft->children.back()->matrix = taxonomy::make(); - } - auto m = (m4b * loft->children.back()->matrix->ccomponents()).eval(); - loft->children.back()->matrix->components() = m; - } - } - - return loft; + return make_loft(settings_, inst, pwf, cross_sections); } #endif diff --git a/src/ifcgeom/mapping/IfcSectionedSurface.cpp b/src/ifcgeom/mapping/IfcSectionedSurface.cpp new file mode 100644 index 0000000000..3edcde7946 --- /dev/null +++ b/src/ifcgeom/mapping/IfcSectionedSurface.cpp @@ -0,0 +1,93 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "mapping.h" +#define mapping POSTFIX_SCHEMA(mapping) +using namespace ifcopenshell::geometry; + +#include "../../ifcgeom/profile_helper.h" +#include "../../ifcgeom/infra_sweep_helper.h" + +#ifdef SCHEMA_HAS_IfcSectionedSurface + + +taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface* inst) { + std::vector cross_sections; + + auto dir = map(inst->Directrix()); + auto pwf = taxonomy::dcast(dir); + if (!pwf) { + // Only implement on alignment curves + Logger::Warning("IfcSectionedSurface is only implemented for piecewise function Directrix curves", inst); + return nullptr; + } + + { + auto css = inst->CrossSections(); + auto csps = inst->CrossSectionPositions(); + std::vector faces; + + // The PointByDistanceExpressesions are factored out into (a) a cartesian offset relative to the + // reference frame along a certain curve location (b) the longitude. + + // The longitudes determine the range of the sweep and the offsets are interpolated in between + // sweep segments. + std::vector profile_offsets; + std::vector longitudes; + + for (auto& cs : *css) { + faces.push_back(std::move(taxonomy::cast(map(cs)))); + } +#ifdef SCHEMA_HAS_IfcPointByDistanceExpression + for (auto& csp : *csps) { + auto pbde = csp->Location()->as(true); + + longitudes.push_back(*pbde->DistanceAlong()->as(true) * length_unit_); + + // Corresponds to the profile X, Y directions (hopefully). + Eigen::Vector3d po( + pbde->OffsetLateral().get_value_or(0.), + // @todo I don't understand whether vertical is an offset relative to the tangent plane or to the global XY plane + pbde->OffsetVertical().get_value_or(0.), + 0. + ); + + profile_offsets.push_back(po); + } +#else + return nullptr; +#endif + if (faces.size() != profile_offsets.size()) { + Logger::Warning("Expected CrossSections and CrossSectionPositions to be equal length, but got " + std::to_string(faces.size()) + " and " + std::to_string(profile_offsets.size()) + " respectively", inst); + return nullptr; + } + if (faces.size() < 2) { + Logger::Warning("Expected at least two cross sections, but got " + std::to_string(faces.size()), inst); + return nullptr; + } + + for (size_t i = 0; i < faces.size(); ++i) { + cross_sections.push_back({ longitudes[i], faces[i], profile_offsets[i] }); + } + } + + return make_loft(settings_, inst, pwf, cross_sections); +} + +#endif diff --git a/src/ifcgeom/mapping/mapping.i b/src/ifcgeom/mapping/mapping.i index c6ed7a11f9..51e001f05d 100644 --- a/src/ifcgeom/mapping/mapping.i +++ b/src/ifcgeom/mapping/mapping.i @@ -135,6 +135,9 @@ BIND(IfcFixedReferenceSweptAreaSolid) #ifdef SCHEMA_HAS_IfcSectionedSolidHorizontal BIND(IfcSectionedSolidHorizontal) #endif +#ifdef SCHEMA_HAS_IfcSectionedSurface +BIND(IfcSectionedSurface) +#endif BIND(IfcCircle); BIND(IfcEllipse); diff --git a/src/ifcgeom/piecewise_function_evaluator.cpp b/src/ifcgeom/piecewise_function_evaluator.cpp index e4a03511ca..b85b165f21 100644 --- a/src/ifcgeom/piecewise_function_evaluator.cpp +++ b/src/ifcgeom/piecewise_function_evaluator.cpp @@ -4,7 +4,7 @@ using namespace ifcopenshell::geometry; -piecewise_function_evaluator::piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, ifcopenshell::geometry::Settings* settings) : pwf_(pwf) { +piecewise_function_evaluator::piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings) : pwf_(pwf) { if (settings) { settings_ = *settings; } diff --git a/src/ifcgeom/piecewise_function_evaluator.h b/src/ifcgeom/piecewise_function_evaluator.h index ca1bddce54..3d91547268 100644 --- a/src/ifcgeom/piecewise_function_evaluator.h +++ b/src/ifcgeom/piecewise_function_evaluator.h @@ -10,7 +10,7 @@ namespace ifcopenshell { namespace geometry { /// @brief utility class to evaluate piecewise_function objects class piecewise_function_evaluator { public: - piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, ifcopenshell::geometry::Settings* settings=nullptr); + piecewise_function_evaluator(taxonomy::piecewise_function::const_ptr pwf, const ifcopenshell::geometry::Settings* settings=nullptr); /// @brief returns a vector of "distance along" points where the evaluate function computes loop points std::vector evaluation_points() const; diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 1dd61d72b0..2feacfae15 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -313,7 +313,7 @@ namespace { } bool compare(const loft& a, const loft& b) { - return compare_collection(a, b); + return compare_collection(a, b); } bool compare(const collection& a, const collection& b) { diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index db9c97863e..c1ced08786 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -810,7 +810,7 @@ typedef item const* ptr; } }; - struct loft : public collection_base { + struct loft : public collection_base { DECLARE_PTR(loft) item::ptr axis; diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 2b2178e125..ca0851324f 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -316,7 +316,7 @@ assign_children_access(loop, edge); assign_children_access(face, loop); assign_children_access(shell, face); assign_children_access(solid, shell); -assign_children_access(loft, face); +assign_children_access(loft, geom_item); assign_children_access(boolean_result, geom_item); %define assign_matrix_access(item_name)