diff --git a/include/spline/bezier_curve.h b/include/spline/bezier_curve.h index dfdc94e058061eb6528d37b46f8a87382bfb3989..163e1d3addb41ddbf36899fd29117a7d7cd5487e 100644 --- a/include/spline/bezier_curve.h +++ b/include/spline/bezier_curve.h @@ -258,6 +258,82 @@ struct bezier_curve : public curve_abc<Time, Numeric, Dim, Safe, Point> const t_point_t& waypoints() const {return pts_;} + + /** + * @brief evalDeCasteljau evaluate the curve value at time t using deCasteljau algorithm + * @param t unNormalized time + * @return the point at time t + */ + point_t evalDeCasteljau(const Numeric T) const { + // normalize time : + const Numeric t = T/T_; + t_point_t pts = deCasteljauReduction(waypoints(),t); + while(pts.size() > 1){ + pts = deCasteljauReduction(pts,t); + } + return pts[0]*mult_T_; + } + + t_point_t deCasteljauReduction(const Numeric t) const{ + return deCasteljauReduction(waypoints(),t/T_); + } + + /** + * @brief deCasteljauReduction compute the de Casteljau's reduction of the given list of points at time t + * @param pts the original list of points + * @param t the NORMALIZED time + * @return the reduced list of point (size of pts - 1) + */ + t_point_t deCasteljauReduction(const t_point_t& pts, const Numeric t) const{ + if(t < 0 || t > 1) + throw std::out_of_range("In deCasteljau reduction : t is not in [0;1]"); + if(pts.size() == 1) + return pts; + + t_point_t new_pts; + for(cit_point_t cit = pts.begin() ; cit != (pts.end() - 1) ; ++cit){ + new_pts.push_back((1-t) * (*cit) + t*(*(cit+1))); + } + return new_pts; + } + + + /** + * @brief split split the curve in 2 at time t + * @param t + * @return + */ + std::pair<bezier_curve_t,bezier_curve_t> split(const Numeric T){ + t_point_t wps_first(size_),wps_second(size_); + const double t = T/T_; + wps_first[0] = pts_.front(); + wps_second[degree_] = pts_.back(); + t_point_t casteljau_pts = waypoints(); + size_t id = 1; + while(casteljau_pts.size() > 1){ + casteljau_pts = deCasteljauReduction(casteljau_pts,t); + wps_first[id] = casteljau_pts.front(); + wps_second[degree_-id] = casteljau_pts.back(); + ++id; + } + + bezier_curve_t c_first(wps_first.begin(), wps_first.end(), T,mult_T_); + bezier_curve_t c_second(wps_second.begin(), wps_second.end(), T_-T,mult_T_); + return std::make_pair(c_first,c_second); + } + + bezier_curve_t extract(const Numeric t1, const Numeric t2){ + if(t1 < 0. || t1 > T_ || t2 < 0. || t2 > T_) + throw std::out_of_range("In Extract curve : times out of bounds"); + if(t1 == 0.) + return split(t2).first; + if(t2 == T_) + return split(t1).second; + + std::pair<bezier_curve_t,bezier_curve_t> c_split = this->split(t1); + return c_split.second->split(t2).first; + } + private: template<typename In> t_point_t add_constraints(In PointsBegin, In PointsEnd, const curve_constraints_t& constraints) diff --git a/src/tests/spline_test/Main.cpp b/src/tests/spline_test/Main.cpp index ddb35e1049a0409c26b9edef61aa46adcb8b2c8f..686e8f6ab631d7bce4834e0e931a5e8bf50b39f3 100644 --- a/src/tests/spline_test/Main.cpp +++ b/src/tests/spline_test/Main.cpp @@ -255,7 +255,7 @@ void BezierCurveTestCompareHornerAndBernstein(bool& /*error*/) // 3d curve bezier_curve_t cf(params.begin(), params.end()); - clock_t s0,e0,s1,e1,s2,e2; + clock_t s0,e0,s1,e1,s2,e2,s3,e3; s0 = clock(); for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit) { @@ -277,12 +277,18 @@ void BezierCurveTestCompareHornerAndBernstein(bool& /*error*/) } e2 = clock(); - - std::cout << "time for analytical eval " << double(e0 - s0) / CLOCKS_PER_SEC << std::endl; - std::cout << "time for bernstein eval " << double(e1 - s1) / CLOCKS_PER_SEC << std::endl; - std::cout << "time for horner eval " << double(e2 - s2) / CLOCKS_PER_SEC << std::endl; + s3 = clock(); + for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit) + { + cf.evalDeCasteljau(*cit); + } + e3 = clock(); + std::cout << "time for analytical eval " << double(e0 - s0) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for bernstein eval " << double(e1 - s1) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for horner eval " << double(e2 - s2) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for deCasteljau eval " << double(e3 - s3) / CLOCKS_PER_SEC << std::endl; std::cout << "now with high order polynom " << std::endl; @@ -317,10 +323,19 @@ void BezierCurveTestCompareHornerAndBernstein(bool& /*error*/) } e0 = clock(); + s3 = clock(); + for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit) + { + cf2.evalDeCasteljau(*cit); + } + e3 = clock(); + + - std::cout << "time for analytical eval " << double(e0 - s0) / CLOCKS_PER_SEC << std::endl; - std::cout << "time for bernstein eval " << double(e1 - s1) / CLOCKS_PER_SEC << std::endl; - std::cout << "time for horner eval " << double(e2 - s2) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for analytical eval " << double(e0 - s0) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for bernstein eval " << double(e1 - s1) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for horner eval " << double(e2 - s2) / CLOCKS_PER_SEC << std::endl; + std::cout << "time for deCasteljau eval " << double(e3 - s3) / CLOCKS_PER_SEC << std::endl; } @@ -768,6 +783,143 @@ void TestReparametrization(bool& error) } } +point_t randomPoint(const double min, const double max ){ + point_t p; + for(size_t i = 0 ; i < 3 ; ++i) + p[i] = (rand()/(double)RAND_MAX ) * (max-min) + min; + return p; +} + +void BezierEvalDeCasteljau(bool& error){ + using namespace std; + std::vector<double> values; + for (int i =0; i < 100000; ++i) + values.push_back(rand()/RAND_MAX); + + //first compare regular evaluation (low dim pol) + point_t a(1,2,3); + point_t b(2,3,4); + point_t c(3,4,5); + point_t d(3,6,7); + point_t e(3,61,7); + point_t f(3,56,7); + point_t g(3,36,7); + point_t h(43,6,7); + point_t i(3,6,77); + + std::vector<point_t> params; + params.push_back(a); + params.push_back(b); + params.push_back(c); + + // 3d curve + bezier_curve_t cf(params.begin(), params.end()); + + for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit) + { + if(cf.evalDeCasteljau(*cit) != cf(*cit)){ + error = true; + std::cout<<" De Casteljau evaluation did not return the same value as analytical"<<std::endl; + } + } + + params.push_back(d); + params.push_back(e); + params.push_back(f); + params.push_back(g); + params.push_back(h); + params.push_back(i); + + bezier_curve_t cf2(params.begin(), params.end()); + + for(std::vector<double>::const_iterator cit = values.begin(); cit != values.end(); ++cit) + { + if(cf.evalDeCasteljau(*cit) != cf(*cit)){ + error = true; + std::cout<<" De Casteljau evaluation did not return the same value as analytical"<<std::endl; + } + } + +} + + +/** + * @brief BezierSplitCurve test the 'split' method of bezier curve + * @param error + */ +void BezierSplitCurve(bool& error){ + // test for degree 5 + int n = 5; + double t_min = 0.2; + double t_max = 10; + for(size_t i = 0 ; i < 1 ; ++i){ + // build a random curve and split it at random time : + //std::cout<<"build a random curve"<<std::endl; + point_t a; + std::vector<point_t> wps; + for(size_t j = 0 ; j <= n ; ++j){ + wps.push_back(randomPoint(-10.,10.)); + } + double t = (rand()/(double)RAND_MAX )*(t_max-t_min) + t_min; + double ts = (rand()/(double)RAND_MAX )*(t); + + bezier_curve_t c(wps.begin(), wps.end(),t); + std::pair<bezier_curve_t,bezier_curve_t> cs = c.split(ts); + //std::cout<<"split curve of duration "<<t<<" at "<<ts<<std::endl; + + // test on splitted curves : + + if(! ((c.degree_ == cs.first.degree_) && (c.degree_ == cs.second.degree_) )){ + error = true; + std::cout<<" Degree of the splitted curve are not the same as the original curve"<<std::endl; + } + + if(c.max() != (cs.first.max() + cs.second.max())){ + error = true; + std::cout<<"Duration of the splitted curve doesn't correspond to the original"<<std::endl; + } + + if(c(0) != cs.first(0)){ + error = true; + std::cout<<"initial point of the splitted curve doesn't correspond to the original"<<std::endl; + } + + if(c(t) != cs.second(cs.second.max())){ + error = true; + std::cout<<"final point of the splitted curve doesn't correspond to the original"<<std::endl; + } + + if(cs.first.max() != ts){ + error = true; + std::cout<<"timing of the splitted curve doesn't correspond to the original"<<std::endl; + } + + if(cs.first(ts) != cs.second(0.)){ + error = true; + std::cout<<"splitting point of the splitted curve doesn't correspond to the original"<<std::endl; + } + + // check along curve : + double ti = 0.; + while(ti <= ts){ + if((cs.first(ti) - c(ti)).norm() > 1e-14){ + error = true; + std::cout<<"first splitted curve and original curve doesn't correspond, error = "<<cs.first(ti) - c(ti) <<std::endl; + } + ti += 0.01; + } + while(ti <= t){ + if((cs.second(ti-ts) - c(ti)).norm() > 1e-14){ + error = true; + std::cout<<"second splitted curve and original curve doesn't correspond, error = "<<cs.second(ti-ts) - c(ti)<<std::endl; + } + ti += 0.01; + } + } +} + + + int main(int /*argc*/, char** /*argv[]*/) { std::cout << "performing tests... \n"; @@ -789,7 +941,9 @@ int main(int /*argc*/, char** /*argv[]*/) BezierCurveTestCompareHornerAndBernstein(error); BezierDerivativeCurveTimeReparametrizationTest(error); BezierToPolynomConversionTest(error); - if(error) + BezierEvalDeCasteljau(error); + BezierSplitCurve(error); + if(error) { std::cout << "There were some errors\n"; return -1;