2022-04-23 21:47:16 +02:00
# include "BezierCurve.hpp"
2023-01-31 01:03:23 +01:00
# include <algorithm>
2022-04-23 21:47:16 +02:00
void CBezierCurve : : setup ( std : : vector < Vector2D > * pVec ) {
m_dPoints . clear ( ) ;
2022-04-23 23:16:43 +02:00
const auto BEGIN = std : : chrono : : high_resolution_clock : : now ( ) ;
2022-12-16 18:17:31 +01:00
m_dPoints . emplace_back ( Vector2D ( 0 , 0 ) ) ;
2022-04-23 21:47:16 +02:00
for ( auto & p : * pVec ) {
m_dPoints . push_back ( p ) ;
}
2022-12-16 18:17:31 +01:00
m_dPoints . emplace_back ( Vector2D ( 1 , 1 ) ) ;
2022-04-23 21:47:16 +02:00
RASSERT ( m_dPoints . size ( ) = = 4 , " CBezierCurve only supports cubic beziers! (points num: %i) " , m_dPoints . size ( ) ) ;
2022-04-23 23:16:43 +02:00
// bake BAKEDPOINTS points for faster lookups
// T -> X ( / BAKEDPOINTS )
for ( int i = 0 ; i < BAKEDPOINTS ; + + i ) {
2022-05-12 16:59:51 +02:00
m_aPointsBaked [ i ] = Vector2D ( getXForT ( ( i + 1 ) / ( float ) BAKEDPOINTS ) , getYForT ( ( i + 1 ) / ( float ) BAKEDPOINTS ) ) ;
2022-04-23 21:47:16 +02:00
}
2022-04-23 23:16:43 +02:00
2022-12-16 18:17:31 +01:00
const auto ELAPSEDUS = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( std : : chrono : : high_resolution_clock : : now ( ) - BEGIN ) . count ( ) / 1000.f ;
2022-04-23 23:16:43 +02:00
const auto POINTSSIZE = m_aPointsBaked . size ( ) * sizeof ( m_aPointsBaked [ 0 ] ) / 1000.f ;
const auto BEGINCALC = std : : chrono : : high_resolution_clock : : now ( ) ;
for ( float i = 0.1f ; i < 1.f ; i + = 0.1f )
getYForPoint ( i ) ;
const auto ELAPSEDCALCAVG = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( std : : chrono : : high_resolution_clock : : now ( ) - BEGINCALC ) . count ( ) / 1000.f / 10.f ;
2022-12-16 18:17:31 +01:00
Debug : : log ( LOG , " Created a bezier curve, baked %i points, mem usage: %.2fkB, time to bake: %.2fµs. Estimated average calc time: %.2fµs. " , BAKEDPOINTS , POINTSSIZE , ELAPSEDUS ,
ELAPSEDCALCAVG ) ;
2022-04-23 21:47:16 +02:00
}
float CBezierCurve : : getYForT ( float t ) {
return 3 * t * pow ( 1 - t , 2 ) * m_dPoints [ 1 ] . y + 3 * pow ( t , 2 ) * ( 1 - t ) * m_dPoints [ 2 ] . y + pow ( t , 3 ) ;
}
float CBezierCurve : : getXForT ( float t ) {
return 3 * t * pow ( 1 - t , 2 ) * m_dPoints [ 1 ] . x + 3 * pow ( t , 2 ) * ( 1 - t ) * m_dPoints [ 2 ] . x + pow ( t , 3 ) ;
}
// Todo: this probably can be done better and faster
float CBezierCurve : : getYForPoint ( float x ) {
// binary search for the range UPDOWN X
2022-05-12 16:59:51 +02:00
float upperT = 1 ;
float lowerT = 0 ;
2022-12-16 18:17:31 +01:00
float mid = 0.5 ;
2022-04-23 21:47:16 +02:00
2022-12-16 18:17:31 +01:00
while ( std : : abs ( upperT - lowerT ) > INVBAKEDPOINTS ) {
2022-05-12 16:59:51 +02:00
if ( m_aPointsBaked [ ( ( int ) ( mid * ( float ) BAKEDPOINTS ) ) ] . x > x ) {
upperT = mid ;
2022-04-23 21:47:16 +02:00
} else {
2022-05-12 16:59:51 +02:00
lowerT = mid ;
2022-04-23 21:47:16 +02:00
}
2022-05-12 16:59:51 +02:00
mid = ( upperT + lowerT ) / 2.f ;
2022-04-23 21:47:16 +02:00
}
// in the name of performance i shall make a hack
2022-05-17 18:37:14 +02:00
const auto LOWERPOINT = & m_aPointsBaked [ std : : clamp ( ( int ) ( ( float ) BAKEDPOINTS * lowerT ) , 0 , 199 ) ] ;
const auto UPPERPOINT = & m_aPointsBaked [ std : : clamp ( ( int ) ( ( float ) BAKEDPOINTS * upperT ) , 0 , 199 ) ] ;
2022-05-12 16:59:51 +02:00
const auto PERCINDELTA = ( x - LOWERPOINT - > x ) / ( UPPERPOINT - > x - LOWERPOINT - > x ) ;
2022-04-23 21:47:16 +02:00
2022-12-16 18:17:31 +01:00
if ( std : : isnan ( PERCINDELTA ) | | std : : isinf ( PERCINDELTA ) ) // can sometimes happen for VERY small x
2022-04-23 21:47:16 +02:00
return 0.f ;
2022-05-12 16:59:51 +02:00
return LOWERPOINT - > y + ( UPPERPOINT - > y - UPPERPOINT - > y ) * PERCINDELTA ;
2022-04-23 21:47:16 +02:00
}