From 5f7e89a5f89cbcbc24b6287dd799bb0ec1ec2cba Mon Sep 17 00:00:00 2001
From: Richard Brice <37087370+RickBrice@users.noreply.github.com>
Date: Tue, 21 Nov 2023 08:54:06 -0800
Subject: [PATCH] Implements IfcOffsetCurveByDistance mapping
---
.../mapping/IfcOffsetCurveByDistance.cpp | 162 ++++++++++++++++++
src/ifcgeom/mapping/mapping.i | 3 +
2 files changed, 165 insertions(+)
create mode 100644 src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
diff --git a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
new file mode 100644
index 0000000000..32c6ff03cc
--- /dev/null
+++ b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
@@ -0,0 +1,162 @@
+/********************************************************************************
+ * *
+ * 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 copz 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;
+
+// ifc4x1
+//#define SCHEMA_IfcOffsetCurveByDistances_HAS_OffsetValues
+//#define SCHEMA_IfcOffsetCurveByDistances_HAS_Tag
+//#define SCHEMA_IfcOffsetCurveByDistances_Tag_IS_OPTIONAL
+
+#ifdef SCHEMA_HAS_IfcOffsetCurveByDistances
+
+taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst) {
+ auto offset_values = inst->OffsetValues();
+ if (offset_values->size() == 0) {
+ 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 offsets = taxonomy::make();
+
+#if defined SCHEMA_HAS_IfcDistanceExpression
+ double first_distance = first_offset_value->DistanceAlong();
+#else
+ double first_distance = *first_offset_value->DistanceAlong()->as();
+#endif
+
+ if (first_distance < 0.0) {
+ Logger::Warning("IfcOffsetCurveByDistance first offset value is before the start of the curve.");
+ }
+
+ if(0.0 < first_distance)
+ {
+ // First offset is defined after the start of the curve so the lateral and vertical offsets
+ // implicitly continue with the same value towards the start of the basis curve
+ double py = first_offset_value->OffsetLateral().get_value_or(0.0);
+ double pz = first_offset_value->OffsetVertical().get_value_or(0.0);
+ py *= length_unit_;
+ pz *= length_unit_;
+
+ auto fn = [py, pz](double u) -> Eigen::Matrix4d {
+ Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
+ m.col(3)(1) = py;
+ m.col(3)(2) = pz;
+ return m; };
+ offsets->spans.push_back({first_distance, fn});
+ }
+
+ auto iter = offset_values->begin();
+ auto next = std::next(iter);
+ auto prev = std::prev(next);
+ auto end = offset_values->end();
+ for (; next != end; prev++, next++) {
+#if defined SCHEMA_HAS_IfcPointByDistanceExpression
+ if ((*prev)->BasisCurve() != basis_curve || (*next)->BasisCurve() != basis_curve) {
+ Logger::Error("All offsets from a IfcOffsetCurveByDistances must refer to the same BasisCurve");
+ }
+#endif
+
+#if defined SCHEMA_HAS_IfcDistanceExpression
+ double dn = (*next)->DistanceAlong();
+ double dp = (*prev)->DistanceAlong();
+#else
+ double dn = *(*next)->DistanceAlong()->as();
+ double dp = *(*prev)->DistanceAlong()->as();
+#endif
+ if ((dp < 0.0 || basis_curve_length < dp)
+ or
+ (dn < 0.0 || basis_curve_length < dn))
+ {
+ Logger::Warning("IfcOffsetCurveByDistance offset value is out of bounds.");
+ continue;
+ }
+
+ double l = (dn - dp)*length_unit_;
+ double yn = (*next)->OffsetLateral().get_value_or(0.0) * length_unit_;
+ double yp = (*prev)->OffsetLateral().get_value_or(0.0) * length_unit_;
+ double zn = (*next)->OffsetVertical().get_value_or(0.0) * length_unit_;
+ double zp = (*prev)->OffsetVertical().get_value_or(0.0) * length_unit_;
+
+ auto fn = [yp, yn, zp, zn, l](double u) -> Eigen::Matrix4d {
+ Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
+ m.col(3)(1) = (l == 0.0 ? yp : (yp + (yn - yp) * u / l));
+ m.col(3)(2) = (l == 0.0 ? zp : (zp + (zn - zp) * u / l));
+ return m;
+ };
+ offsets->spans.push_back({l, fn});
+ }
+
+ // at this point, next == end and prev == end-1
+ #if defined SCHEMA_HAS_IfcDistanceExpression
+ double last_distance = (*prev)->DistanceAlong() * length_unit_;
+ #else
+ double last_distance = *(*prev)->DistanceAlong()->as() * length_unit_;
+ #endif
+
+ if (basis_curve_length < last_distance) {
+ Logger::Warning("IfcOffsetCurveByDistance last offset value is after the end of the curve.");
+ }
+
+ if (last_distance < basis_curve_length) {
+ // Last offset is defined before the end of the curve so the lateral and vertical offsets
+ // implicitly continue with the same value towards the end of the basis curve
+ double py = (*prev)->OffsetLateral().get_value_or(0.0);
+ double pz = (*prev)->OffsetVertical().get_value_or(0.0);
+ py *= length_unit_;
+ pz *= length_unit_;
+ double l = basis_curve_length - last_distance;
+ auto fn = [py, pz](double u) -> Eigen::Matrix4d {
+ Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
+ m.col(3)(1) = py;
+ m.col(3)(2) = pz;
+ return m; };
+
+ offsets->spans.push_back({l, fn});
+ }
+
+ auto composition = [basis, offsets](double u)->Eigen::Matrix4d {
+ auto p = basis->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
+ // this may change depending on decisions in the bSI-IF
+ auto pwf = taxonomy::make();
+ pwf->spans.emplace_back( basis_curve_length, composition );
+ pwf->instance = inst;
+ return pwf;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/ifcgeom/mapping/mapping.i b/src/ifcgeom/mapping/mapping.i
index 6466904093..3173368d90 100644
--- a/src/ifcgeom/mapping/mapping.i
+++ b/src/ifcgeom/mapping/mapping.i
@@ -120,6 +120,9 @@ BIND(IfcSegmentedReferenceCurve);
BIND(IfcGradientCurve);
#endif
BIND(IfcCompositeCurve);
+#ifdef SCHEMA_HAS_IfcOffsetCurveByDistances
+BIND(IfcOffsetCurveByDistances)
+#endif
BIND(IfcTrimmedCurve);
BIND(IfcArbitraryOpenProfileDef);
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve