From e30fd6096abfe3dc6ef4fd649a1f140100e4d13d Mon Sep 17 00:00:00 2001
From: Richard Brice <37087370+RickBrice@users.noreply.github.com>
Date: Thu, 11 Apr 2024 11:15:16 -0700
Subject: [PATCH] Adds framework for alignment helper functions.
Function include building a simple highway alignment and mapping of sementic segment definitions to geometric definitions (this part is still a work in progress)
---
src/ifcparse/IfcAlignmentHelper.cpp | 767 ++++++++++++++++++++++++++++
src/ifcparse/IfcAlignmentHelper.h | 59 +++
src/ifcparse/IfcHierarchyHelper.cpp | 23 +
src/ifcparse/IfcHierarchyHelper.h | 2 +
4 files changed, 851 insertions(+)
create mode 100644 src/ifcparse/IfcAlignmentHelper.cpp
create mode 100644 src/ifcparse/IfcAlignmentHelper.h
diff --git a/src/ifcparse/IfcAlignmentHelper.cpp b/src/ifcparse/IfcAlignmentHelper.cpp
new file mode 100644
index 0000000000..9913507f5a
--- /dev/null
+++ b/src/ifcparse/IfcAlignmentHelper.cpp
@@ -0,0 +1,767 @@
+/********************************************************************************
+ * *
+ * 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();
+
+
+#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();
+ auto [xBT, yBT] = *point_iter;
+
+ point_iter++;
+ auto [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) {
+ horizontal_curve_segments->push(mapAlignmentHorizontalSegment(design_parameters).first);
+ }
+
+ 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 two representations, a plan view footprint
+ 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();
+ auto [xPBG, yPBG] = *point_iter;
+
+ point_iter++;
+ auto [xPVI, yPVI] = *point_iter;
+
+ double xPFG, yPFG;
+ for (auto length : vclengths) {
+ // back gradient
+ auto dxBG = xPVI - xPBG;
+ auto dyBG = yPVI - yPBG;
+ auto start_slope = atan2(dyBG, dxBG);
+
+ // forward gradient
+ point_iter++;
+ std::tie(xPFG, yPFG) = *point_iter;
+ auto dxFG = xPFG - xPVI;
+ auto dyFG = yPFG - yPVI;
+ auto end_slope = atan2(dyFG, dxFG);
+
+ double xEVC = xPVI + length / 2;
+ double yEVC = yPVI + end_slope * length / 2;
+
+ // create gradient
+ {
+ file.addDoublet(xPBG, yPBG);
+ 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;
+
+ file.addDoublet(xBVC, yBVC);
+
+ 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 = atan2(dy, dx);
+ auto gradient_length = sqrt(dx * dx + dy * dy);
+ file.addDoublet(xPBG, yPBG);
+ 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
+ file.addDoublet(xPVI, yPVI);
+ 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) {
+ vertical_curve_segments->push(mapAlignmentVerticalSegment(design_parameters).first);
+ }
+
+ 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);
+
+ // create the 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);
+
+ // the gradient curve is a representation item
+ typename aggregate_of::ptr profile_representation_items(new aggregate_of());
+ profile_representation_items->push(gradient_curve);
+
+ // 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 two representations, a plan view footprint and a 3d curve
+ typename aggregate_of::ptr alignment_representations(new aggregate_of());
+ alignment_representations->push(footprint_shape_representation); // 2D footprint
+ 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));
+
+ 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({sqrt(1.0 - start_gradient * start_gradient), start_gradient})),
+ new Ifc4x3_add2::IfcLengthMeasure(0.0), // start
+ new Ifc4x3_add2::IfcLengthMeasure(horizontal_length),
+ parent_curve);
+
+ result.first = curve_segment;
+
+ } else if (type == Ifc4x3_add2::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC) {
+ double A = 0.0;
+ 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);
+
+ 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({1.0, 0.0})),
+ new Ifc4x3_add2::IfcLengthMeasure(0.0),
+ new Ifc4x3_add2::IfcLengthMeasure(horizontal_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);
+
+ 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(horizontal_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
diff --git a/src/ifcparse/IfcAlignmentHelper.h b/src/ifcparse/IfcAlignmentHelper.h
new file mode 100644
index 0000000000..444cedd3fd
--- /dev/null
+++ b/src/ifcparse/IfcAlignmentHelper.h
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * *
+ * 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 *
+ * *
+ ********************************************************************************/
+
+#ifndef IFCALIGNMENTHELPER_H
+#define IFCALIGNMENTHELPER_H
+
+#include "ifc_parse_api.h"
+#include "IfcLogger.h"
+
+#ifdef HAS_SCHEMA_4x3_add2
+#include "Ifc4x3_add2.h"
+#endif
+
+#include "IfcHierarchyHelper.h"
+
+#ifdef HAS_SCHEMA_4x3_add2
+//
+// alignment helper methods
+//
+
+// creates a horizontal alignment from a list of PI points and curve radii. if include_geometry is true, the geometric representations are created, otherwise only business logic is created
+IFC_PARSE_API Ifc4x3_add2::IfcAlignment* addHorizontalAlignment(IfcHierarchyHelper& file, const std::string& alignment_name, const std::vector>& points, const std::vector& radii,bool include_geometry = true);
+
+// creates a horizontal and vertical alignment from a list of PI points, curve radii, and VPI points and vertical curve lengths. if include_geometry is true, the geometric representations are created, otherwise only business logic is created
+IFC_PARSE_API 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& vclength, bool include_geometry = true);
+
+// Maps horizontal alignment business logic to geometry.
+// Bloss curves have two geometry elements for one horizontal alignment segment. That is the reason for returning a pair.
+// Typically the first element of the pair will have the geometry and the second element will be nullptr
+IFC_PARSE_API std::pair mapAlignmentSegment(const Ifc4x3_add2::IfcAlignmentSegment* segment);
+IFC_PARSE_API std::pair mapAlignmentHorizontalSegment(const Ifc4x3_add2::IfcAlignmentHorizontalSegment* segment);
+IFC_PARSE_API std::pair mapAlignmentVerticalSegment(const Ifc4x3_add2::IfcAlignmentVerticalSegment* segment);
+IFC_PARSE_API std::pair mapAlignmentCantSegment(const Ifc4x3_add2::IfcAlignmentCantSegment* segment);
+#endif
+
+
+#endif
\ No newline at end of file
diff --git a/src/ifcparse/IfcHierarchyHelper.cpp b/src/ifcparse/IfcHierarchyHelper.cpp
index 3e699d7bf6..377131b71c 100644
--- a/src/ifcparse/IfcHierarchyHelper.cpp
+++ b/src/ifcparse/IfcHierarchyHelper.cpp
@@ -1013,6 +1013,29 @@ typename Schema::IfcGeometricRepresentationContext* IfcHierarchyHelper::
return contexts_[s] = context;
}
+template
+typename Schema::IfcGeometricRepresentationSubContext* IfcHierarchyHelper::getRepresentationSubContext(const std::string& ident, const std::string& type) {
+ auto geometric_representation_context = getRepresentationContext(type); // creates the representation context if it doesn't already exist
+
+ // search for a subcontext that matches the ContextIdentifier
+ auto subcontexts = geometric_representation_context->HasSubContexts();
+ typename Schema::IfcGeometricRepresentationSubContext* rep_subcontext = nullptr;
+ for (auto subcontext : *subcontexts) {
+ if (subcontext->ContextIdentifier().get_value_or("") == ident) {
+ rep_subcontext = subcontext;
+ break; // found it, break out of the loop
+ }
+ }
+
+ if (rep_subcontext == nullptr) {
+ // didn't find the subcontext, create it
+ rep_subcontext = new typename Schema::IfcGeometricRepresentationSubContext(ident, type, geometric_representation_context, boost::none, Schema::IfcGeometricProjectionEnum::IfcGeometricProjection_MODEL_VIEW, boost::none);
+ addEntity(rep_subcontext);
+ }
+
+ return rep_subcontext;
+}
+
#ifdef HAS_SCHEMA_2x3
template IFC_PARSE_API class IfcHierarchyHelper;
#endif
diff --git a/src/ifcparse/IfcHierarchyHelper.h b/src/ifcparse/IfcHierarchyHelper.h
index f37dcc48ba..04db4d8dd3 100644
--- a/src/ifcparse/IfcHierarchyHelper.h
+++ b/src/ifcparse/IfcHierarchyHelper.h
@@ -517,6 +517,8 @@ class IFC_PARSE_API IfcHierarchyHelper : public IfcParse::IfcFile {
typename Schema::IfcGeometricRepresentationContext* getRepresentationContext(const std::string&);
+ typename Schema::IfcGeometricRepresentationSubContext* getRepresentationSubContext(const std::string& ident, const std::string& type);
+
private:
std::map contexts_;
};