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