/******************************************************************************** * * * 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" #include "../profile_helper.h" #include "../function_item_evaluator.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.empty()) { logger::error("IfcOffsetCurveByDistances must have at least one offset value"); } auto& first_offset_value = offset_values.front(); auto basis_curve = inst.BasisCurve(); // // 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()) { // basis_curve = offset_curve; // } // //#if defined SCHEMA_HAS_IfcGradientCurve // if (auto gc = basis_curve->as()) { // basis_curve = gc->BaseCurve(); // } //#endif auto basis_curve_fn = taxonomy::dcast(map(basis_curve)); if (!basis_curve_fn) { // Only implement on alignment curves logger::warning("IfcOffsetCurveByDistances is only implemented for BasisCurves curves based on taxonomy::function_item", inst); return nullptr; } double start = basis_curve_fn->start(); double basis_curve_length = basis_curve_fn->length(); taxonomy::piecewise_function::spans_t offset_spans; #if defined SCHEMA_HAS_IfcDistanceExpression double first_distance = first_offset_value.DistanceAlong(); #else double first_distance = first_offset_value.DistanceAlong().as(); #endif first_distance *= length_unit_; 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); 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; }; offset_spans.emplace_back(taxonomy::make(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_IfcDistanceExpression double dp = (*prev).DistanceAlong(); double dn = (*next).DistanceAlong(); #else double dp = prev->DistanceAlong().as(); double dn = next->DistanceAlong().as(); #endif dp *= length_unit_; dn *= length_unit_; if (dn < dp) // next is before previous { logger::warning("IfcOffsetCurveByDistance offset value is out of bounds."); continue; } 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; } 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; }; offset_spans.emplace_back(taxonomy::make(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 = (double) prev->DistanceAlong().as() * length_unit_; #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); 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; }; offset_spans.emplace_back(taxonomy::make(l, fn)); } auto offsets = taxonomy::make(start,offset_spans); auto fn = taxonomy::make(basis_curve_fn, offsets); return fn; } #endif