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 00:03:23 +00:00
# include <algorithm>
2022-04-23 21:47:16 +02:00
void CBezierCurve : : setup ( std : : vector < Vector2D > * pVec ) {
2022-04-23 23:16:43 +02:00
const auto BEGIN = std : : chrono : : high_resolution_clock : : now ( ) ;
2024-11-22 02:47:51 +01:00
// Avoid reallocations by reserving enough memory upfront
m_dPoints . resize ( pVec - > size ( ) + 2 ) ;
m_dPoints [ 0 ] = Vector2D ( 0 , 0 ) ; // Start point
size_t index = 1 ; // Start after the first element
for ( const auto & vec : * pVec ) {
if ( index < m_dPoints . size ( ) - 1 ) { // Bounds check to ensure safety
m_dPoints [ index ] = vec ;
+ + index ;
}
2022-04-23 21:47:16 +02:00
}
2024-11-22 02:47:51 +01:00
m_dPoints . back ( ) = Vector2D ( 1 , 1 ) ; // End point
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 ) {
2024-11-22 02:47:51 +01:00
float const t = ( i + 1 ) / ( float ) BAKEDPOINTS ;
m_aPointsBaked [ i ] = Vector2D ( getXForT ( t ) , getYForT ( t ) ) ;
2022-04-23 21:47:16 +02:00
}
2022-04-23 23:16:43 +02:00
2022-12-16 17:17:31 +00: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
}
2024-11-22 02:47:51 +01:00
float CBezierCurve : : getXForT ( float const & t ) {
float t2 = t * t ;
float t3 = t2 * t ;
return 3 * t * ( 1 - t ) * ( 1 - t ) * m_dPoints [ 1 ] . x + 3 * t2 * ( 1 - t ) * m_dPoints [ 2 ] . x + t3 * m_dPoints [ 3 ] . x ;
2022-04-23 21:47:16 +02:00
}
2024-11-22 02:47:51 +01:00
float CBezierCurve : : getYForT ( float const & t ) {
float t2 = t * t ;
float t3 = t2 * t ;
return 3 * t * ( 1 - t ) * ( 1 - t ) * m_dPoints [ 1 ] . y + 3 * t2 * ( 1 - t ) * m_dPoints [ 2 ] . y + t3 * m_dPoints [ 3 ] . y ;
2022-04-23 21:47:16 +02:00
}
// Todo: this probably can be done better and faster
2024-11-22 02:47:51 +01:00
float CBezierCurve : : getYForPoint ( float const & x ) {
2023-09-16 19:32:33 +02:00
if ( x > = 1.f )
return 1.f ;
2024-11-22 02:47:51 +01:00
if ( x < = 0.f )
return 0.f ;
2023-09-16 19:32:33 +02:00
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 17:17:31 +00: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 13:08:46 +00:00
return LOWERPOINT - > y + ( UPPERPOINT - > y - LOWERPOINT - > y ) * PERCINDELTA ;
2023-09-16 19:32:33 +02:00
}