2023-09-16 22:23:04 +02:00
/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
# include "mapping.h"
# define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell : : geometry ;
# ifdef SCHEMA_HAS_IfcCurveSegment
# include "../profile_helper.h"
2023-10-02 16:30:39 -07:00
# include <boost/math/quadrature/trapezoidal.hpp>
2024-02-16 13:54:26 -08:00
# include <boost/math/tools/roots.hpp>
2024-05-04 16:03:36 -07:00
# include <boost/mpl/for_each.hpp>
# include <boost/mpl/vector.hpp>
# include <numeric>
2023-09-16 22:23:04 +02:00
2023-10-13 15:03:23 -07:00
namespace {
2023-10-20 13:29:33 -07:00
// @todo: rb is there a common math library these functions can be moved to?
2024-05-04 16:03:36 -07:00
auto sign = [ ] ( double v ) - > double { return v ? v / fabs ( v ) : 1.0 ; } ;
2023-10-27 08:01:46 -07:00
2023-11-08 19:42:41 -08:00
enum segment_type_t {
ST_HORIZONTAL ,
ST_VERTICAL ,
ST_CANT
} ;
2023-10-27 08:01:46 -07:00
// @todo use std::numbers::pi when upgrading to C++ 20
static const double PI = boost : : math : : constants : : pi < double > ( ) ;
2024-05-04 16:03:36 -07:00
double translate_to_length_measure ( const IfcSchema : : IfcCurve * crv , double param_value ) {
if ( std : : abs ( param_value ) < 1.e-7 ) {
return param_value ;
2024-05-04 18:07:09 -07:00
} else if ( auto line = crv - > as < IfcSchema : : IfcLine > ( ) ) {
return line - > Dir ( ) - > Magnitude ( ) * param_value ;
} else if ( auto clothoid = crv - > as < IfcSchema : : IfcClothoid > ( ) ) {
// param_value = 1.0, corresponds to tangent direction = PI/2
// param_value = (arc length)/fabs(A*PI)
return fabs ( clothoid - > ClothoidConstant ( ) * sqrt ( PI ) ) * param_value ;
2024-05-04 16:03:36 -07:00
} else if ( auto circ = crv - > as < IfcSchema : : IfcCircle > ( ) ) {
return circ - > Radius ( ) * param_value ;
2024-09-06 08:38:44 -07:00
} else if ( auto poly = crv - > as < IfcSchema : : IfcPolynomialCurve > ( ) ) {
2024-06-28 13:39:19 +02:00
return param_value ;
2024-05-04 16:03:36 -07:00
} else {
throw std : : runtime_error ( " Unsupported curve measure type " ) ;
2024-04-29 21:00:59 +02:00
}
}
2024-05-04 16:03:36 -07:00
double translate_if_param_value ( const IfcSchema : : IfcCurve * crv , IfcSchema : : IfcCurveMeasureSelect * val ) {
if ( auto param = val - > as < IfcSchema : : IfcParameterValue > ( ) ) {
// We don't care whether length- or positive length measure.
return translate_to_length_measure ( crv , * param ) ;
} else {
2024-08-23 20:29:07 +02:00
return val - > data ( ) . get_attribute_value ( 0 ) ;
2023-11-08 19:42:41 -08:00
}
2024-05-04 16:03:36 -07:00
}
2023-11-08 19:42:41 -08:00
2024-01-17 12:59:48 -08:00
// vector of parent curve types that are supported for IfcCurveSegment.ParentCurve
2023-09-16 22:23:04 +02:00
typedef boost : : mpl : : vector <
2024-05-04 16:03:36 -07:00
IfcSchema : : IfcLine
, IfcSchema : : IfcCircle
, IfcSchema : : IfcPolynomialCurve
2023-09-16 22:23:04 +02:00
# ifdef SCHEMA_HAS_IfcClothoid
2024-05-04 16:03:36 -07:00
, IfcSchema : : IfcClothoid
2024-02-26 16:34:41 -08:00
# endif
# if defined SCHEMA_HAS_IfcCosineSpiral
2024-05-04 16:03:36 -07:00
, IfcSchema : : IfcCosineSpiral
2024-02-26 16:34:41 -08:00
# endif
# if defined SCHEMA_HAS_IfcSineSpiral
, IfcSchema : : IfcSineSpiral
2023-09-28 11:51:26 -07:00
# endif
# if defined SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
2024-05-04 16:03:36 -07:00
, IfcSchema : : IfcSecondOrderPolynomialSpiral
2023-09-16 22:23:04 +02:00
# endif
2024-01-17 12:59:48 -08:00
# if defined SCHEMA_HAS_IfcThirdOrderPolynomialSpiral
, IfcSchema : : IfcThirdOrderPolynomialSpiral
# endif
# if defined SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral
, IfcSchema : : IfcSeventhOrderPolynomialSpiral
# endif
2023-09-16 22:23:04 +02:00
> curve_seg_types ;
2025-01-02 11:10:56 -08:00
struct parent_curve_function {
parent_curve_function ( ) = default ;
parent_curve_function ( const parent_curve_function & ) = default ;
parent_curve_function ( std : : function < Eigen : : Matrix4d ( double ) > fn ) : fn_ ( fn ) {
}
parent_curve_function & operator = ( std : : function < Eigen : : Matrix4d ( double ) > fn ) {
fn_ = fn ;
return * this ;
}
virtual Eigen : : Matrix4d operator ( ) ( double u ) const { return fn_ ( u ) ; }
private :
std : : function < Eigen : : Matrix4d ( double ) > fn_ ;
} ;
struct polynomial_parent_curve : public parent_curve_function {
using parent_curve_function : : parent_curve_function ;
} ;
struct line_parent_curve : public parent_curve_function {
using parent_curve_function : : parent_curve_function ;
} ;
struct circle_parent_curve : public parent_curve_function {
using parent_curve_function : : parent_curve_function ;
} ;
struct spiral_parent_curve : public parent_curve_function {
using parent_curve_function : : parent_curve_function ;
} ;
// this is the piecewise curve segment function for horizontal and vertical
struct curve_segment_function {
curve_segment_function ( const Eigen : : Matrix4d & curve_segment_placement , const Eigen : : Matrix4d & remove_parent_curve_rotation , const Eigen : : Matrix4d & remove_parent_curve_translation , std : : shared_ptr < parent_curve_function > parent_curve_fn ) :
curve_segment_placement_ ( curve_segment_placement ) ,
remove_parent_curve_rotation_ ( remove_parent_curve_rotation ) ,
remove_parent_curve_translation_ ( remove_parent_curve_translation ) ,
parent_curve_fn_ ( parent_curve_fn ) {
}
Eigen : : Matrix4d operator ( ) ( double u ) const {
Eigen : : Matrix4d parent_curve_point = ( * parent_curve_fn_ ) ( u ) ;
Eigen : : Matrix4d curve_segment_point = curve_segment_placement_ * remove_parent_curve_rotation_ * remove_parent_curve_translation_ * parent_curve_point ;
return curve_segment_point ;
}
private :
Eigen : : Matrix4d curve_segment_placement_ ;
Eigen : : Matrix4d remove_parent_curve_rotation_ ;
Eigen : : Matrix4d remove_parent_curve_translation_ ;
std : : shared_ptr < parent_curve_function > parent_curve_fn_ ;
} ;
// this is the piecewise curve segment function for cant
struct cant_curve_segment_function {
cant_curve_segment_function ( const Eigen : : Matrix4d & curve_segment_placement , const Eigen : : Matrix4d & parent_curve_start_point , std : : shared_ptr < parent_curve_function > parent_curve_fn ) :
curve_segment_placement_ ( curve_segment_placement ) ,
parent_curve_start_point_ ( parent_curve_start_point ) ,
parent_curve_fn_ ( parent_curve_fn ) {
}
Eigen : : Matrix4d operator ( ) ( double u ) const {
// The parent curve function returns the cant rotation and superelevation for the parent curve.
// Subtract the parent_curve_start_point to get the incremental cant rotation and superelevation
// Add the incremental cant rotation and superelevation to curve_segment_placement to get the curve_segment_point
Eigen : : Matrix4d parent_curve_point = ( * parent_curve_fn_ ) ( u ) ;
Eigen : : Matrix4d cant_increment = parent_curve_point - parent_curve_start_point_ ;
Eigen : : Matrix4d curve_segment_point = curve_segment_placement_ + cant_increment ;
return curve_segment_point ;
}
private :
Eigen : : Matrix4d curve_segment_placement_ ;
Eigen : : Matrix4d parent_curve_start_point_ ;
std : : shared_ptr < parent_curve_function > parent_curve_fn_ ;
} ;
// evaluates a IfcCurveSegment to set up the placement and parent curve function
2023-09-16 22:23:04 +02:00
class curve_segment_evaluator {
2023-10-25 06:46:34 -07:00
private :
2024-03-27 13:30:53 -07:00
mapping * mapping_ = nullptr ;
2024-05-04 16:03:36 -07:00
const IfcSchema : : IfcCurveSegment * inst_ = nullptr ; // this curve segment instance
2023-10-25 06:46:34 -07:00
double length_unit_ ;
double start_ ;
2024-03-27 13:30:53 -07:00
double length_ ; // length along the curve, as provided from the IfcCurveSegment
2023-10-25 06:46:34 -07:00
segment_type_t segment_type_ ;
2024-03-27 13:30:53 -07:00
const IfcSchema : : IfcCurve * parent_curve_ = nullptr ;
2023-10-27 08:01:46 -07:00
2024-03-27 13:30:53 -07:00
double projected_length_ ; // for vertical segments, this is the length of curve projected onto the "Distance Along" axis
2024-03-06 12:00:35 -08:00
2025-01-02 11:10:56 -08:00
std : : shared_ptr < parent_curve_function > parent_curve_fn_ ; // function for the parent curve. Function takes distances along, u, and returns the 4x4 position matrix
std : : optional < Eigen : : Matrix4d > parent_curve_start_point_ ; // placement matrix for the parent curve
2023-10-25 06:46:34 -07:00
2025-01-02 11:10:56 -08:00
std : : optional < Eigen : : Matrix4d > curve_segment_placement_ ; // placement of this segment
2024-07-17 15:46:03 -07:00
std : : optional < Eigen : : Matrix4d > next_segment_placement_ ; // placement of the next segment
2023-10-25 06:46:34 -07:00
public :
2025-01-02 11:10:56 -08:00
curve_segment_evaluator ( mapping * mapping , const IfcSchema : : IfcCurveSegment * inst , double length_unit )
2023-10-25 06:46:34 -07:00
: mapping_ ( mapping ) ,
2024-05-04 16:03:36 -07:00
inst_ ( inst ) ,
length_unit_ ( length_unit ) ,
2024-05-07 10:24:46 -07:00
parent_curve_ ( inst - > ParentCurve ( ) ) {
2025-01-02 11:10:56 -08:00
auto composite_curves = inst - > UsingCurves ( ) ;
// Find the next segment after inst
const IfcSchema : : IfcCurveSegment * next_inst = nullptr ;
if ( composite_curves ) {
if ( composite_curves - > size ( ) = = 1 ) {
auto segments = ( * composite_curves - > begin ( ) ) - > as < IfcSchema : : IfcCompositeCurve > ( ) - > Segments ( ) ;
bool emit_next = false ;
for ( auto & s : * segments ) {
if ( emit_next ) {
next_inst = s - > as < IfcSchema : : IfcCurveSegment > ( ) ;
break ;
}
if ( s = = inst ) {
emit_next = true ;
}
}
} else {
Logger : : Warning ( " IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the next segment. " ) ;
}
}
bool is_horizontal = false ;
bool is_vertical = false ;
bool is_cant = false ;
if ( composite_curves ) {
for ( auto & cc : * composite_curves ) {
if ( cc - > as < IfcSchema : : IfcSegmentedReferenceCurve > ( ) ) {
is_cant = true ;
} else if ( cc - > as < IfcSchema : : IfcGradientCurve > ( ) ) {
is_vertical = true ;
} else {
is_horizontal = true ;
}
}
}
if ( ( is_horizontal + is_vertical + is_cant ) ! = 1 ) {
// We have to choose the correct functor based on usage. We can't
// support multiple, because we don't know the caller at this point.
Logger : : Error ( std : : runtime_error ( " multiple uses of IfcSegmentCurve not supported " ) , inst_ ) ;
}
segment_type_ = is_horizontal ? ST_HORIZONTAL : is_vertical ? ST_VERTICAL : ST_CANT ;
2024-04-29 21:02:31 +02:00
start_ = translate_if_param_value ( inst - > ParentCurve ( ) , inst - > SegmentStart ( ) ) * length_unit ;
length_ = translate_if_param_value ( inst - > ParentCurve ( ) , inst - > SegmentLength ( ) ) * length_unit ;
2024-07-17 15:46:03 -07:00
if ( inst ) {
2025-01-02 11:10:56 -08:00
curve_segment_placement_ = taxonomy : : cast < taxonomy : : matrix4 > ( mapping_ - > map ( inst - > Placement ( ) ) ) - > ccomponents ( ) ;
2024-07-17 15:46:03 -07:00
}
if ( next_inst ) {
next_segment_placement_ = taxonomy : : cast < taxonomy : : matrix4 > ( mapping_ - > map ( next_inst - > Placement ( ) ) ) - > ccomponents ( ) ;
2024-09-06 08:38:44 -07:00
} else {
// there is not a next segment, however IfcGradientCurve and IfcSegmentReferenceCurve have an
// optional EndPoint which services the same purpose as the zero-length last segment.
IfcSchema : : IfcPlacement * end_point = nullptr ;
if ( composite_curves - > size ( ) = = 1 ) {
auto & cc = * ( composite_curves ) - > begin ( ) ;
if ( segment_type_ = = ST_VERTICAL ) {
auto gradient_curve = cc - > as < IfcSchema : : IfcGradientCurve > ( ) ;
end_point = gradient_curve - > EndPoint ( ) ;
} else if ( segment_type_ = = ST_CANT ) {
auto segmented_reference_curve = cc - > as < IfcSchema : : IfcSegmentedReferenceCurve > ( ) ;
end_point = segmented_reference_curve - > EndPoint ( ) ;
}
} else {
Logger : : Warning ( " IfcCurveSegment belongs to multiple IfcCompositeCurve instances. Cannot determine the end point. " ) ;
}
if ( end_point ) {
next_segment_placement_ = taxonomy : : cast < taxonomy : : matrix4 > ( mapping_ - > map ( end_point ) ) - > ccomponents ( ) ;
}
2024-07-17 15:46:03 -07:00
}
2023-10-27 08:01:46 -07:00
}
2024-05-04 16:03:36 -07:00
// Take the boost::type value from mpl::for_each and test it against our curve instance
template < typename T >
void operator ( ) ( boost : : type < T > ) {
if ( parent_curve_ - > as < T > ( ) ) {
( * this ) ( parent_curve_ - > as < T > ( ) ) ;
2023-10-27 08:01:46 -07:00
}
2023-10-25 06:46:34 -07:00
}
2024-05-04 16:03:36 -07:00
double length ( ) const {
return ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_CANT ) ? length_ : projected_length_ ;
}
2025-01-02 11:10:56 -08:00
taxonomy : : ptr get_segment_curve_function ( ) {
if ( ! parent_curve_fn_ | | ! parent_curve_start_point_ ) {
Logger : : Error ( std : : runtime_error ( inst_ - > ParentCurve ( ) - > declaration ( ) . name ( ) + " not implemented " ) , inst_ ) ;
}
2024-05-04 16:03:36 -07:00
2025-01-02 11:10:56 -08:00
auto length = fabs ( this - > length ( ) ) ;
2024-05-04 16:03:36 -07:00
2025-01-02 11:10:56 -08:00
if ( segment_type_ = = ST_CANT ) {
auto fn = cant_curve_segment_function ( * curve_segment_placement_ , * parent_curve_start_point_ , parent_curve_fn_ ) ;
return taxonomy : : make < taxonomy : : functor_item > ( length , fn ) ;
} else {
// The parent curve function returns the 4x4 matrix for the parent curve.
// Subtract the parent curve start point (remove the translation and rotation)
// to get the incremental translation and rotation. Apply the incremental
// translation and rotation to the curve_segment_placement to get the curve_segment_point
// Do a negative translation of the parent curve point relative to the start of the parent curve.
// This moves parent_curve_fn(u=0.0) to coordinate (0,0).
// This is done so the curve_segment_placement is applied relative to (0,0)
Eigen : : Matrix4d remove_parent_curve_translation = Eigen : : Matrix4d : : Identity ( ) ;
remove_parent_curve_translation . col ( 3 ) = - 1.0 * ( * parent_curve_start_point_ ) . col ( 3 ) ;
remove_parent_curve_translation ( 3 , 3 ) = 1.0 ;
// Do a rotation so that the tangent of the parent curve is in the direction (1,0)
// Example: if the parent curve IfcLine is at a 30 degree clockwise angle, this does
// a 30 degree counter-clockwise rotation
// Clockwise rotation matrix = [cos(angle) -sin(angle)]
// [sin(angle) cos(angle)]
//
// Counter-clockwise rotation = [ cos(angle) sin(angle)]
// [-sin(angle) cos(angle)]
//
// That's just a sign flip in positions (0,1) and (1,0)
Eigen : : Matrix4d remove_parent_curve_rotation = ( * parent_curve_start_point_ ) ;
remove_parent_curve_rotation ( 0 , 1 ) * = - 1.0 ;
remove_parent_curve_rotation ( 1 , 0 ) * = - 1.0 ;
remove_parent_curve_rotation . col ( 3 ) = Eigen : : Vector4d ( 0 , 0 , 0 , 1 ) ; // remove the parent curve placement point
auto fn = curve_segment_function ( * curve_segment_placement_ , remove_parent_curve_rotation , remove_parent_curve_translation , parent_curve_fn_ ) ;
return taxonomy : : make < taxonomy : : functor_item > ( length , fn ) ;
}
2024-07-17 15:46:03 -07:00
}
2024-05-04 16:03:36 -07:00
void set_spiral_function ( double s , std : : function < double ( double ) > fnX , std : : function < double ( double ) > fnY ) {
if ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_VERTICAL ) {
2024-03-06 12:00:35 -08:00
projected_length_ = length_ ;
2024-04-06 19:02:18 -07:00
2024-04-11 11:13:02 -07:00
// start of trimmed curve
2024-04-06 19:02:18 -07:00
double pcStartX = 0.0 , pcStartY = 0.0 ;
2024-04-09 16:46:43 -07:00
double pcStartDx = 1.0 , pcStartDy = 0.0 ;
2024-05-04 16:03:36 -07:00
if ( start_ ) {
// the spiral doesn't start at the inflection point
// compute the point where it starts
pcStartX = boost : : math : : quadrature : : trapezoidal ( fnX , 0.0 , start_ / s ) ;
pcStartY = boost : : math : : quadrature : : trapezoidal ( fnY , 0.0 , start_ / s ) ;
2024-04-09 16:46:43 -07:00
// compute the slope of the spiral at the start point
2024-05-04 16:03:36 -07:00
pcStartDx = s ? fnX ( start_ / s ) / s : 1.0 ;
pcStartDy = s ? fnY ( start_ / s ) / s : 0.0 ;
2024-04-06 19:02:18 -07:00
}
2024-05-04 16:03:36 -07:00
Eigen : : Matrix4d p = Eigen : : Matrix4d : : Identity ( ) ;
p . col ( 0 ) = Eigen : : Vector4d ( pcStartDx , pcStartDy , 0 , 0 ) ;
p . col ( 1 ) = Eigen : : Vector4d ( - pcStartDy , pcStartDx , 0 , 0 ) ;
p . col ( 3 ) = Eigen : : Vector4d ( pcStartX , pcStartY , 0 , 1 ) ;
2024-05-22 13:51:55 -07:00
parent_curve_start_point_ = p ;
2024-05-04 16:03:36 -07:00
std : : function < double ( double ) > convert_u ;
if ( segment_type_ = = ST_HORIZONTAL )
{
convert_u = [ ] ( double u ) - > double { return u ; } ;
} else {
// This functor is f'(x) = dy/dx
auto df = [ fnX , fnY ] ( double t ) - > double {
2024-05-20 13:17:18 -07:00
auto dy = fnY ( t ) ;
auto dx = fnX ( t ) ;
return dx ? dy / dx : 0.0 ;
2024-05-04 16:03:36 -07:00
} ;
// This functor computes the curve length
// Integral (sqrt (f'(x) ^ 2 + 1)dx
convert_u = [ df ] ( double x ) - > double {
auto fs = [ df ] ( double x ) - > double {
return sqrt ( pow ( df ( x ) , 2 ) + 1 ) ;
} ;
auto s = boost : : math : : quadrature : : trapezoidal ( fs , 0.0 , x ) ;
return s ;
} ;
}
2023-10-25 06:46:34 -07:00
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < spiral_parent_curve > ( [ start = start_ , s , convert_u , fnX , fnY ] ( double u ) {
2024-05-04 16:03:36 -07:00
u = convert_u ( u + start ) ;
2023-10-25 06:46:34 -07:00
// integration limits, integrate from a to b
2024-02-26 16:34:41 -08:00
auto b = s ? u / s : 0.0 ;
2023-10-25 06:46:34 -07:00
2024-04-06 19:02:18 -07:00
// point on parent curve
2024-04-11 11:13:02 -07:00
auto x = boost : : math : : quadrature : : trapezoidal ( fnX , 0.0 , b ) ;
auto y = boost : : math : : quadrature : : trapezoidal ( fnY , 0.0 , b ) ;
2024-04-06 19:02:18 -07:00
auto dx = s ? fnX ( b ) / s : 1.0 ;
auto dy = s ? fnY ( b ) / s : 0.0 ;
2023-10-20 13:29:33 -07:00
2024-04-09 16:46:43 -07:00
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
2024-03-06 12:00:35 -08:00
m . col ( 0 ) = Eigen : : Vector4d ( dx , dy , 0 , 0 ) ;
m . col ( 1 ) = Eigen : : Vector4d ( - dy , dx , 0 , 0 ) ;
2024-05-04 16:03:36 -07:00
m . col ( 3 ) = Eigen : : Vector4d ( x , y , 0 , 1 ) ;
2024-03-06 12:00:35 -08:00
return m ;
2025-01-02 11:10:56 -08:00
} ) ;
2024-03-27 13:30:53 -07:00
} else if ( segment_type_ = = ST_CANT ) {
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered - cant is handled in set_cant_spiral_function - should never get here " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
} else {
2023-10-25 06:46:34 -07:00
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-03-27 13:30:53 -07:00
}
}
2024-05-04 16:03:36 -07:00
// defines the parent_curve_fn_ functor for cant segments.
2024-09-06 08:38:44 -07:00
void set_cant_spiral_function ( std : : function < double ( double ) > Superelevation , std : : function < double ( double ) > SuperelevationSlope , std : : function < double ( double ) > Cant ) {
2025-01-02 11:10:56 -08:00
auto dy = ( * curve_segment_placement_ ) ( 1 , 2 ) ; // placement dy
auto dz = ( * curve_segment_placement_ ) ( 2 , 2 ) ; // placement dz
2024-09-06 08:38:44 -07:00
auto start_angle = atan2 ( dz , dy ) ;
dy = ( next_segment_placement_ . has_value ( ) ? ( * next_segment_placement_ ) ( 1 , 2 ) : 0.0 ) ;
dz = ( next_segment_placement_ . has_value ( ) ? ( * next_segment_placement_ ) ( 2 , 2 ) : 1.0 ) ;
auto end_angle = atan2 ( dz , dy ) ;
auto delta_angle = end_angle - start_angle ;
auto start_cant = Cant ( 0.0 /*start_*/ ) ;
auto end_cant = Cant ( /* start_ + */ length_ ) ;
auto delta_cant = end_cant - start_cant ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < spiral_parent_curve > ( [ start_angle , delta_angle , start_cant , delta_cant , Superelevation , SuperelevationSlope , Cant ] ( double u ) - > Eigen : : Matrix4d {
2024-09-06 08:38:44 -07:00
// departure of the curve segment from the base curve (superelevation)
auto super_elevation = Superelevation ( u ) ;
auto slope = SuperelevationSlope ( u ) ;
// direction along curve segment
auto angle = atan ( slope ) ;
auto dx = cos ( angle ) ;
auto dy = sin ( angle ) ;
Eigen : : Vector4d ref_dir ( dx , dy , 0.0 , 0.0 ) ;
// tilt angle in the plane of the cross section
auto cant = Cant ( u ) ;
auto tilt_angle = start_angle + delta_angle * ( cant - start_cant ) / delta_cant ;
Eigen : : Vector4d z ( 0.0 , cos ( tilt_angle ) , sin ( tilt_angle ) , 0.0 ) ;
// compute axis direction
Eigen : : Vector4d y = z . cross3 ( ref_dir ) ;
Eigen : : Vector4d axis = ref_dir . cross3 ( y ) ;
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
m . col ( 0 ) = ref_dir ;
m . col ( 1 ) = y ;
m . col ( 2 ) = axis ;
m . col ( 3 ) = Eigen : : Vector4d ( u , super_elevation , 0.0 , 1.0 ) ;
return m ;
2025-01-02 11:10:56 -08:00
} ) ;
2023-10-02 16:30:39 -07:00
2024-05-22 13:51:55 -07:00
parent_curve_start_point_ = ( * parent_curve_fn_ ) ( 0.0 ) ;
2024-03-27 13:30:53 -07:00
}
2023-10-02 16:30:39 -07:00
2024-09-06 08:38:44 -07:00
// returns function for super elevation and the slope of the super elevation curve if the super elevation is constant
// over the length of the segment. otherwise, no functions are returned because they are the same as the cant tilt angle
// functions.
std : : pair < boost : : optional < std : : function < double ( double ) > > , boost : : optional < std : : function < double ( double ) > > > get_superelevation_functions ( ) {
boost : : optional < std : : function < double ( double ) > > superelevation_fn ;
boost : : optional < std : : function < double ( double ) > > superelevation_slope_fn ;
2024-07-17 15:46:03 -07:00
2025-01-02 11:10:56 -08:00
if ( curve_segment_placement_ . has_value ( ) & & next_segment_placement_ . has_value ( ) ) {
double y1 = ( * curve_segment_placement_ ) ( 1 , 3 ) ;
2024-07-17 15:46:03 -07:00
double y2 = ( * next_segment_placement_ ) ( 1 , 3 ) ;
2024-09-06 08:38:44 -07:00
// if y2-y1 = 0, the super elevation is constant
// so we need a function that always returns the constant value
2024-07-17 15:46:03 -07:00
if ( ! ( y2 - y1 ) ) {
2024-09-06 08:38:44 -07:00
superelevation_fn = [ y1 ] ( double ) - > double { return y1 ; } ;
superelevation_slope_fn = [ ] ( double ) - > double { return 0.0 ; } ;
2024-07-17 15:46:03 -07:00
}
}
2024-09-06 08:38:44 -07:00
return std : : make_pair ( superelevation_fn , superelevation_slope_fn ) ;
2024-07-17 15:46:03 -07:00
}
2023-10-20 13:29:33 -07:00
# ifdef SCHEMA_HAS_IfcClothoid
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcClothoid * c ) {
2024-10-24 08:43:47 -07:00
auto A = c - > ClothoidConstant ( ) * length_unit_ ;
2024-10-31 08:55:01 -07:00
auto L = length ( ) ; // already includes length_unit_
2024-05-04 16:03:36 -07:00
if ( segment_type_ = = ST_CANT ) {
2024-09-06 08:38:44 -07:00
boost : : optional < std : : function < double ( double ) > > super , slope ;
std : : tie ( super , slope ) = get_superelevation_functions ( ) ;
2024-10-31 08:55:01 -07:00
auto cant = [ A , L ] ( double t ) - > double { return A ? L * A * t / fabs ( pow ( A , 3 ) ) : 0.0 ; } ;
2024-09-06 08:38:44 -07:00
if ( ! super . has_value ( ) ) {
super = cant ;
}
2024-07-17 15:46:03 -07:00
2024-09-06 08:38:44 -07:00
if ( ! slope . has_value ( ) ) {
2024-10-31 08:55:01 -07:00
slope = [ A , L ] ( double /*t*/ ) - > double { return A ? L * A / fabs ( pow ( A , 3 ) ) : 0.0 ; } ;
2024-07-17 15:46:03 -07:00
}
2024-09-06 08:38:44 -07:00
set_cant_spiral_function ( * super , * slope , cant ) ;
2024-05-04 16:03:36 -07:00
} else {
auto s = fabs ( A * sqrt ( PI ) ) ; // curve length when u = 1.0
auto fn_x = [ A , s ] ( double t ) - > double { return A ? s * cos ( PI * A * t * t / ( 2 * fabs ( A ) ) ) : 0.0 ; } ;
auto fn_y = [ A , s ] ( double t ) - > double { return A ? s * sin ( PI * A * t * t / ( 2 * fabs ( A ) ) ) : 0.0 ; } ;
set_spiral_function ( s , fn_x , fn_y ) ;
}
}
2023-10-20 13:29:33 -07:00
# endif
2024-02-26 16:34:41 -08:00
# if defined SCHEMA_HAS_IfcCosineSpiral
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcCosineSpiral * c ) {
auto constant_term = c - > ConstantTerm ( ) ;
2024-10-31 08:55:01 -07:00
if ( constant_term . has_value ( ) ) {
constant_term . value ( ) * = length_unit_ ;
}
auto cosine_term = c - > CosineTerm ( ) * length_unit_ ;
auto L = length ( ) ; // already converted to internal units by constructor
2024-05-04 16:03:36 -07:00
if ( segment_type_ = = ST_HORIZONTAL ) {
2024-10-31 08:55:01 -07:00
auto theta = [ constant_term , cosine_term , L ] ( double t ) - > double {
auto a0 = constant_term . has_value ( ) ? t / constant_term . value ( ) : 0.0 ;
auto a1 = ( L / PI ) * ( 1.0 / cosine_term ) * sin ( ( PI / L ) * t ) ;
2024-05-04 16:03:36 -07:00
return a0 + a1 ;
} ;
auto fn_x = [ theta ] ( double t ) - > double { return cos ( theta ( t ) ) ; } ;
auto fn_y = [ theta ] ( double t ) - > double { return sin ( theta ( t ) ) ; } ;
double s = 1.0 ;
set_spiral_function ( s , fn_x , fn_y ) ;
} else if ( segment_type_ = = ST_CANT ) {
2024-09-06 08:38:44 -07:00
boost : : optional < std : : function < double ( double ) > > super , slope ;
std : : tie ( super , slope ) = get_superelevation_functions ( ) ;
2024-10-31 08:55:01 -07:00
auto cant = [ constant_term , cosine_term , L ] ( double t ) - > double {
auto a0 = constant_term . has_value ( ) ? L / constant_term . value ( ) : 0.0 ;
auto a1 = ( L / cosine_term ) * cos ( PI * t / L ) ;
2024-09-06 08:38:44 -07:00
return a0 + a1 ;
} ;
if ( ! super . has_value ( ) ) {
super = cant ;
}
if ( ! slope . has_value ( ) ) {
2024-10-31 08:55:01 -07:00
slope = [ cosine_term , L ] ( double t ) - > double {
2024-11-04 15:00:27 -08:00
auto a1 = - ( PI / cosine_term ) * sin ( PI * t / L ) ;
2024-09-06 08:38:44 -07:00
return a1 ;
2024-07-17 15:46:03 -07:00
} ;
}
2024-09-06 08:38:44 -07:00
set_cant_spiral_function ( * super , * slope , cant ) ;
2024-05-04 16:03:36 -07:00
} else if ( segment_type_ = = ST_VERTICAL ) {
Logger : : Error ( std : : runtime_error ( " IfcCosineSpiral cannot be used for vertical alignment " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
} else {
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
}
}
2024-02-26 16:34:41 -08:00
# endif
2024-02-16 13:54:26 -08:00
2024-02-26 16:34:41 -08:00
# if defined SCHEMA_HAS_IfcSineSpiral
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcSineSpiral * c ) {
auto constant_term = c - > ConstantTerm ( ) ;
2024-10-31 08:55:01 -07:00
if ( constant_term . has_value ( ) ) {
constant_term . value ( ) * = length_unit_ ;
}
2024-05-04 16:03:36 -07:00
auto linear_term = c - > LinearTerm ( ) ;
2024-10-31 08:55:01 -07:00
if ( linear_term . has_value ( ) ) {
linear_term . value ( ) * = length_unit_ ;
}
auto sine_term = c - > SineTerm ( ) * length_unit_ ;
auto L = length ( ) ; // already converted to internal units by constructor
2024-05-04 16:03:36 -07:00
if ( segment_type_ = = ST_HORIZONTAL ) {
2024-10-31 08:55:01 -07:00
auto theta = [ constant_term , linear_term , sine_term , L ] ( double t ) - > double {
auto a0 = constant_term . has_value ( ) ? t / constant_term . value ( ) : 0.0 ;
auto a1 = linear_term . has_value ( ) ? sign ( linear_term . value ( ) ) * pow ( t / linear_term . value ( ) , 2.0 ) / 2.0 : 0.0 ;
auto a2 = - 1.0 * ( L / ( 2 * PI * sine_term ) ) * ( cos ( 2 * PI * t / L ) - 1.0 ) ;
2024-05-04 16:03:36 -07:00
return a0 + a1 + a2 ;
} ;
auto fn_x = [ theta ] ( double t ) - > double { return cos ( theta ( t ) ) ; } ;
auto fn_y = [ theta ] ( double t ) - > double { return sin ( theta ( t ) ) ; } ;
double s = 1.0 ;
set_spiral_function ( s , fn_x , fn_y ) ;
} else if ( segment_type_ = = ST_CANT ) {
2024-09-06 08:38:44 -07:00
boost : : optional < std : : function < double ( double ) > > super , slope ;
std : : tie ( super , slope ) = get_superelevation_functions ( ) ;
2024-10-31 08:55:01 -07:00
auto cant = [ constant_term , linear_term , sine_term , L ] ( double t ) - > double {
auto a0 = constant_term . has_value ( ) ? L / constant_term . value ( ) : 0.0 ;
auto a1 = linear_term . has_value ( ) ? sign ( linear_term . value ( ) ) * pow ( L / linear_term . value ( ) , 2.0 ) * ( t / L ) : 0.0 ;
auto a2 = ( L / sine_term ) * sin ( 2 * PI * t / L ) ;
2024-09-06 08:38:44 -07:00
return a0 + a1 + a2 ;
} ;
if ( ! super . has_value ( ) ) {
super = cant ;
}
if ( ! slope . has_value ( ) ) {
2024-10-31 08:55:01 -07:00
slope = [ linear_term , sine_term , L ] ( double t ) - > double {
auto a1 = linear_term . has_value ( ) ? sign ( linear_term . value ( ) ) * pow ( L / linear_term . value ( ) , 2.0 ) * ( 1.0 / L ) : 0.0 ;
2024-11-04 15:00:27 -08:00
auto a2 = ( 2 * PI / sine_term ) * cos ( 2 * PI * t / L ) ;
2024-09-06 08:38:44 -07:00
return a1 + a2 ;
2024-07-17 15:46:03 -07:00
} ;
}
2024-09-06 08:38:44 -07:00
set_cant_spiral_function ( * super , * slope , cant ) ;
2024-05-04 16:03:36 -07:00
} else if ( segment_type_ = = ST_VERTICAL ) {
Logger : : Error ( std : : runtime_error ( " IfcSineSpiral cannot be used for vertical alignment " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
} else {
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
}
}
2024-02-26 16:34:41 -08:00
# endif
2024-02-16 13:54:26 -08:00
2024-03-27 13:30:53 -07:00
void polynomial_spiral ( boost : : optional < double > A0 , boost : : optional < double > A1 , boost : : optional < double > A2 , boost : : optional < double > A3 , boost : : optional < double > A4 , boost : : optional < double > A5 , boost : : optional < double > A6 , boost : : optional < double > A7 ) {
2024-05-04 16:03:36 -07:00
auto theta = [ A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 , start = start_ * length_unit_ , lu = length_unit_ ] ( double t ) {
2024-03-27 13:30:53 -07:00
auto a0 = A0 . has_value ( ) ? t / ( A0 . value ( ) * lu ) : 0.0 ;
auto a1 = A1 . has_value ( ) ? A1 . value ( ) * lu * std : : pow ( t , 2 ) / ( 2 * fabs ( std : : pow ( A1 . value ( ) * lu , 3 ) ) ) : 0.0 ;
auto a2 = A2 . has_value ( ) ? std : : pow ( t , 3 ) / ( 3 * std : : pow ( A2 . value ( ) * lu , 3 ) ) : 0.0 ;
auto a3 = A3 . has_value ( ) ? A3 . value ( ) * lu * std : : pow ( t , 4 ) / ( 4 * fabs ( std : : pow ( A3 . value ( ) * lu , 5 ) ) ) : 0.0 ;
auto a4 = A4 . has_value ( ) ? std : : pow ( t , 5 ) / ( 5 * std : : pow ( A4 . value ( ) * lu , 5 ) ) : 0.0 ;
auto a5 = A5 . has_value ( ) ? A5 . value ( ) * lu * std : : pow ( t , 6 ) / ( 6 * fabs ( std : : pow ( A5 . value ( ) * lu , 7 ) ) ) : 0.0 ;
auto a6 = A6 . has_value ( ) ? std : : pow ( t , 7 ) / ( 7 * std : : pow ( A6 . value ( ) * lu , 7 ) ) : 0.0 ;
auto a7 = A7 . has_value ( ) ? A7 . value ( ) * lu * std : : pow ( t , 8 ) / ( 8 * fabs ( std : : pow ( A7 . value ( ) * lu , 9 ) ) ) : 0.0 ;
return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 ;
2024-05-04 16:03:36 -07:00
} ;
2024-02-16 13:54:26 -08:00
2024-05-04 16:03:36 -07:00
auto fn_x = [ theta ] ( double t ) - > double { return cos ( theta ( t ) ) ; } ;
auto fn_y = [ theta ] ( double t ) - > double { return sin ( theta ( t ) ) ; } ;
2024-02-16 13:54:26 -08:00
2024-05-04 16:03:36 -07:00
double s = 1.0 ;
set_spiral_function ( s , fn_x , fn_y ) ;
2024-03-27 13:30:53 -07:00
}
void polynomial_cant_spiral ( boost : : optional < double > A0 , boost : : optional < double > A1 , boost : : optional < double > A2 , boost : : optional < double > A3 , boost : : optional < double > A4 , boost : : optional < double > A5 , boost : : optional < double > A6 , boost : : optional < double > A7 ) {
2024-09-06 08:38:44 -07:00
boost : : optional < std : : function < double ( double ) > > super , slope ;
std : : tie ( super , slope ) = get_superelevation_functions ( ) ;
2024-03-27 13:30:53 -07:00
2024-10-31 08:55:01 -07:00
auto cant = [ A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 , start = start_ , L = length_ , lu = length_unit_ , length = length_ ] ( double t ) {
2024-03-27 13:30:53 -07:00
t + = start ;
2024-09-06 08:38:44 -07:00
auto a0 = A0 . has_value ( ) ? 1 / ( A0 . value ( ) * lu ) : 0.0 ;
auto a1 = A1 . has_value ( ) ? A1 . value ( ) * lu * t / fabs ( std : : pow ( A1 . value ( ) * lu , 3 ) ) : 0.0 ;
auto a2 = A2 . has_value ( ) ? std : : pow ( t , 2 ) / std : : pow ( A2 . value ( ) * lu , 3 ) : 0.0 ;
auto a3 = A3 . has_value ( ) ? A3 . value ( ) * lu * std : : pow ( t , 3 ) / fabs ( std : : pow ( A3 . value ( ) * lu , 5 ) ) : 0.0 ;
auto a4 = A4 . has_value ( ) ? std : : pow ( t , 4 ) / std : : pow ( A4 . value ( ) * lu , 5 ) : 0.0 ;
auto a5 = A5 . has_value ( ) ? A5 . value ( ) * lu * std : : pow ( t , 5 ) / fabs ( std : : pow ( A5 . value ( ) * lu , 7 ) ) : 0.0 ;
auto a6 = A6 . has_value ( ) ? std : : pow ( t , 6 ) / std : : pow ( A6 . value ( ) * lu , 7 ) : 0.0 ;
auto a7 = A7 . has_value ( ) ? A7 . value ( ) * lu * std : : pow ( t , 7 ) / fabs ( std : : pow ( A7 . value ( ) * lu , 9 ) ) : 0.0 ;
return L * ( a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 ) ;
2024-03-27 13:30:53 -07:00
} ;
2024-09-06 08:38:44 -07:00
if ( ! super . has_value ( ) ) {
super = cant ;
}
if ( ! slope . has_value ( ) ) {
2024-10-31 08:55:01 -07:00
slope = [ A1 , A2 , A3 , A4 , A5 , A6 , A7 , start = start_ , L = length_ , lu = length_unit_ , length = length_ ] ( double t ) {
2024-09-06 08:38:44 -07:00
t + = start ;
auto a1 = A1 . has_value ( ) ? A1 . value ( ) * lu / fabs ( std : : pow ( A1 . value ( ) * lu , 3 ) ) : 0.0 ;
auto a2 = A2 . has_value ( ) ? 2 * t / std : : pow ( A2 . value ( ) * lu , 3 ) : 0.0 ;
auto a3 = A3 . has_value ( ) ? 3 * A3 . value ( ) * lu * std : : pow ( t , 2 ) / fabs ( std : : pow ( A3 . value ( ) * lu , 5 ) ) : 0.0 ;
auto a4 = A4 . has_value ( ) ? 4 * std : : pow ( t , 3 ) / std : : pow ( A4 . value ( ) * lu , 5 ) : 0.0 ;
auto a5 = A5 . has_value ( ) ? 5 * A5 . value ( ) * lu * std : : pow ( t , 4 ) / fabs ( std : : pow ( A5 . value ( ) * lu , 7 ) ) : 0.0 ;
auto a6 = A6 . has_value ( ) ? 6 * std : : pow ( t , 5 ) / std : : pow ( A6 . value ( ) * lu , 7 ) : 0.0 ;
auto a7 = A7 . has_value ( ) ? 7 * A7 . value ( ) * lu * std : : pow ( t , 6 ) / fabs ( std : : pow ( A7 . value ( ) * lu , 9 ) ) : 0.0 ;
return L * ( a1 + a2 + a3 + a4 + a5 + a6 + a7 ) ;
} ;
}
set_cant_spiral_function ( * super , * slope , cant ) ;
2024-02-16 13:54:26 -08:00
}
2023-09-28 11:51:26 -07:00
# ifdef SCHEMA_HAS_IfcSecondOrderPolynomialSpiral
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcSecondOrderPolynomialSpiral * c ) {
auto A0 = c - > ConstantTerm ( ) ;
auto A1 = c - > LinearTerm ( ) ;
auto A2 = c - > QuadraticTerm ( ) ;
boost : : optional < double > A3 , A4 , A5 , A6 , A7 ;
2024-03-27 13:30:53 -07:00
2024-05-04 16:03:36 -07:00
if ( segment_type_ = = ST_CANT ) {
polynomial_cant_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
} else {
polynomial_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
}
}
2023-09-16 22:23:04 +02:00
# endif
2024-01-17 12:59:48 -08:00
# ifdef SCHEMA_HAS_IfcThirdOrderPolynomialSpiral
void operator ( ) ( const IfcSchema : : IfcThirdOrderPolynomialSpiral * c ) {
auto A0 = c - > ConstantTerm ( ) ;
auto A1 = c - > LinearTerm ( ) ;
auto A2 = c - > QuadraticTerm ( ) ;
auto A3 = c - > CubicTerm ( ) ;
2024-02-16 13:54:26 -08:00
boost : : optional < double > A4 , A5 , A6 , A7 ;
2024-03-27 13:30:53 -07:00
if ( segment_type_ = = ST_CANT ) {
polynomial_cant_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
} else {
polynomial_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
}
2024-01-17 12:59:48 -08:00
}
# endif
# ifdef SCHEMA_HAS_IfcSeventhOrderPolynomialSpiral
void operator ( ) ( const IfcSchema : : IfcSeventhOrderPolynomialSpiral * c ) {
auto A0 = c - > ConstantTerm ( ) ;
auto A1 = c - > LinearTerm ( ) ;
auto A2 = c - > QuadraticTerm ( ) ;
auto A3 = c - > CubicTerm ( ) ;
auto A4 = c - > QuarticTerm ( ) ;
auto A5 = c - > QuinticTerm ( ) ;
auto A6 = c - > SexticTerm ( ) ;
auto A7 = c - > SepticTerm ( ) ;
2024-03-27 13:30:53 -07:00
if ( segment_type_ = = ST_CANT ) {
polynomial_cant_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
} else {
polynomial_spiral ( A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 ) ;
}
2024-01-17 12:59:48 -08:00
}
# endif
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcCircle * c ) {
if ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_VERTICAL ) {
auto R = c - > Radius ( ) * length_unit_ ;
auto parent_curve_position = taxonomy : : cast < taxonomy : : matrix4 > ( mapping_ - > map ( c - > Position ( ) ) ) - > ccomponents ( ) ;
2024-04-06 19:02:18 -07:00
2024-05-04 16:03:36 -07:00
// center point of the parent curve
auto pcCenterX = parent_curve_position ( 0 , 3 ) ;
auto pcCenterY = parent_curve_position ( 1 , 3 ) ;
auto pcDx = parent_curve_position ( 0 , 0 ) ;
auto pcDy = parent_curve_position ( 1 , 0 ) ;
2024-04-06 19:02:18 -07:00
2024-05-04 16:03:36 -07:00
// angle from X = 0 to the parent curve X-axis
auto pc_axis_angle = atan2 ( pcDy , pcDx ) ;
// sweep angle from the parent curve X-axis to the first point on the trimmed curve
2024-05-22 13:51:55 -07:00
auto sweep_start_angle = R ? start_ / R : 0.0 ;
2024-05-04 16:03:36 -07:00
// angle from X = 0 to the first point on the trimmed curve
auto start_angle = pc_axis_angle + sweep_start_angle ;
2023-10-13 15:03:23 -07:00
2024-05-04 16:03:36 -07:00
auto sign_l = sign ( length_ ) ;
2023-09-22 13:52:48 -07:00
2024-05-04 16:03:36 -07:00
projected_length_ = length_ ;
2023-10-27 08:01:46 -07:00
2024-05-04 16:03:36 -07:00
std : : function < double ( double ) > convert_u ;
if ( segment_type_ = = ST_HORIZONTAL ) {
convert_u = [ ] ( double u ) { return u ; } ;
} else {
auto curve_segment_placement = taxonomy : : cast < taxonomy : : matrix4 > ( mapping_ - > map ( inst_ - > Placement ( ) ) ) - > ccomponents ( ) ;
auto csStartX = curve_segment_placement ( 0 , 3 ) ;
auto csStartY = curve_segment_placement ( 1 , 3 ) ;
auto csStartDx = curve_segment_placement ( 0 , 0 ) ;
auto csStartDy = curve_segment_placement ( 1 , 0 ) ;
auto csCenterX = csStartX - sign_l * csStartDy * R ;
auto csCenterY = csStartY + sign_l * csStartDx * R ;
convert_u = [ csStartX , csStartY , csCenterX , csCenterY , R , sign_l ] ( double u ) {
// for vertical, u is measured along the horizonal but we need it to be an arc length
// x and y are coordinates on the curve segment for horizontal distance u from the start point
// u is a horizontal distance so x = csStartX + u
// Recognizing the triangle
2024-05-22 13:51:55 -07:00
// R^2 = (u + csStartX - csCenterX)^2 + (y - csCenterY)^2
2024-05-04 16:03:36 -07:00
// solve for y
2024-05-22 13:51:55 -07:00
// (y - csCenterY) = sqrt( R^2 - (u + csStartX - csCenterX)^2 )
// y = csCenterY + sqrt( R^2 - (u + csStartX - csCenterX)^2 )
2024-05-04 16:03:36 -07:00
auto x = csStartX + u ;
2024-05-22 13:51:55 -07:00
auto y = csCenterY - sign_l * sqrt ( pow ( R , 2 ) - pow ( u + csStartX - csCenterX , 2 ) ) ;
2024-05-04 16:03:36 -07:00
// compute the chord distance between the start point and (x,y)
auto c = sqrt ( pow ( x - csStartX , 2.0 ) + pow ( y - csStartY , 2.0 ) ) ;
// compute the subtended angle
// c = 2R*sin(delta/2)
2024-05-22 13:51:55 -07:00
auto delta = R ? 2.0 * asin ( c / ( 2 * R ) ) : 0.0 ;
2024-05-04 16:03:36 -07:00
// compute the arc length (this will always be a positive value)
u = R * fabs ( delta ) ;
return u ;
} ;
}
2024-04-10 15:15:11 -07:00
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < circle_parent_curve > ( [ segment_type = segment_type_ , R , pcCenterX , pcCenterY , start_angle , sign_l , convert_u ] ( double u ) {
2024-05-04 16:03:36 -07:00
u = convert_u ( u ) ;
// u is measured along the circle
// angle from the X=0 axis to the current point
2024-05-22 13:51:55 -07:00
auto delta = R ? sign_l * u / R : 0.0 ;
2024-05-04 16:03:36 -07:00
auto sweep_angle = start_angle + delta ;
auto cos_sweep_angle = cos ( sweep_angle ) ;
auto sin_sweep_angle = sin ( sweep_angle ) ;
2024-02-26 16:34:41 -08:00
2024-05-04 16:03:36 -07:00
// point on the parent curve
auto pcX = R * cos_sweep_angle + pcCenterX ;
auto pcY = R * sin_sweep_angle + pcCenterY ;
auto pcDx = - sign_l * sin_sweep_angle ;
auto pcDy = sign_l * cos_sweep_angle ;
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
m . col ( 0 ) = Eigen : : Vector4d ( pcDx , pcDy , 0 , 0 ) ;
m . col ( 1 ) = Eigen : : Vector4d ( - pcDy , pcDx , 0 , 0 ) ;
m . col ( 3 ) = Eigen : : Vector4d ( pcX , pcY , 0.0 , 1.0 ) ;
return m ;
2025-01-02 11:10:56 -08:00
} ) ;
2024-05-04 16:03:36 -07:00
2024-05-22 13:51:55 -07:00
if ( segment_type_ = = ST_HORIZONTAL ) {
parent_curve_start_point_ = ( * parent_curve_fn_ ) ( start_ ) ;
} else {
// @todo - find a way to simplify this
// For vertical circle "u" is a horizontal measurement and it needs to be converted
// to a distance along the circle. However, when evaluating the start point,
// start_ is distance along. parent_curve_fn_ will call it's convert_u method
// which would be wrong in this case. For this reason, the start point of the parent curve
// is explicitly computed here. This code is a little redundant with parent_curve_fn_.
auto cos_start_angle = cos ( start_angle ) ;
auto sin_start_angle = sin ( start_angle ) ;
// point on the parent curve
auto pcStartX = R * cos_start_angle + pcCenterX ;
auto pcStartY = R * sin_start_angle + pcCenterY ;
auto pcStartDx = - sign_l * sin_start_angle ;
auto pcStartDy = sign_l * cos_start_angle ;
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
m . col ( 0 ) = Eigen : : Vector4d ( pcStartDx , pcStartDy , 0 , 0 ) ;
m . col ( 1 ) = Eigen : : Vector4d ( - pcStartDy , pcStartDx , 0 , 0 ) ;
m . col ( 3 ) = Eigen : : Vector4d ( pcStartX , pcStartY , 0.0 , 1.0 ) ;
parent_curve_start_point_ = m ;
}
2024-05-04 16:03:36 -07:00
} else if ( segment_type_ = = ST_CANT ) {
Logger : : Warning ( std : : runtime_error ( " Use of IfcCircle for cant is not supported " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
} else {
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
}
}
2024-05-04 16:14:15 -07:00
void operator ( ) ( const IfcSchema : : IfcLine * l ) {
2024-05-04 16:03:36 -07:00
projected_length_ = length_ ;
auto c = l - > Pnt ( ) - > Coordinates ( ) ;
auto pcX = c [ 0 ] * length_unit_ ;
auto pcY = c [ 1 ] * length_unit_ ;
// 8.9.3.75 IfcVector https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcVector.htm
// 8.9.3.30 IfcDirection https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcDirection.htm
// "The IfcDirection does not imply a vector length, and the direction ratios does not have to be normalized."
//
2024-05-22 13:51:55 -07:00
// Therefore, the direction ratios need to be normalized to compute points on the line.
//
// Magnitude is not used because it relates to the parameterization of the line, which isn't currently done for IfcCurveSegment
// @todo - parameterization was recently added so Magnitude needs to be taking into consideration
2024-05-04 16:03:36 -07:00
auto dr = l - > Dir ( ) - > Orientation ( ) - > DirectionRatios ( ) ;
// normalize the direction ratios
double m_squared = std : : inner_product ( dr . begin ( ) , dr . end ( ) , dr . begin ( ) , 0.0 ) ;
double m = sqrt ( m_squared ) ;
2024-09-28 06:18:56 -07:00
std : : transform ( dr . begin ( ) , dr . end ( ) , dr . begin ( ) , [ m ] ( auto & d ) { return d / m ; } ) ;
2024-05-04 16:03:36 -07:00
auto pcDx = dr [ 0 ] ;
auto pcDy = dr [ 1 ] ;
2025-01-02 11:10:56 -08:00
if ( segment_type_ = = ST_VERTICAL & & curve_segment_placement_ ) {
2024-10-20 08:19:49 -07:00
// the general algorithm for mapping parent curve onto curve segment doesn't
2024-10-24 08:43:47 -07:00
// exactly work for IfcLine. This is easily overcome by using the curve segment
2024-10-20 08:19:49 -07:00
// placement for the IfcLine direction
2025-01-02 11:10:56 -08:00
pcDx = ( * curve_segment_placement_ ) ( 0 , 0 ) ;
pcDy = ( * curve_segment_placement_ ) ( 1 , 0 ) ;
2024-10-20 08:19:49 -07:00
}
2024-05-08 10:28:01 -07:00
if ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_VERTICAL | | segment_type_ = = ST_CANT ) {
2024-05-04 16:03:36 -07:00
std : : function < double ( double ) > convert_u ;
2024-05-08 10:28:01 -07:00
if ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_CANT ) {
2024-05-04 16:03:36 -07:00
convert_u = [ ] ( double u ) { return u ; } ; // u is along curve
} else {
// u is along horizontal, convert to along curve
convert_u = [ pcDx ] ( double u ) { return u / pcDx ; } ;
}
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < line_parent_curve > ( [ pcX , pcY , pcDx , pcDy , convert_u ] ( double u ) {
2024-05-04 16:03:36 -07:00
u = convert_u ( u ) ;
auto x = pcX + pcDx * u ;
auto y = pcY + pcDy * u ;
2023-10-20 13:29:33 -07:00
2024-03-27 13:30:53 -07:00
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
2024-05-04 16:03:36 -07:00
m . col ( 0 ) = Eigen : : Vector4d ( pcDx , pcDy , 0 , 0 ) ;
m . col ( 1 ) = Eigen : : Vector4d ( - pcDy , pcDx , 0 , 0 ) ;
m . col ( 3 ) = Eigen : : Vector4d ( x , y , 0.0 , 1.0 ) ;
return m ;
2025-01-02 11:10:56 -08:00
} ) ;
2024-05-08 10:28:01 -07:00
2024-05-22 13:51:55 -07:00
parent_curve_start_point_ = ( * parent_curve_fn_ ) ( start_ ) ;
} else {
2024-05-04 16:03:36 -07:00
Logger : : Warning ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
}
2023-10-27 08:01:46 -07:00
}
2023-09-16 22:23:04 +02:00
2024-05-04 16:03:36 -07:00
void operator ( ) ( const IfcSchema : : IfcPolynomialCurve * pc ) {
// see https://forums.buildingsmart.org/t/ifcpolynomialcurve-clarification/4716 for discussion on IfcPolynomialCurve
auto coeffX = pc - > CoefficientsX ( ) . get_value_or ( std : : vector < double > ( ) ) ;
auto coeffY = pc - > CoefficientsY ( ) . get_value_or ( std : : vector < double > ( ) ) ;
auto coeffZ = pc - > CoefficientsZ ( ) . get_value_or ( std : : vector < double > ( ) ) ;
if ( ! coeffZ . empty ( ) ) {
Logger : : Warning ( " Expected IfcPolynomialCurve.CoefficientsZ to be undefined for alignment geometry. Coefficients ignored. " , pc ) ;
}
2024-03-06 12:00:35 -08:00
2024-05-04 16:03:36 -07:00
if ( segment_type_ = = ST_HORIZONTAL | | segment_type_ = = ST_VERTICAL ) {
projected_length_ = length_ ;
2024-03-06 12:00:35 -08:00
2025-01-02 11:10:56 -08:00
// There is one significant difference between IfcPolynomialCurve used for horizontal and vertical alignments.
2024-05-04 16:03:36 -07:00
// For horizontal alignment, u is the distance along the curve. For vertical alignment, u is the horizontal distance.
// From 4.2.2.2.8 the polynomial curve equation is in the form of y = Ax^3 for horizontal parabolic transition segments.
// To evaluate the horizontal function, the value of x that corresponds to the distance along the curve u is needed.
// This is what the convert_u functor does. For vertical curves, the convert_u functor simply returns x = u.
std : : function < double ( double ) > convert_u ;
if ( segment_type_ = = ST_HORIZONTAL ) {
// Distance along the curve is Integral[0,x] (sqrt(f'(x)^2 + 1) dx
// This functor is the derivative of y(x) => dy/dx = f'(x)
2024-12-02 14:44:46 -08:00
auto df = [ lu = length_unit_ , coeffY ] ( double x ) - > double {
2025-01-06 14:36:37 -08:00
auto begin = coeffY . begin ( ) ;
auto iter = std : : next ( begin ) ;
2024-05-04 16:03:36 -07:00
auto end = coeffY . end ( ) ;
double value = 0 ;
2025-01-06 14:36:37 -08:00
// y = A0 + A1*x + A2*x^2 + A3*x^3
// y' = 0 + 1*A1*x^0 + 2*A2*x^1 + 3*A3*x^2
// The units of y are length. y' is unitless
// The units of x are length.
// A unit conversion of the coefficients is needed
// A1 = length^0
// A2 = length^-1
// A3 = Length^-2
2024-05-04 16:03:36 -07:00
for ( ; iter ! = end ; iter + + ) {
auto exp = std : : distance ( begin , iter ) ;
2024-10-31 08:55:01 -07:00
auto coeff = ( * iter ) ;
2025-01-06 14:36:37 -08:00
double v = ( double ) exp * coeff * pow ( lu , 1 - exp ) * pow ( x , exp - 1 ) ;
2025-01-02 11:10:56 -08:00
value + = v ;
2024-05-04 16:03:36 -07:00
}
return value ;
} ;
2023-10-13 15:03:23 -07:00
2024-05-04 16:03:36 -07:00
// This functor computes the curve length
// Integral[0,x] (sqrt(f'(x)^2 + 1) dx
auto curve_length_fn = [ df ] ( double x ) - > double {
auto fs = [ df ] ( double x ) - > double {
return sqrt ( pow ( df ( x ) , 2 ) + 1 ) ;
} ;
auto s = boost : : math : : quadrature : : trapezoidal ( fs , 0.0 , x ) ;
return s ;
} ;
2023-10-20 13:29:33 -07:00
2024-05-04 16:03:36 -07:00
// There isn't a closed form solution to get x that corresponds to a distance along the curve, u
// A numerical solution is required.
// This functor finds the value of x such that s(x) - u = 0, where u is the input value and s is the
// computed curve length.
convert_u = [ curve_length_fn ] ( double u ) - > double {
std : : uintmax_t max_iter = 5000 ;
auto tol = [ ] ( double a , double b ) { return fabs ( b - a ) < 1.0E-09 ; } ;
auto x = u ; // start by assuming u = x (it's not, but it will be close)
try {
// set up the root finding function that evaluates s(x) - u
auto f = [ curve_length_fn , u ] ( double x ) - > double { return curve_length_fn ( x ) - u ; } ;
// use a root finder to get x
auto result = boost : : math : : tools : : bracket_and_solve_root ( f , x , 2.0 , true , tol , max_iter ) ;
x = result . first ;
} catch ( . . . ) {
Logger : : Warning ( " root solver failed " ) ;
}
return x ;
} ;
} else {
// for vertical, u = x
convert_u = [ ] ( double u ) - > double { return u ; } ;
}
2023-10-20 13:29:33 -07:00
2024-05-04 16:03:36 -07:00
// This functor evaluates the polynomial at a distance u along the curve
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < polynomial_parent_curve > ( [ start = start_ , lu = length_unit_ , coeffX , coeffY , convert_u ] ( double u ) - > Eigen : : Matrix4d {
2024-05-04 16:03:36 -07:00
auto x = convert_u ( u + start ) ; // find x for u
// evaluate the polynomial at x
std : : array < const std : : vector < double > * , 2 > coefficients { & coeffX , & coeffY } ;
std : : array < double , 2 > position { 0.0 , 0.0 } ; // = SUM(coeff*u^pos)
std : : array < double , 2 > slope { 0.0 , 0.0 } ; // slope is derivative of the curve = SUM( coeff*pos*u^(pos-1) )
for ( int i = 0 ; i < 2 ; i + + ) { // loop over X and Y
auto begin = coefficients [ i ] - > cbegin ( ) ;
auto end = coefficients [ i ] - > cend ( ) ;
for ( auto iter = begin ; iter ! = end ; iter + + ) {
auto exp = std : : distance ( begin , iter ) ;
2024-10-31 08:55:01 -07:00
auto coeff = ( * iter ) ;
2025-01-06 14:36:37 -08:00
position [ i ] + = coeff * pow ( lu , 1 - exp ) * pow ( x , exp ) ;
2024-05-04 16:03:36 -07:00
if ( iter ! = begin ) {
2025-01-06 14:36:37 -08:00
slope [ i ] + = exp * coeff * pow ( lu , 1 - exp ) * pow ( x , exp - 1 ) ;
2024-05-04 16:03:36 -07:00
}
}
}
2024-03-06 12:00:35 -08:00
2024-05-04 16:03:36 -07:00
auto X = position [ 0 ] ;
auto Y = position [ 1 ] ;
2023-10-23 08:22:03 -07:00
2024-05-04 16:03:36 -07:00
auto Dx = slope [ 0 ] ;
auto Dy = slope [ 1 ] ;
2024-02-26 16:34:41 -08:00
2024-05-04 16:03:36 -07:00
auto angle = atan2 ( Dy , Dx ) ;
Dx = cos ( angle ) ;
Dy = sin ( angle ) ;
2024-02-26 16:34:41 -08:00
2024-05-04 16:03:36 -07:00
Eigen : : Matrix4d m = Eigen : : Matrix4d : : Identity ( ) ;
m . col ( 0 ) = Eigen : : Vector4d ( Dx , Dy , 0 , 0 ) ;
m . col ( 1 ) = Eigen : : Vector4d ( - Dy , Dx , 0 , 0 ) ;
m . col ( 3 ) = Eigen : : Vector4d ( X , Y , 0.0 , 1.0 ) ;
return m ;
2025-01-02 11:10:56 -08:00
} ) ;
2024-02-26 16:34:41 -08:00
2024-05-22 13:51:55 -07:00
parent_curve_start_point_ = ( * parent_curve_fn_ ) ( 0.0 ) ; // start is added to u in parent_curve_fn_, so use 0.0 here
2024-05-04 16:03:36 -07:00
} else if ( segment_type_ = = ST_CANT ) {
Logger : : Warning ( std : : runtime_error ( " Use of IfcPolynomialCurve for cant is not supported " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
} else {
Logger : : Error ( std : : runtime_error ( " Unexpected segment type encountered " ) ) ;
2025-01-02 11:10:56 -08:00
parent_curve_fn_ = std : : make_shared < parent_curve_function > ( [ ] ( double /*u*/ ) - > Eigen : : Matrix4d { return Eigen : : Matrix4d : : Identity ( ) ; } ) ;
2024-05-04 16:03:36 -07:00
}
}
2023-09-16 22:23:04 +02:00
} ;
2024-04-10 11:48:26 -07:00
} // namespace
2023-09-16 22:23:04 +02:00
taxonomy : : ptr mapping : : map_impl ( const IfcSchema : : IfcCurveSegment * inst ) {
2025-01-02 11:10:56 -08:00
curve_segment_evaluator cse ( this , inst , length_unit_ ) ;
2024-05-04 16:03:36 -07:00
boost : : mpl : : for_each < curve_seg_types , boost : : type < boost : : mpl : : _ > > ( std : : ref ( cse ) ) ;
2025-01-02 11:10:56 -08:00
return cse . get_segment_curve_function ( ) ;
2023-09-16 22:23:04 +02:00
}
2024-05-04 16:03:36 -07:00
# endif