/******************************************************************************** * * * 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 "alignment_helper.h" #include // @todo use std::numbers::pi when upgrading to C++ 20 static const double PI = boost::math::constants::pi(); #include // sets the segment name like ("H1" for horizontal, "V1" for vertical, "C1" for cant) void _name_segments(const char* prefix, std::vector& 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(hierarchy_helper& file, Ifc4x3_add2::IfcLocalPlacement global_placement, Ifc4x3_add2::IfcGeometricRepresentationSubContext segment_axis_subcontext, std::vector& curve_segments, std::vector& 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(); auto axis_representation = file.create(); axis_representation.setContextOfItems(segment_axis_subcontext); axis_representation.setRepresentationIdentifier("Axis"); axis_representation.setRepresentationType("Segment"); axis_representation.setItems(std::vector{curve_segment}); auto product = file.create(); product.setRepresentations(std::vector{axis_representation}); 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, std::vector, Ifc4x3_add2::IfcCompositeCurve> _createHorizontalAlignment(hierarchy_helper& file, const std::vector>& points, const std::vector& radii,bool include_geometry) { std::vector horizontal_segments; // business logic std::vector horizontal_curve_segments; // 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 = file.create(); design_parameters.setStartPoint(pt); design_parameters.setStartDirection(angleBT); design_parameters.setStartRadiusOfCurvature(0.0); design_parameters.setEndRadiusOfCurvature(0.0); design_parameters.setSegmentLength(tangent_run); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); horizontal_segments.push_back(alignment_segment); if (include_geometry) { horizontal_curve_segments.push_back(mapAlignmentHorizontalSegment(file, design_parameters).first); } } // create circular curve { auto pc = file.addDoublet(xPC, yPC); auto design_parameters = file.create(); design_parameters.setStartPoint(pc); design_parameters.setStartDirection(angleBT); design_parameters.setStartRadiusOfCurvature(radius); design_parameters.setEndRadiusOfCurvature(radius); design_parameters.setSegmentLength(lc); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); horizontal_segments.push_back(alignment_segment); if (include_geometry) { horizontal_curve_segments.push_back(mapAlignmentHorizontalSegment(file, 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 = file.create(); design_parameters.setStartPoint(pt); design_parameters.setStartDirection(angleBT); design_parameters.setStartRadiusOfCurvature(0); design_parameters.setEndRadiusOfCurvature(0); design_parameters.setSegmentLength(tangent_run); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); horizontal_segments.push_back(alignment_segment); if (include_geometry) { horizontal_curve_segments.push_back(mapAlignmentHorizontalSegment(file, design_parameters).first); } // create zero length terminator segment auto poe = file.addDoublet(xPI, yPI); design_parameters = file.create(); design_parameters.setStartPoint(poe); design_parameters.setStartDirection(angleBT); design_parameters.setStartRadiusOfCurvature(0); design_parameters.setEndRadiusOfCurvature(0); design_parameters.setSegmentLength(0.0); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE); alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); horizontal_segments.push_back(alignment_segment); if (include_geometry) { auto segment = mapAlignmentHorizontalSegment(file, design_parameters).first; segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); horizontal_curve_segments.push_back(segment); } Ifc4x3_add2::IfcCompositeCurve composite_curve; if (include_geometry) { composite_curve = file.create(); composite_curve.setSegments(horizontal_curve_segments); composite_curve.setSelfIntersect(false); } return {horizontal_segments, horizontal_curve_segments, composite_curve}; } Ifc4x3_add2::IfcAlignment addHorizontalAlignment(hierarchy_helper& 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 = file.create(); horizontal_alignment.setGlobalId(ifcopenshell::global_id()); horizontal_alignment.setName(alignment_name + " - Horizontal"); auto nests_horizontal_segments = file.create(); nests_horizontal_segments.setGlobalId(ifcopenshell::global_id()); nests_horizontal_segments.setName("Nests horizontal alignment segments with horizontal alignment"); nests_horizontal_segments.setRelatingObject(horizontal_alignment); nests_horizontal_segments.setRelatedObjects(horizontal_segments); // // Create geometric representation // Ifc4x3_add2::IfcLocalPlacement placement; Ifc4x3_add2::IfcProductDefinitionShape product_definition_shape; if (include_geometry) { // create the footprint representation auto axis_model_representation_subcontext = file.getRepresentationSubContext("Axis", "Model"); auto footprint_shape_representation = file.create(); footprint_shape_representation.setContextOfItems(axis_model_representation_subcontext); footprint_shape_representation.setRepresentationIdentifier("FootPrint"); footprint_shape_representation.setRepresentationType("Curve2D"); footprint_shape_representation.setItems(std::vector{composite_curve}); placement = file.addLocalPlacement(); // the alignment has a plan view footprint representation // create the alignment product definition product_definition_shape = file.create(); product_definition_shape.setName("Alignment Product Definition Shape"); product_definition_shape.setRepresentations(std::vector{footprint_shape_representation}); // create representations for each segment _createSegmentRepresentations(file, placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments); } // create the alignment auto alignment = file.create(); alignment.setGlobalId(ifcopenshell::global_id()); alignment.setName(alignment_name); alignment.setObjectPlacement(placement); alignment.setRepresentation(product_definition_shape); return alignment; } std::tuple, std::vector, Ifc4x3_add2::IfcGradientCurve> _createVerticalAlignment(hierarchy_helper& file, Ifc4x3_add2::IfcCompositeCurve composite_curve,const std::vector>& vpoints, const std::vector& vclengths, bool include_geometry) { std::vector vertical_segments; // business logic std::vector vertical_curve_segments; // 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 = file.create(); design_parameters.setStartDistAlong(xPBG); design_parameters.setHorizontalLength(gradient_length); design_parameters.setStartHeight(yPBG); design_parameters.setStartGradient(start_slope); design_parameters.setEndGradient(start_slope); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); vertical_segments.push_back(alignment_segment); if (include_geometry) { vertical_curve_segments.push_back(mapAlignmentVerticalSegment(file, 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 = file.create(); design_parameters.setStartDistAlong(xBVC); design_parameters.setHorizontalLength(length); design_parameters.setStartHeight(yBVC); design_parameters.setStartGradient(start_slope); design_parameters.setEndGradient(end_slope); design_parameters.setRadiusOfCurvature(1/k); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); vertical_segments.push_back(alignment_segment); if (include_geometry) { vertical_curve_segments.push_back(mapAlignmentVerticalSegment(file, 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 = file.create(); design_parameters.setStartDistAlong(xPBG); design_parameters.setHorizontalLength(gradient_length); design_parameters.setStartHeight(yPBG); design_parameters.setStartGradient(slope); design_parameters.setEndGradient(slope); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); auto alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); vertical_segments.push_back(alignment_segment); if (include_geometry) { vertical_curve_segments.push_back(mapAlignmentVerticalSegment(file, design_parameters).first); } // create zero length terminator segment design_parameters = file.create(); design_parameters.setStartDistAlong(xPVI); design_parameters.setHorizontalLength(0.); design_parameters.setStartHeight(yPVI); design_parameters.setStartGradient(slope); design_parameters.setEndGradient(slope); design_parameters.setPredefinedType(Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT); alignment_segment = file.create(); alignment_segment.setGlobalId(ifcopenshell::global_id()); alignment_segment.setDesignParameters(design_parameters); vertical_segments.push_back(alignment_segment); if (include_geometry) { auto segment = mapAlignmentVerticalSegment(file, design_parameters).first; segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS); vertical_curve_segments.push_back(segment); } Ifc4x3_add2::IfcGradientCurve gradient_curve; if (include_geometry) { gradient_curve = file.create(); gradient_curve.setSegments(vertical_curve_segments); gradient_curve.setSelfIntersect(false); gradient_curve.setBaseCurve(composite_curve); } return {vertical_segments, vertical_curve_segments, gradient_curve}; } Ifc4x3_add2::IfcAlignment addAlignment(hierarchy_helper& 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 = file.create(); horizontal_alignment.setGlobalId(ifcopenshell::global_id()); horizontal_alignment.setName(alignment_name + " - Horizontal"); auto nests_horizontal_segments = file.create(); nests_horizontal_segments.setGlobalId(ifcopenshell::global_id()); nests_horizontal_segments.setName("Nests horizontal alignment segments with horizontal alignment"); nests_horizontal_segments.setRelatingObject(horizontal_alignment); nests_horizontal_segments.setRelatedObjects(horizontal_segments); // // Create the vertical alignment (IfcAlignmentVertical) and nest the segments // auto vertical_profile = file.create(); vertical_profile.setGlobalId(ifcopenshell::global_id()); vertical_profile.setName(alignment_name + "- Vertical"); auto nests_vertical_segments = file.create(); nests_vertical_segments.setGlobalId(ifcopenshell::global_id()); nests_vertical_segments.setName("Nests vertical alignment segments with vertical alignment"); nests_vertical_segments.setRelatingObject(vertical_profile); nests_vertical_segments.setRelatedObjects(vertical_segments); Ifc4x3_add2::IfcLocalPlacement placement; Ifc4x3_add2::IfcProductDefinitionShape product_definition_shape; if (include_geometry) { auto axis_model_representation_subcontext = file.getRepresentationSubContext("Axis", "Model"); // create footprint representation auto footprint_shape_representation = file.create(); footprint_shape_representation.setContextOfItems(axis_model_representation_subcontext); footprint_shape_representation.setRepresentationIdentifier("FootPrint"); footprint_shape_representation.setRepresentationType("Curve2D"); footprint_shape_representation.setItems(std::vector{composite_curve}); // create the axis representation auto axis3d_shape_representation = file.create(); axis3d_shape_representation.setContextOfItems(axis_model_representation_subcontext); axis3d_shape_representation.setRepresentationIdentifier("Axis"); axis3d_shape_representation.setRepresentationType("Curve3D"); axis3d_shape_representation.setItems(std::vector{gradient_curve}); // 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 // create the alignment product definition product_definition_shape = file.create(); product_definition_shape.setName("Alignment Product Definition Shape"); product_definition_shape.setRepresentations(std::vector{footprint_shape_representation, axis3d_shape_representation}); } // // Create the IfcAlignment // auto alignment = file.create(); alignment.setGlobalId(ifcopenshell::global_id()); alignment.setName(alignment_name); alignment.setObjectPlacement(placement); alignment.setRepresentation(product_definition_shape); // 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 auto nests_alignment_layouts = file.create(); nests_alignment_layouts.setGlobalId(ifcopenshell::global_id()); nests_alignment_layouts.setName("Nest horizontal and vertical alignment layouts with the alignment"); nests_alignment_layouts.setRelatingObject(alignment); nests_alignment_layouts.setRelatedObjects(std::vector{horizontal_alignment, vertical_profile}); return alignment; } std::pair mapAlignmentSegment(hierarchy_helper& file, const Ifc4x3_add2::IfcAlignmentSegment& segment, logger& logger) { std::pair result; auto design_parameters = segment.DesignParameters(); auto horizontal = design_parameters.as(); auto vertical = design_parameters.as(); auto cant = design_parameters.as(); if (horizontal) { result = mapAlignmentHorizontalSegment(file, horizontal); } else if (vertical) { result = mapAlignmentVerticalSegment(file, vertical); } else if (cant) { result = mapAlignmentCantSegment(file, cant); } else { logger.error(std::string("Unexpected IfcAlignmentSegment subtype encountered")); } return result; } namespace { Ifc4x3_add2::IfcLengthMeasure create_length(hierarchy_helper& file, double d) { auto inst = file.create(); inst.set_attribute_value(0, d); return inst; } } std::pair mapAlignmentHorizontalSegment(hierarchy_helper& file, const Ifc4x3_add2::IfcAlignmentHorizontalSegment& segment, logger& logger) { std::pair result; 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) { auto parent_curve = file.create(); parent_curve.setPnt(file.addDoublet(0.0, 0.0)); auto vec = file.create(); vec.setOrientation(file.addDoublet(1.0, 0.0)); vec.setMagnitude(1.); parent_curve.setDir(vec); auto curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC) { auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setRadius(std::fabs(start_radius)); auto curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, length * start_radius / std::fabs(start_radius))); curve_segment.setParentCurve(parent_curve); result.first = curve_segment; } else if (type == Ifc4x3_add2::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CLOTHOID) { double A = length / sqrt(fabs(f)) * f / fabs(f); auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setClothoidConstant(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; } auto curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, offset)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(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; std::optional A0_optional, A1_optional, A2_optional; if (A0) { A0_optional = A0; } if (A1) { A1_optional = A1; } if (A2) { A2_optional = A2; } auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setCubicTerm(A3); parent_curve.setQuadraticTerm(A2_optional); parent_curve.setLinearTerm(A1_optional); parent_curve.setConstantTerm(A0_optional); Ifc4x3_add2::IfcCurveSegment curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(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 = std::optional(); if (A0) { A0_optional = A0; } auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setCosineTerm(A1); parent_curve.setConstantTerm(A0_optional); Ifc4x3_add2::IfcCurveSegment curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(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; } auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setCoefficientsX(std::vector{0.0, 1.0}); parent_curve.setCoefficientsY(std::vector{A0, A1, A2, A3}); Ifc4x3_add2::IfcCurveSegment curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, offset)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(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 = std::optional(); if (A0_1) { A0_1_optional = A0_1; } auto A1_1_optional = std::optional(); if (A1_1) { A1_1_optional = A1_1; } auto parent_curve1 = file.create(); parent_curve1.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve1.setQuadraticTerm(A2_1); parent_curve1.setLinearTerm(A1_1_optional); parent_curve1.setConstantTerm(A0_1_optional); Ifc4x3_add2::IfcCurveSegment curve_segment1 = file.create(); curve_segment1.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment1.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment1.setSegmentLength(create_length(file, 0.0)); curve_segment1.setSegmentLength(create_length(file, length / 2)); curve_segment1.setParentCurve(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 = std::optional(); if (A0_2) { A0_2_optional = A0_2; } auto A1_2_optional = std::optional(); if (A1_2) { A1_2_optional = A1_2; } Ifc4x3_add2::IfcCurve parent_curve2 = file.create(); parent_curve1.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve1.setQuadraticTerm(A2_2); parent_curve1.setLinearTerm(A1_2_optional); parent_curve1.setConstantTerm(A0_2_optional); Ifc4x3_add2::IfcCurveSegment curve_segment2 = file.create(); curve_segment2.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment2.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment2.setSegmentLength(create_length(file, length / 2)); curve_segment2.setSegmentLength(create_length(file, length / 2)); curve_segment2.setParentCurve(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 = std::optional(); if (A0) { A0_optional = A0; } auto A1_optional = std::optional(); if (A1) { A1_optional = A1; } auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setSineTerm(A2); parent_curve.setLinearTerm(A1_optional); parent_curve.setConstantTerm(A0_optional); Ifc4x3_add2::IfcCurveSegment curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_point.Coordinates()[0], start_point.Coordinates()[1], cos(start_direction), sin(start_direction))); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, length)); curve_segment.setParentCurve(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(hierarchy_helper& file, const Ifc4x3_add2::IfcAlignmentVerticalSegment& segment, logger& logger) { std::pair result; 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 = file.create(); parent_curve.setPnt(file.addDoublet(0, 0)); auto vec = file.create(); vec.setOrientation(file.addDoublet(1, 0)); vec.setMagnitude(1.0); parent_curve.setDir(vec); // 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 = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_distance_along, start_height, dx, dy)); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, segment_curve_length)); curve_segment.setParentCurve(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 = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setCoefficientsX(std::vector{0.0, 1.0}); parent_curve.setCoefficientsY(std::vector{A, B, C}); // 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 = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_distance_along, start_height, dx, dy)); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, segment_curve_length)); curve_segment.setParentCurve(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)); } auto parent_curve = file.create(); parent_curve.setPosition(file.addPlacement2d(0., 0., 1.0, 0.)); parent_curve.setRadius(radius); auto segment_curve_length = radius * fabs(end_angle - start_angle); Ifc4x3_add2::IfcCurveSegment curve_segment = file.create(); curve_segment.setTransition(Ifc4x3_add2::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT); curve_segment.setPlacement(file.addPlacement2d(start_distance_along, start_height, 1.0, 0.)); curve_segment.setSegmentLength(create_length(file, 0.0)); curve_segment.setSegmentLength(create_length(file, segment_curve_length)); curve_segment.setParentCurve(parent_curve); result.first = curve_segment; } else { logger.error(std::string("unexpected AlignmentVerticalSegmentType encountered")); } return result; } std::pair mapAlignmentCantSegment(hierarchy_helper& file, const Ifc4x3_add2::IfcAlignmentCantSegment& segment, logger& logger) { std::pair result; 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; }