From a8b88b8ae1cce0bab55fff5907f0577445420bf9 Mon Sep 17 00:00:00 2001
From: Richard Brice <37087370+RickBrice@users.noreply.github.com>
Date: Thu, 11 Jan 2024 15:37:44 -0800
Subject: [PATCH] Adds loop_to_piecewise_function_upgrade
This provides for a more uniform treatment of curves when mapping IfcOffsetCurveByDistances and IfcPointByDistanceExpression
---
.../mapping/IfcOffsetCurveByDistance.cpp | 27 +++--
.../mapping/IfcPointByDistanceExpression.cpp | 10 +-
src/ifcgeom/taxonomy.cpp | 8 +-
src/ifcgeom/taxonomy.h | 99 ++++++++++++++++++-
4 files changed, 117 insertions(+), 27 deletions(-)
diff --git a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
index 390db1ee71..6bba88b482 100644
--- a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
+++ b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
@@ -12,12 +12,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
- * You should have received a copz of the Lesser GNU General Public License *
+ * You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see . *
* *
********************************************************************************/
#include "mapping.h"
+#include "../profile_helper.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -34,17 +35,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
Logger::Error("IfcOffsetCurveByDistances must have at least one offset value");
}
- auto basis_curve = inst->BasisCurve();
-
auto first_offset_value = *(offset_values->begin());
- // todo@ rb - basis_curve might not be piecewise - other valid types are IfcOffsetCurveByDistances, IfcPolyline and IfcIndexedPolyCurve
- // Is there a more generic type that can be evaluated at "u"?
- auto basis = taxonomy::cast(map(basis_curve));
- double basis_curve_length = 0;
- for (auto& s : basis->spans) {
- basis_curve_length += s.first;
- }
+ auto basis_curve = inst->BasisCurve();
+ //auto pw_curve = ifcopenshell::geometry::piecewise_from_item(map(basis_curve));
+ auto pw_curve = taxonomy::dcast(map(basis_curve));
+ double basis_curve_length = pw_curve->length();
auto offsets = taxonomy::make(&settings_);
@@ -95,7 +91,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
#endif
if ((dp < 0.0 || basis_curve_length < dp)
or
- (dn < 0.0 || basis_curve_length < dn))
+ (dn < 0.0 || basis_curve_length < dn)
+ or
+ (dn < dp)
+ )
{
Logger::Warning("IfcOffsetCurveByDistance offset value is out of bounds.");
continue;
@@ -144,14 +143,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
offsets->spans.push_back({l, fn});
}
- auto composition = [basis, offsets](double u)->Eigen::Matrix4d {
- auto p = basis->evaluate(u);
+ auto composition = [pw_curve, offsets](double u) -> Eigen::Matrix4d {
+ auto p = pw_curve->evaluate(u);
auto offset = offsets->evaluate(u);
Eigen::Matrix4d m = p * offset;
return m;
};
- // current implementation assumes that offsets is equal to the full length of basis curve
+ // current implementation assumes that composition is equal to the full length of basis curve
// this may change depending on decisions in the bSI-IF
auto pwf = taxonomy::make(&settings_);
pwf->spans.emplace_back( basis_curve_length, composition );
diff --git a/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp b/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp
index 95a5778a93..51417d924c 100644
--- a/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp
+++ b/src/ifcgeom/mapping/IfcPointByDistanceExpression.cpp
@@ -18,6 +18,8 @@
********************************************************************************/
#include "mapping.h"
+#include "../profile_helper.h"
+
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
@@ -25,9 +27,11 @@ using namespace ifcopenshell::geometry;
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression* inst) {
auto u = (*inst->DistanceAlong()->as()) * length_unit_;
- // @todo: rb - is it safe to assume the basis curve is a piecewise_function?
- auto curve = ifcopenshell::geometry::taxonomy::cast(map(inst->BasisCurve()));
- auto m = curve->evaluate(u);
+ //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()));
+ auto m = pw_curve->evaluate(u);
auto o = m.col(3).head<3>();
auto z = m.col(2).head<3>();
diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp
index 9419db78ee..bafd9d73e9 100644
--- a/src/ifcgeom/taxonomy.cpp
+++ b/src/ifcgeom/taxonomy.cpp
@@ -455,9 +455,7 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box(
ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate() const {
// @todo configure resolution
- double length = 0.0;
- for (auto& s : spans)
- length += s.first;
+ double curve_length = length();
std::vector polygon;
@@ -466,12 +464,12 @@ ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::pi
int num_steps = 0;
if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) {
// parameter is max step size
- num_steps = (int)std::ceil(length / param);
+ num_steps = (int)std::ceil(curve_length / param);
} else {
// parameter is minimum number of steps
num_steps = (int)std::ceil(param);
}
- auto resolution = length / num_steps;
+ auto resolution = curve_length / num_steps;
for (int i = 0; i <= num_steps; ++i) {
auto u = resolution * i;
diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h
index 569d877314..c15c413493 100644
--- a/src/ifcgeom/taxonomy.h
+++ b/src/ifcgeom/taxonomy.h
@@ -2,6 +2,7 @@
#define TAXONOMY_H
#include "../ifcparse/IfcBaseClass.h"
+#include "../ifcparse/IfcLogger.h"
#include "ConversionSettings.h"
@@ -14,6 +15,7 @@
#include
#include
#include
+#include
#ifndef TAXONOMY_USE_UNIQUE_PTR
#ifndef TAXONOMY_USE_NAKED_PTR
@@ -147,6 +149,10 @@ typedef item const* ptr;
// length of span, function to evaluate span
std::vector>> spans;
+ double length() const {
+ return std::accumulate(spans.begin(), spans.end(), 0.0, [](const auto& v,const auto& s) { return v + s.first; });
+ }
+
void print(std::ostream& o, int = 0) const {
o << "piecewise_function" << std::endl;
}
@@ -1096,6 +1102,65 @@ typedef item const* ptr;
}
};
+ // Hacks around not wanting to use if constexpr
+ template
+ class loop_to_piecewise_function_upgrade {
+ public:
+ loop_to_piecewise_function_upgrade(taxonomy::ptr) {}
+
+ operator bool() const {
+ return false;
+ }
+
+ operator taxonomy::piecewise_function::ptr() const {
+ throw taxonomy::topology_error();
+ }
+
+ operator typename T::ptr() const {
+ throw taxonomy::topology_error();
+ }
+ };
+
+ template <>
+ class loop_to_piecewise_function_upgrade {
+ private:
+ boost::optional pwf_;
+
+ public:
+ loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
+ auto loop = taxonomy::dcast(item);
+ if (loop) {
+ pwf_ = taxonomy::make();
+ for (auto& edge : loop->children) {
+ // the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet
+ if (edge->basis) {
+ Logger::Message(Logger::Severity::LOG_NOTICE, "Shape of basis curve ignored - edge is treated as a straight line edge");
+ }
+
+ const auto& s = boost::get(edge->start)->ccomponents();
+ const auto& e = boost::get(edge->end)->ccomponents();
+ Eigen::Vector3d v = e - s;
+ auto l = v.norm(); // the norm of a vector is a measure of its length
+ v.normalize(); // normalize the vector so that it is a unit direction vector
+ std::function fn = [s, v](double u) {
+ Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v);
+ auto Y = axis.cross(refDirection).normalized();
+ axis = refDirection.cross(Y).normalized();
+ return taxonomy::make(o, axis, refDirection)->components();
+ };
+ (*pwf_)->spans.emplace_back(l, fn);
+ }
+ }
+ }
+
+ operator bool() const {
+ return pwf_.is_initialized();
+ }
+
+ operator taxonomy::piecewise_function::ptr() const {
+ return *pwf_;
+ }
+ };
#ifdef TAXONOMY_USE_SHARED_PTR
template
@@ -1104,6 +1169,10 @@ typedef item const* ptr;
if (upg) {
return upg;
}
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
return std::static_pointer_cast(u);
}
template
@@ -1112,7 +1181,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
- return std::dynamic_pointer_cast(u);
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
+ return std::dynamic_pointer_cast(u);
}
#endif
#ifdef TAXONOMY_USE_UNIQUE_PTR
@@ -1122,7 +1195,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
- return static_cast(&*u);
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
+ return static_cast(&*u);
}
template
T* dcast(const std::unique_ptr& u) {
@@ -1130,7 +1207,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
- return dynamic_cast(&*u);
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
+ return dynamic_cast(&*u);
}
#endif
#ifdef TAXONOMY_USE_NAKED_PTR
@@ -1140,7 +1221,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
- return std::static_cast(u);
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
+ return std::static_cast(u);
}
template
T* dcast(const U*& u) {
@@ -1148,7 +1233,11 @@ typedef item const* ptr;
if (upg) {
return upg;
}
- return std::dynamic_cast(u);
+ loop_to_piecewise_function_upgrade pwupg(u);
+ if (pwupg) {
+ return pwupg;
+ }
+ return std::dynamic_cast(u);
}
#endif