2022-04-23 21:47:16 +02:00
# include "BezierCurve.hpp"
2023-08-07 13:35:19 +02:00
# include "../debug/Log.hpp"
# include "../macros.hpp"
2022-04-23 21:47:16 +02:00
2023-08-07 13:35:19 +02:00
# include <chrono>
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
2024-08-26 20:24:30 +02:00
for ( auto const & p : * pVec ) {
2022-04-23 21:47:16 +02:00
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
2023-09-06 12:51:36 +02:00
RASSERT ( m_dPoints . size ( ) = = 4 , " CBezierCurve only supports cubic beziers! (points num: {}) " , m_dPoints . size ( ) ) ;
2022-04-23 21:47:16 +02:00
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 ( ) ;
2024-07-31 21:00:14 +02:00
for ( int j = 1 ; j < 10 ; + + j ) {
float i = j / 10.0f ;
2022-04-23 23:16:43 +02:00
getYForPoint ( i ) ;
2024-07-31 21:00:14 +02:00
}
2022-04-23 23:16:43 +02:00
const auto ELAPSEDCALCAVG = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( std : : chrono : : high_resolution_clock : : now ( ) - BEGINCALC ) . count ( ) / 1000.f / 10.f ;
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " Created a bezier curve, baked {} points, mem usage: {:.2f}kB, 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 ) {
2023-09-16 19:32:33 +02:00
if ( x > = 1.f )
return 1.f ;
int index = 0 ;
bool below = true ;
for ( int step = ( BAKEDPOINTS + 1 ) / 2 ; step > 0 ; step / = 2 ) {
if ( below )
index + = step ;
else
index - = step ;
below = m_aPointsBaked [ index ] . x < x ;
2022-04-23 21:47:16 +02:00
}
2023-09-16 19:32:33 +02:00
int lowerIndex = index - ( ! below | | index = = BAKEDPOINTS - 1 ) ;
2022-04-23 21:47:16 +02:00
// in the name of performance i shall make a hack
2023-09-16 19:32:33 +02:00
const auto LOWERPOINT = & m_aPointsBaked [ lowerIndex ] ;
const auto UPPERPOINT = & m_aPointsBaked [ lowerIndex + 1 ] ;
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 ;
2023-03-03 14:08:46 +01:00
return LOWERPOINT - > y + ( UPPERPOINT - > y - LOWERPOINT - > y ) * PERCINDELTA ;
2023-09-16 19:32:33 +02:00
}