/******************************************************************************** * * * 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 . * * * ********************************************************************************/ /******************************************************************************** * * * Implements convenience functions for alignments * * * ********************************************************************************/ #include "IfcAlignmentHelper.h" #include // @todo use std::numbers::pi when upgrading to C++ 20 static const double PI = boost::math::constants::pi(); #include #ifdef HAS_SCHEMA_4x3_add2 // sets the segment name like ("H1" for horizontal, "V1" for vertical, "C1" for cant) void _name_segments(const char* prefix, typename aggregate_of::ptr segments) { unsigned idx = 1; for (auto& segment : *segments) { std::ostringstream os; os << prefix << idx++; segment->setName(os.str()); } } // creates representations for each IfcAlignmentSegment per CT 4.1.7.1.1.4 // https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Segments/content.html void _createSegmentRepresentations(IfcHierarchyHelper& file, Ifc4x3_add2::IfcLocalPlacement* global_placement, Ifc4x3_add2::IfcGeometricRepresentationSubContext* segment_axis_subcontext, typename aggregate_of::ptr curve_segments, typename aggregate_of::ptr segments) { auto cs_iter = curve_segments->begin(); auto s_iter = segments->begin(); for (; cs_iter != curve_segments->end(); cs_iter++, s_iter++) { auto curve_segment = *cs_iter; auto alignment_segment = (*s_iter)->as(); typename aggregate_of::ptr representation_items(new aggregate_of()); representation_items->push(curve_segment); auto axis_representation = new Ifc4x3_add2::IfcShapeRepresentation(segment_axis_subcontext, std::string("Axis"), std::string("Segment"), representation_items); file.addEntity(axis_representation); typename aggregate_of::ptr representations(new aggregate_of()); representations->push(axis_representation); auto product = new Ifc4x3_add2::IfcProductDefinitionShape(boost::none, boost::none, representations); file.addEntity(product); alignment_segment->setObjectPlacement(global_placement); alignment_segment->setRepresentation(product); } } // creates a horizontal alignment using a vector of PI points and curve radii // returns a list of object definitions, curve segments, and a composite curve std::tuple::ptr, typename aggregate_of::ptr, Ifc4x3_add2::IfcCompositeCurve*> _createHorizontalAlignment(IfcHierarchyHelper& file, const std::vector>& points, const std::vector& radii,bool include_geometry) { typename aggregate_of::ptr horizontal_segments(new aggregate_of()); // business logic typename aggregate_of::ptr horizontal_curve_segments(include_geometry ? new aggregate_of() : nullptr); // geometry auto point_iter = points.begin(); double xBT, yBT, xPI, yPI; boost::tie(xBT, yBT) = *point_iter; point_iter++; boost::tie(xPI, yPI) = *point_iter; double xFT, yFT; for (auto radius : radii) { // back tangent auto dxBT = xPI - xBT; auto dyBT = yPI - yBT; auto angleBT = atan2(dyBT, dxBT); auto lengthBT = sqrt(dxBT * dxBT + dyBT * dyBT); // forward tangent point_iter++; std::tie(xFT, yFT) = *point_iter; auto dxFT = xFT - xPI; auto dyFT = yFT - yPI; auto angleFT = atan2(dyFT, dxFT); auto delta = angleFT - angleBT; auto tangent = fabs(radius * tan(delta / 2)); auto lc = fabs(radius * delta); radius *= delta / fabs(delta); auto xPC = xPI - tangent * cos(angleBT); auto yPC = yPI - tangent * sin(angleBT); auto xPT = xPI + tangent * cos(angleFT); auto yPT = yPI + tangent * sin(angleFT); auto tangent_run = lengthBT - tangent; // create back tangent run { auto pt = file.addDoublet(xBT, yBT); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pt, angleBT, 0.0, 0.0, tangent_run, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); } } // create circular curve { auto pc = file.addDoublet(xPC, yPC); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pc, angleBT, radius, radius, lc, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); } } xBT = xPT; yBT = yPT; xPI = xFT; yPI = yFT; } // create last tangent run auto dx = xPI - xBT; auto dy = yPI - yBT; auto angleBT = atan2(dy, dx); auto tangent_run = sqrt(dx * dx + dy * dy); auto pt = file.addDoublet(xBT, yBT); auto design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, pt, angleBT, 0.0, 0.0, tangent_run, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first); } // create zero length terminator segment auto poe = file.addDoublet(xPI, yPI); design_parameters = new Ifc4x3_add2::IfcAlignmentHorizontalSegment(boost::none, boost::none, poe, angleBT, 0.0, 0.0, 0.0, boost::none, Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); horizontal_segments->push(alignment_segment); if (include_geometry) { auto segment = mapAlignmentHorizontalSegment(design_parameters).first; segment->setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); horizontal_curve_segments->push(segment); } Ifc4x3_add2::IfcCompositeCurve* composite_curve = nullptr; if (include_geometry) { composite_curve = new Ifc4x3_add2::IfcCompositeCurve(horizontal_curve_segments, false /*not self-intersecting*/); file.addEntity(composite_curve); } return {horizontal_segments, horizontal_curve_segments, composite_curve}; } Ifc4x3_add2::IfcAlignment* addHorizontalAlignment(IfcHierarchyHelper& file, const std::string& alignment_name, const std::vector>& points, const std::vector& radii,bool include_geometry) { auto [horizontal_segments, horizontal_curve_segments, composite_curve] = _createHorizontalAlignment(file, points, radii, include_geometry); _name_segments("H", horizontal_segments); // // Create the horizontal alignment (IfcAlignmentHorizontal) and nest alignment segments // auto horizontal_alignment = new Ifc4x3_add2::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, alignment_name + std::string(" - Horizontal"), boost::none, boost::none, nullptr, nullptr); file.addEntity(horizontal_alignment); auto nests_horizontal_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments); file.addEntity(nests_horizontal_segments); // // Create geometric representation // Ifc4x3_add2::IfcLocalPlacement* placement = nullptr; Ifc4x3_add2::IfcProductDefinitionShape* product_definition_shape = nullptr; if (include_geometry) { typename aggregate_of::ptr alignment_representation_items(new aggregate_of()); alignment_representation_items->push(composite_curve); // create the footprint representation auto axis_model_representation_subcontext = file.getRepresentationSubContext("Axis", "Model"); auto footprint_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items); file.addEntity(footprint_shape_representation); placement = file.addLocalPlacement(); // the alignment has a plan view footprint representation typename aggregate_of::ptr alignment_representations(new aggregate_of()); alignment_representations->push(footprint_shape_representation); // 2D footprint // create the alignment product definition product_definition_shape = new Ifc4x3_add2::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations); // create representations for each segment _createSegmentRepresentations(file, placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments); } // create the alignment auto alignment = new Ifc4x3_add2::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, alignment_name, boost::none, boost::none, placement, product_definition_shape, boost::none); file.addEntity(alignment); return alignment; } std::tuple::ptr, typename aggregate_of::ptr, Ifc4x3_add2::IfcGradientCurve*> _createVerticalAlignment(IfcHierarchyHelper& file, Ifc4x3_add2::IfcCompositeCurve* composite_curve,const std::vector>& vpoints, const std::vector& vclengths, bool include_geometry) { typename aggregate_of::ptr vertical_segments(new aggregate_of()); // business logic typename aggregate_of::ptr vertical_curve_segments(new aggregate_of()); // geometry auto point_iter = vpoints.begin(); double xPBG, yPBG, xPVI, yPVI; boost::tie(xPBG, yPBG) = *point_iter; point_iter++; boost::tie(xPVI, yPVI) = *point_iter; double xPFG, yPFG; for (auto length : vclengths) { // back gradient auto dxBG = xPVI - xPBG; auto dyBG = yPVI - yPBG; auto start_slope = tan(atan2(dyBG,dxBG)); // forward gradient point_iter++; std::tie(xPFG, yPFG) = *point_iter; auto dxFG = xPFG - xPVI; auto dyFG = yPFG - yPVI; auto end_slope = tan(atan2(dyFG,dxFG)); double xEVC = xPVI + length / 2; double yEVC = yPVI + end_slope * length / 2; // create gradient { auto gradient_length = dxBG - length/2; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, start_slope, start_slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); } } // create vertical curve { double k = (end_slope - start_slope) / length; double xBVC = xPVI - length / 2; double yBVC = yPVI - start_slope * length / 2; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xBVC, length, yBVC, start_slope, end_slope, 1 / k, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); } } xPBG = xEVC; yPBG = yEVC; xPVI = xPFG; yPVI = yPFG; } // create last tangent run auto dx = xPVI - xPBG; auto dy = yPVI - yPBG; auto slope = tan(atan2(dy,dx)); auto gradient_length = dx; auto design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPBG, gradient_length, yPBG, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); auto alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first); } // create zero length terminator segment design_parameters = new Ifc4x3_add2::IfcAlignmentVerticalSegment(boost::none, boost::none, xPVI, 0.0, yPVI, slope, slope, boost::none, Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); alignment_segment = new Ifc4x3_add2::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters); vertical_segments->push(alignment_segment); if (include_geometry) { auto segment = mapAlignmentVerticalSegment(design_parameters).first; segment->setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); vertical_curve_segments->push(segment); } Ifc4x3_add2::IfcGradientCurve* gradient_curve = nullptr; if (include_geometry) { gradient_curve = new Ifc4x3_add2::IfcGradientCurve(vertical_curve_segments, false, composite_curve, nullptr); file.addEntity(gradient_curve); } return {vertical_segments, vertical_curve_segments, gradient_curve}; } Ifc4x3_add2::IfcAlignment* addAlignment(IfcHierarchyHelper& file, const std::string& alignment_name, const std::vector>& points, const std::vector& radii, const std::vector>& vpoints, const std::vector& vclengths,bool include_geometry) { auto [horizontal_segments, horizontal_curve_segments, composite_curve] = _createHorizontalAlignment(file, points, radii, include_geometry); auto [vertical_segments, vertical_curve_segments, gradient_curve] = _createVerticalAlignment(file, composite_curve, vpoints, vclengths, include_geometry); _name_segments("H", horizontal_segments); _name_segments("V", vertical_segments); // // Create the horizontal alignment (IfcAlignmentHorizontal) and nest the segments // auto horizontal_alignment = new Ifc4x3_add2::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, alignment_name + std::string(" - Horizontal"), boost::none, boost::none, nullptr, nullptr); file.addEntity(horizontal_alignment); auto nests_horizontal_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments); file.addEntity(nests_horizontal_segments); // // Create the vertical alignment (IfcAlignmentVertical) and nest the segments // auto vertical_profile = new Ifc4x3_add2::IfcAlignmentVertical(IfcParse::IfcGlobalId(), nullptr, alignment_name + std::string("- Vertical"), boost::none, boost::none, nullptr, nullptr); file.addEntity(vertical_profile); auto nests_vertical_segments = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments); file.addEntity(nests_vertical_segments); Ifc4x3_add2::IfcLocalPlacement* placement = nullptr; Ifc4x3_add2::IfcProductDefinitionShape* product_definition_shape = nullptr; if (include_geometry) { auto axis_model_representation_subcontext = file.getRepresentationSubContext("Axis", "Model"); // the composite curve is a representation item typename aggregate_of::ptr alignment_representation_items(new aggregate_of()); alignment_representation_items->push(composite_curve); // the gradient curve is a representation item typename aggregate_of::ptr profile_representation_items(new aggregate_of()); profile_representation_items->push(gradient_curve); // create footprint representation auto footprint_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items); file.addEntity(footprint_shape_representation); // create the axis representation auto axis3d_shape_representation = new Ifc4x3_add2::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("Axis"), std::string("Curve3D"), profile_representation_items); file.addEntity(axis3d_shape_representation); // create axis representations for each segment placement = file.addLocalPlacement(); _createSegmentRepresentations(file, placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments); _createSegmentRepresentations(file, placement, axis_model_representation_subcontext, vertical_curve_segments, vertical_segments); // the alignment has a 3d curve representation typename aggregate_of::ptr alignment_representations(new aggregate_of()); alignment_representations->push(footprint_shape_representation); // 2D curve alignment_representations->push(axis3d_shape_representation); // 3D curve // create the alignment product definition product_definition_shape = new Ifc4x3_add2::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations); } // // Create the IfcAlignment // auto alignment = new Ifc4x3_add2::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, alignment_name, boost::none, boost::none, placement, product_definition_shape, boost::none); file.addEntity(alignment); // Nest the IfcAlignmentHorizontal and IfcAlignmentVertical with the IfcAlignment to complete the business logic // 4.1.4.4.1 Alignments nest horizontal and vertical layouts // https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Nesting/Alignment_Layouts/content.html typename aggregate_of::ptr alignment_layout_list(new aggregate_of()); alignment_layout_list->push(horizontal_alignment); alignment_layout_list->push(vertical_profile); auto nests_alignment_layouts = new Ifc4x3_add2::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list); file.addEntity(nests_alignment_layouts); return alignment; } std::pair mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment) { std::pair result(nullptr, nullptr); auto design_parameters = segment->DesignParameters(); auto horizontal = design_parameters->as(); auto vertical = design_parameters->as(); auto cant = design_parameters->as(); if (horizontal) { result = mapAlignmentHorizontalSegment(horizontal); } else if (vertical) { result = mapAlignmentVerticalSegment(vertical); } else if (cant) { result = mapAlignmentCantSegment(cant); } else { Logger::Error(std::string("Unexpected IfcAlignmentSegment subtype encountered")); } return result; } std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment) { std::pair result(nullptr, nullptr); auto start_point = segment->StartPoint(); auto start_direction = segment->StartDirection(); auto start_radius = segment->StartRadiusOfCurvature(); auto end_radius = segment->EndRadiusOfCurvature(); auto length = segment->SegmentLength(); auto type = segment->PredefinedType(); double f = (end_radius ? length / end_radius : 0.0) - (start_radius ? length / start_radius : 0.0); if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE) { Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcLine( new Ifc4x3_add2::IfcCartesianPoint({0.0, 0.0}), new Ifc4x3_add2::IfcVector(new Ifc4x3_add2::IfcDirection({1.0, 0.0}), 1.0)); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC) { Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcCircle( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), fabs(start_radius)); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length * start_radius / fabs(start_radius)), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CLOTHOID) { double A = length / sqrt(fabs(f)) * f / fabs(f); Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcClothoid( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A); double offset; if ((fabs(start_radius) < fabs(end_radius) && start_radius) || end_radius == 0.) { offset = -length - (end_radius ? length * start_radius / (end_radius - start_radius) : 0); } else { offset = start_radius ? length * end_radius / (start_radius - end_radius) : 0; } Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(offset), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_BLOSSCURVE) { auto a0 = start_radius ? length / start_radius : 0.0; // constant term auto a1 = 0.0; // linear term auto a2 = 3 * f; // quadratic term auto a3 = -2 * f; // cubic term auto A0 = a0 ? length * pow(fabs(a0), -1. / 1.) * a0 / fabs(a0) : 0.0; auto A1 = a1 ? length * pow(fabs(a1), -1. / 2.) * a1 / fabs(a1) : 0.0; auto A2 = a2 ? length * pow(fabs(a2), -1. / 3.) * a2 / fabs(a2) : 0.0; auto A3 = a3 ? length * pow(fabs(a3), -1. / 4.) * a3 / fabs(a3) : 0.0; boost::optional A0_optional, A1_optional, A2_optional; if (A0) { A0_optional = A0; } if (A1) { A1_optional = A1; } if (A2) { A2_optional = A2; } Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcThirdOrderPolynomialSpiral( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A3, A2_optional, A1_optional, A0_optional); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_COSINECURVE) { auto a0 = 0.5 * f + (start_radius ? length / start_radius : 0.0); // constant term auto a1 = -0.5 * f; // cosine term auto A0 = a0 ? length * pow(fabs(a0), -1. / 1.) * a0 / fabs(a0) : 0.0; auto A1 = a1 ? length * pow(fabs(a1), -1. / 1.) * a1 / fabs(a1) : 0.0; auto A0_optional = boost::optional(); if (A0) { A0_optional = A0; } Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcCosineSpiral( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A1, A0_optional); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CUBIC) { double offset = 0; double A0 = 0; // constant term double A1 = 0; // linear term double A2 = 0; // quadratic term double A3 = 0; // cubic term if (end_radius && start_radius && end_radius != start_radius) { f = (start_radius - end_radius) / end_radius; A3 = f / (6. * start_radius * length); offset = length / f; } else if (end_radius) { A3 = 1. / (6. * end_radius * length); offset = 0.0; } else if (start_radius) { A3 = -1. / (6. * start_radius * length); offset = -length; } Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcPolynomialCurve( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), std::vector{0.0, 1.0}, std::vector{A0, A1, A2, A3}, boost::none ); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(offset), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_HELMERTCURVE) { auto a0_1 = start_radius ? length / start_radius : 0.0; // constant term, first half auto a1_1 = 0.0; // linear term, first half auto a2_1 = 2 * f; // quadratic term, first half auto A0_1 = a0_1 ? length * pow(fabs(a0_1), -1. / 1.) * a0_1 / fabs(a0_1) : 0.0; auto A1_1 = a1_1 ? length * pow(fabs(a1_1), -1. / 2.) * a1_1 / fabs(a1_1) : 0.0; auto A2_1 = a2_1 ? length * pow(fabs(a2_1), -1. / 3.) * a2_1 / fabs(a2_1) : 0.0; auto A0_1_optional = boost::optional(); if (A0_1) { A0_1_optional = A0_1; } auto A1_1_optional = boost::optional(); if (A1_1) { A1_1_optional = A1_1; } Ifc4x3_add2::IfcCurve* parent_curve1 = new Ifc4x3_add2::IfcSecondOrderPolynomialSpiral( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A2_1, A1_1_optional, A0_1_optional); Ifc4x3_add2::IfcCurveSegment* curve_segment1 = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length/2), parent_curve1); result.first = curve_segment1; auto a0_2 = -f + (start_radius ? length / start_radius : 0.0); // constant term, second half auto a1_2 = 4 * f; // linear term, second half auto a2_2 = -2 * f; // quadratic term, second half auto A0_2 = a0_2 ? length * pow(fabs(a0_2), -1. / 1.) * a0_2 / fabs(a0_2) : 0.0; auto A1_2 = a1_2 ? length * pow(fabs(a1_2), -1. / 2.) * a1_2 / fabs(a1_2) : 0.0; auto A2_2 = a2_2 ? length * pow(fabs(a2_2), -1. / 3.) * a2_2 / fabs(a2_2) : 0.0; auto A0_2_optional = boost::optional(); if (A0_2) { A0_2_optional = A0_2; } auto A1_2_optional = boost::optional(); if (A1_2) { A1_2_optional = A1_2; } Ifc4x3_add2::IfcCurve* parent_curve2 = new Ifc4x3_add2::IfcSecondOrderPolynomialSpiral( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A2_2, A1_2_optional, A0_2_optional); Ifc4x3_add2::IfcCurveSegment* curve_segment2 = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(length/2), new Ifc4x3_add2::IfcLengthMeasure(length/2), parent_curve2); result.second = curve_segment2; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_SINECURVE) { auto a0 = start_radius ? length / start_radius : 0.0; // constant term auto a1 = f; // linear term auto a2 = -f / (2 * PI); // sine term auto A0 = a0 ? length * pow(fabs(a0), -1. / 1.) * a0 / fabs(a0) : 0.0; auto A1 = a1 ? length * pow(fabs(a1), -1. / 2.) * a1 / fabs(a1) : 0.0; auto A2 = a2 ? length * pow(fabs(a2), -1. / 1.) * a2 / fabs(a2) : 0.0; auto A0_optional = boost::optional(); if (A0) { A0_optional = A0; } auto A1_optional = boost::optional(); if (A1) { A1_optional = A1; } Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcSineSpiral(new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), A2, A1_optional, A0_optional); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(start_point, new Ifc4x3_add2::IfcDirection({cos(start_direction), sin(start_direction)})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_VIENNESEBEND) { Logger::Warning(std::string("mapping of AlignmentHorizontalSegmentType VIENNESEBEND not supported")); } else { Logger::Error(std::string("unexpected AlignmentHorizontalSegmentType encountered")); } return result; } std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment) { std::pair result(nullptr, nullptr); auto start_distance_along = segment->StartDistAlong(); auto horizontal_length = segment->HorizontalLength(); auto start_height = segment->StartHeight(); auto start_gradient = segment->StartGradient(); auto end_gradient = segment->EndGradient(); auto radius_of_curvature = segment->RadiusOfCurvature(); auto type = segment->PredefinedType(); if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT) { auto parent_curve = new Ifc4x3_add2::IfcLine( new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcVector(new Ifc4x3_add2::IfcDirection(std::vector{1, 0}), 1.0)); // IfcCurveSegment.SegmentLength is the length of the curve segment, not the horizontal length. auto dx = cos(atan(start_gradient)); auto dy = sin(atan(start_gradient)); auto segment_curve_length = horizontal_length / dx; auto curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D( new Ifc4x3_add2::IfcCartesianPoint({start_distance_along, start_height}), new Ifc4x3_add2::IfcDirection({dx,dy})), new Ifc4x3_add2::IfcLengthMeasure(0.0), // start new Ifc4x3_add2::IfcLengthMeasure(segment_curve_length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC) { double A = start_height; double B = start_gradient; double C = (end_gradient - start_gradient) / (2 * horizontal_length); auto parent_curve = new Ifc4x3_add2::IfcPolynomialCurve( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector{0.0, 0.0}), new Ifc4x3_add2::IfcDirection(std::vector{1.0, 0.0})), std::vector{0.0, 1.0}, std::vector{A, B, C}, boost::none); // IfcCurveSegment.SegmentLength is the length of the curve segment, not the horizontal length. // The curve length is calculated by integrating the differential curve length equation sqrt(1 + (dy/dx)^2) from 0 to horizontal_length. // y = A + Bx + Cx^2 // dy/dx = B + 2Cx auto dx = cos(atan(start_gradient)); auto dy = sin(atan(start_gradient)); auto curve_length_fn = [B, C](double x) { return sqrt(1 + pow(B + 2*C * x, 2)); }; auto segment_curve_length = boost::math::quadrature::trapezoidal(curve_length_fn, 0.0, horizontal_length); auto curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D( new Ifc4x3_add2::IfcCartesianPoint({start_distance_along, start_height}), new Ifc4x3_add2::IfcDirection({dx,dy})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(segment_curve_length), parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CLOTHOID) { Logger::Warning(std::string("mapping of AlignmentVerticalSegmentType CLOTHOID not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CIRCULARARC) { auto start_angle = atan(start_gradient); auto end_angle = atan(end_gradient); double radius; if (start_angle < end_angle) { radius = horizontal_length / (sin(end_angle) - sin(start_angle)); } else { radius = horizontal_length / (sin(start_angle) - sin(end_angle)); } Ifc4x3_add2::IfcCurve* parent_curve = new Ifc4x3_add2::IfcCircle( new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint(std::vector({0, 0})), new Ifc4x3_add2::IfcDirection(std::vector{1, 0})), radius); auto segment_curve_length = radius * fabs(end_angle - start_angle); Ifc4x3_add2::IfcCurveSegment* curve_segment = new Ifc4x3_add2::IfcCurveSegment( Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, new Ifc4x3_add2::IfcAxis2Placement2D(new Ifc4x3_add2::IfcCartesianPoint({start_distance_along, start_height}), new Ifc4x3_add2::IfcDirection({1.0, 0.0})), new Ifc4x3_add2::IfcLengthMeasure(0.0), new Ifc4x3_add2::IfcLengthMeasure(segment_curve_length), parent_curve); result.first = curve_segment; } else { Logger::Error(std::string("unexpected AlignmentVerticalSegmentType encountered")); } return result; } std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment) { std::pair result(nullptr, nullptr); auto type = segment->PredefinedType(); if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_BLOSSCURVE) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType BLOSSCURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_CONSTANTCANT) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType CONSTANTCANT not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_COSINECURVE) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType COSINECURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_HELMERTCURVE) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType HELMERTCURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_LINEARTRANSITION) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType LINEARTRANSTION not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_SINECURVE) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType SINECURVE not supported")); } else if (type == Ifc4x3_add2::IfcAlignmentCantSegmentTypeEnum::IfcAlignmentCantSegmentType_VIENNESEBEND) { Logger::Warning(std::string("mapping of AlignmentCantSegmentType VIENNESEBEND not supported")); } else { Logger::Error(std::string("unexpected AlignmentCantSegmentType encountered")); } return result; } #endif