Files
IfcOpenShell/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
8.0 KiB
C++
Raw Normal View History

2023-11-21 08:54:06 -08:00
/********************************************************************************
* *
* 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. *
* *
2024-01-11 15:37:44 -08:00
* You should have received a copy of the Lesser GNU General Public License *
2023-11-21 08:54:06 -08:00
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "mapping.h"
2024-01-11 15:37:44 -08:00
#include "../profile_helper.h"
2025-01-02 11:10:56 -08:00
#include "../function_item_evaluator.h"
2023-11-21 08:54:06 -08:00
#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.empty()) {
2023-11-21 08:54:06 -08:00
Logger::Error("IfcOffsetCurveByDistances must have at least one offset value");
}
auto& first_offset_value = offset_values.front();
2023-11-21 08:54:06 -08:00
auto basis_curve = inst.BasisCurve();
2025-07-25 17:32:11 -07:00
// // IfcOffsetCurveByDistances can be based on another IfcOffsetCurveByDistances, an IfcGradientCurve, or an IfcCompositeCurve
// // When based on IfcOffsetCurveByDistances, it creates a chain of curves that we must navigate down to the base curve.
// // The source curve is IfcGradientCurve or IfcCompositeCurve. This loop drills down to the base curve.
// while (auto offset_curve = basis_curve->as<IfcSchema::IfcOffsetCurveByDistances>()) {
// basis_curve = offset_curve;
// }
//
//#if defined SCHEMA_HAS_IfcGradientCurve
// if (auto gc = basis_curve->as<IfcSchema::IfcGradientCurve>()) {
// basis_curve = gc->BaseCurve();
// }
//#endif
auto basis_curve_fn = taxonomy::dcast<taxonomy::function_item>(map(basis_curve));
if (!basis_curve_fn) {
2025-03-14 07:31:50 -07:00
// Only implement on alignment curves
Logger::Warning("IfcOffsetCurveByDistances is only implemented for BasisCurves curves based on taxonomy::function_item", inst);
return nullptr;
}
2025-07-25 17:32:11 -07:00
double start = basis_curve_fn->start();
double basis_curve_length = basis_curve_fn->length();
2023-11-21 08:54:06 -08:00
taxonomy::piecewise_function::spans_t offset_spans;
2023-11-21 08:54:06 -08:00
#if defined SCHEMA_HAS_IfcDistanceExpression
double first_distance = first_offset_value.DistanceAlong();
2023-11-21 08:54:06 -08:00
#else
double first_distance = first_offset_value.DistanceAlong().as<IfcSchema::IfcLengthMeasure>();
2023-11-21 08:54:06 -08:00
#endif
2025-07-25 17:32:11 -07:00
first_distance *= length_unit_;
2023-11-21 08:54:06 -08:00
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().value_or(0.0);
double pz = first_offset_value.OffsetVertical().value_or(0.0);
2023-11-21 08:54:06 -08:00
py *= length_unit_;
pz *= length_unit_;
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
2023-11-21 08:54:06 -08:00
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(3)(1) = py;
m.col(3)(2) = pz;
return m; };
2025-01-02 11:10:56 -08:00
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(first_distance, fn));
2023-11-21 08:54:06 -08:00
}
auto iter = offset_values.begin();
2023-11-21 08:54:06 -08:00
auto next = std::next(iter);
auto prev = std::prev(next);
auto end = offset_values.end();
2023-11-21 08:54:06 -08:00
for (; next != end; prev++, next++) {
#if defined SCHEMA_HAS_IfcDistanceExpression
double dp = (*prev).DistanceAlong();
double dn = (*next).DistanceAlong();
2023-11-21 08:54:06 -08:00
#else
double dp = prev->DistanceAlong().as<IfcSchema::IfcLengthMeasure>();
double dn = next->DistanceAlong().as<IfcSchema::IfcLengthMeasure>();
2023-11-21 08:54:06 -08:00
#endif
2025-07-25 17:32:11 -07:00
dp *= length_unit_;
dn *= length_unit_;
if (dn < dp) // next is before previous
2023-11-21 08:54:06 -08:00
{
Logger::Warning("IfcOffsetCurveByDistance offset value is out of bounds.");
continue;
}
2025-07-25 17:32:11 -07:00
double l = (dn - dp);
double yn = next->OffsetLateral().value_or(0.0) * length_unit_;
double yp = prev->OffsetLateral().value_or(0.0) * length_unit_;
double zn = next->OffsetVertical().value_or(0.0) * length_unit_;
double zp = prev->OffsetVertical().value_or(0.0) * length_unit_;
if ( (dp < 0.0 && dn < 0.0) || (basis_curve_length < dp && basis_curve_length < dn) ) {
// both points are either before the start of the curve or after the end of the curve. ignore them.
continue;
}
if (dp < 0.0) {
// previous is before the start of the curve
// compute y and z offsets at the start of the curve
auto yp_at_start = yp - (yn - yp) * dp / l;
auto zp_at_start = zp - (zn - zp) * dp / l;
dp = 0.0;
yp = yp_at_start;
zp = zp_at_start;
}
if (basis_curve_length < dn) {
// next is after the end of the curve
// compute y and z offsets at the end of the curve
auto yn_at_end = yn - (yn - yp) * (dn - basis_curve_length) / l;
auto zn_at_end = zn - (zn - zp) * (dn - basis_curve_length) / l;
dn = basis_curve_length;
yn = yn_at_end;
zn = zn_at_end;
}
2023-11-21 08:54:06 -08:00
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;
};
2025-01-02 11:10:56 -08:00
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
2023-11-21 08:54:06 -08:00
}
// 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 = (double) prev->DistanceAlong().as<IfcSchema::IfcLengthMeasure>() * length_unit_;
2023-11-21 08:54:06 -08:00
#endif
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().value_or(0.0);
double pz = prev->OffsetVertical().value_or(0.0);
2023-11-21 08:54:06 -08:00
py *= length_unit_;
pz *= length_unit_;
double l = basis_curve_length - last_distance;
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
2023-11-21 08:54:06 -08:00
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
m.col(3)(1) = py;
m.col(3)(2) = pz;
return m; };
2025-01-02 11:10:56 -08:00
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
2023-11-21 08:54:06 -08:00
}
auto offsets = taxonomy::make<taxonomy::piecewise_function>(start,offset_spans);
2025-07-25 17:32:11 -07:00
auto fn = taxonomy::make<taxonomy::offset_function>(basis_curve_fn, offsets);
2025-01-02 11:10:56 -08:00
return fn;
2023-11-21 08:54:06 -08:00
}
#endif