diff --git a/src/examples/IfcAlignment.cpp b/src/examples/IfcAlignment.cpp index e00ce75690..10dc994915 100644 --- a/src/examples/IfcAlignment.cpp +++ b/src/examples/IfcAlignment.cpp @@ -37,6 +37,46 @@ double to_radian(double deg) { return PI * deg / 180; } #define Schema Ifc4x3_add2 +// performs basic project setup including created the IfcProject object +// and initializing the project units to FEET +Schema::IfcProject* setup_project(IfcHierarchyHelper& file) { + std::vector file_description; + file_description.push_back("ViewDefinition[Alignment-basedReferenceView]"); + file.header().file_description().description(file_description); + + auto project = file.addProject(); + project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment")); + + // set up project units for feet + // the call to file.addProject() sets up length units as millimeter. + auto units_in_context = project->UnitsInContext(); + auto units = units_in_context->Units(); + auto begin = units->begin(); + auto iter = begin; + auto end = units->end(); + for (; iter != end; iter++) { + auto unit = *iter; + if (unit->as() && unit->as()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) { + auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0); + file.addEntity(dimensions); + + auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as()); + file.addEntity(conversion_factor); + + auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor); + file.addEntity(conversion_based_unit); + + units->remove(unit); // remove the millimeter unit + units->push(conversion_based_unit); // add the feet unit + units_in_context->setUnits(units); // update the UnitsInContext + + break; // Done!, the length unit was found, so break out of the loop + } + } + + return project; +} + // creates geometry and business logic segments for horizontal alignment tangent runs std::pair create_tangent(typename Schema::IfcCartesianPoint* p, double dir, double length) { // geometry @@ -161,39 +201,7 @@ void create_segment_representations(IfcHierarchyHelper& file, Schema::If int main() { IfcHierarchyHelper file; - std::vector file_description; - file_description.push_back("ViewDefinition[Alignment-basedReferenceView]"); - file.header().file_description().description(file_description); - - auto project = file.addProject(); - project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment")); - - // set up project units for feet - // the call to file.addProject() sets up length units as millimeter. - auto units_in_context = project->UnitsInContext(); - auto units = units_in_context->Units(); - auto begin = units->begin(); - auto iter = begin; - auto end = units->end(); - for (; iter != end; iter++) { - auto unit = *iter; - if (unit->as() && unit->as()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) { - auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0); - file.addEntity(dimensions); - - auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as()); - file.addEntity(conversion_factor); - - auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor); - file.addEntity(conversion_based_unit); - - units->remove(unit); // remove the millimeter unit - units->push(conversion_based_unit); // add the feet unit - units_in_context->setUnits(units); // update the UnitsInContext - - break; // Done!, the length unit was found, so break out of the loop - } - } + auto project = setup_project(file); auto geometric_representation_context = file.getRepresentationContext(std::string("Model")); // creates the representation context if it doesn't already exist diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 2c9c1670ce..35e5b7f4d1 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -312,15 +312,22 @@ class cant_adjuster : public segment_geometry_adjuster { const Eigen::Matrix4d& get_start_of_segment() const { return transformation_matrix_; } }; +// vector of parent curve types that are supported for IfcCurveSegment.ParentCurve typedef boost::mpl::vector< IfcSchema::IfcLine #ifdef SCHEMA_HAS_IfcClothoid , IfcSchema::IfcClothoid #endif #if defined SCHEMA_HAS_IfcSecondOrderPolynomialSpiral - //, IfcSchema::IfcSecondOrderPolynomialSpiral // this isn't implemented yet, just some stubbed out dummy code + , IfcSchema::IfcSecondOrderPolynomialSpiral #endif - , IfcSchema::IfcPolyline +#if defined SCHEMA_HAS_IfcThirdOrderPolynomialSpiral + , IfcSchema::IfcThirdOrderPolynomialSpiral +#endif +#if defined SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral + , IfcSchema::IfcSeventhOrderPolynomialSpiral +#endif + , IfcSchema::IfcPolyline , IfcSchema::IfcCircle , IfcSchema::IfcPolynomialCurve > curve_seg_types; @@ -451,9 +458,9 @@ class curve_segment_evaluator { geometry_adjuster = std::make_shared(mapping_, segment_type_, inst_, next_inst_); // see https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcClothoid.htm // also see, https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Partial_Templates/Geometry/Curve_Segment_Geometry/Clothoid_Transition_Segment/content.html, - // which defines the clothoid constant as sqrt(L) and L is the length measured from the inflection point + // which defines the clothoid constant as sqrt(L*R) and L is the length measured from the inflection point and R is the radius at L auto A = c->ClothoidConstant(); - auto s = fabs(A * sqrt(PI)); + auto s = fabs(A * sqrt(PI)); // curve length when u = 1.0 auto fn_x = [A, s](double t) -> double { return s * cos(PI * A * t * t / (2 * fabs(A))); }; auto fn_y = [A, s](double t) -> double { return s * sin(PI * A * t * t / (2 * fabs(A))); }; @@ -465,7 +472,6 @@ class curve_segment_evaluator { #ifdef SCHEMA_HAS_IfcSecondOrderPolynomialSpiral void operator()(const IfcSchema::IfcSecondOrderPolynomialSpiral* c) { - // @todo: rb verify - this is an example implementation of a different kind of spiral - lots of clean up needed auto A0 = c->ConstantTerm(); auto A1 = c->LinearTerm(); auto A2 = c->QuadraticTerm(); @@ -481,11 +487,68 @@ class curve_segment_evaluator { auto fn_x = [theta](double t)->double {return cos(theta(t)); }; auto fn_y = [theta](double t)->double {return sin(theta(t)); }; - double s = 1.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0 + double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0 + Logger::Warning(std::string("IfcSecondOrderPolynomialSpiral - the implementation has a bug")); set_spiral_function(mapping_, c, s, fn_x, fn_y); } #endif +#ifdef SCHEMA_HAS_IfcThirdOrderPolynomialSpiral + void operator()(const IfcSchema::IfcThirdOrderPolynomialSpiral* c) { + auto A0 = c->ConstantTerm(); + auto A1 = c->LinearTerm(); + auto A2 = c->QuadraticTerm(); + auto A3 = c->CubicTerm(); + + auto theta = [A0, A1, A2, A3](double t) { + auto a0 = A0.has_value() ? t / A0.value() : 0.0; + auto a1 = A1.has_value() ? A1.value() * std::pow(t, 2) / (2 * fabs(std::pow(A1.value(), 3))) : 0.0; + auto a2 = A2.has_value() ? std::pow(t, 3) / (3 * std::pow(A2.value(), 3)) : 0.0; + auto a3 = A3 * std::pow(t, 4) / (4 * fabs(std::pow(A3, 5))); + return a0 + a1 + a2 + a3; + }; + + auto fn_x = [theta](double t) -> double { return cos(theta(t)); }; + auto fn_y = [theta](double t) -> double { return sin(theta(t)); }; + + double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0 + Logger::Warning(std::string("IfcThirdOrderPolynomialSpiral - the implementation has a bug")); + set_spiral_function(mapping_, c, s, fn_x, fn_y); + } +#endif + +#ifdef SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral + void operator()(const IfcSchema::IfcSeventhOrderPolynomialSpiral* c) { + auto A0 = c->ConstantTerm(); + auto A1 = c->LinearTerm(); + auto A2 = c->QuadraticTerm(); + auto A3 = c->CubicTerm(); + auto A4 = c->QuarticTerm(); + auto A5 = c->QuinticTerm(); + auto A6 = c->SexticTerm(); + auto A7 = c->SepticTerm(); + + auto theta = [A0, A1, A2, A3, A4, A5, A6, A7](double t) { + auto a0 = A0.has_value() ? t / A0.value() : 0.0; + auto a1 = A1.has_value() ? A1.value() * std::pow(t, 2) / (2 * fabs(std::pow(A1.value(), 3))) : 0.0; + auto a2 = A2.has_value() ? std::pow(t, 3) / (3 * std::pow(A2.value(), 3)) : 0.0; + auto a3 = A3.has_value() ? A3.value() * std::pow(t, 4) / (4 * fabs(std::pow(A3.value(), 5))) : 0.0; + auto a4 = A4.has_value() ? std::pow(t, 5) / (5 * std::pow(A4.value(), 5)) : 0.0; + auto a5 = A5.has_value() ? A5.value() * std::pow(t, 6) / (6 * fabs(std::pow(A5.value(), 7))) : 0.0; + auto a6 = A6.has_value() ? std::pow(t, 7) / (7 * std::pow(A6.value(), 7)) : 0.0; + auto a7 = A7 * std::pow(t, 8) / (8 * fabs(std::pow(A7, 9))); + return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7; + }; + + auto fn_x = [theta](double t) -> double { return cos(theta(t)); }; + auto fn_y = [theta](double t) -> double { return sin(theta(t)); }; + + double s = 100.0; // @todo: rb - this is supposed to be the curve length when the parametric value u = 1.0 + Logger::Warning(std::string("IfcSeventhOrderPolynomialSpiral - the implementation has a bug")); + set_spiral_function(mapping_, c, s, fn_x, fn_y); + } +#endif + void operator()(const IfcSchema::IfcCircle* c) { auto R = c->Radius() * length_unit_; @@ -714,7 +777,7 @@ class curve_segment_evaluator { std::array*, 2> coefficients{&coeffX, &coeffY}; std::array position{0.0, 0.0}; // = SUM(coeff*u^pos) std::array slope{0.0, 0.0}; // slope is derivative of the curve = SUM( coeff*pos*u^(pos-1) ) - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { // loop over X and Y auto length_conversion = length_unit; auto begin = coefficients[i]->cbegin(); auto end = coefficients[i]->cend(); 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..a9c5b88161 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; } @@ -753,6 +759,7 @@ typedef item const* ptr; DECLARE_PTR(loop) boost::optional external, closed; + boost::optional pwf; bool is_polyhedron() const { for (auto& e : children) { @@ -1096,6 +1103,70 @@ 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) { + if (loop->pwf.is_initialized()) { + pwf_ = loop->pwf; + } else { + 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); + } + loop->pwf = pwf_; + } + } + } + + operator bool() const { + return pwf_.is_initialized(); + } + + operator taxonomy::piecewise_function::ptr() const { + return *pwf_; + } + }; #ifdef TAXONOMY_USE_SHARED_PTR template @@ -1104,6 +1175,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 +1187,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 +1201,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 +1213,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 +1227,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 +1239,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