diff --git a/src/modules/math/BezierCurve.cpp b/src/modules/math/BezierCurve.cpp index 4cbe984dd..c8e700a45 100644 --- a/src/modules/math/BezierCurve.cpp +++ b/src/modules/math/BezierCurve.cpp @@ -23,6 +23,7 @@ #include "common/Exception.h" #include +#include using namespace std; @@ -185,6 +186,44 @@ Vector BezierCurve::evaluate(double t) const return points[0]; } +BezierCurve* BezierCurve::getSegment(double t1, double t2) const +{ + if (t1 < 0 || t2 > 1) + throw Exception("Invalid segment parameters: must be between 0 and 1"); + if (t2 <= t1) + throw Exception("Invalid segment parameters: t1 must be smaller than t2"); + + // First, sudivide the curve at t2, then subdivide the "left" + // sub-curve at t1/t2. The "right" curve is the segment. + vector points(controlPoints); + vector left, right; + left.reserve(points.size()); + right.reserve(points.size()); + + // first subdivision at t2 (take only the left curve) + for (size_t step = 1; step < points.size(); ++step) + { + left.push_back(points[0]); + for (size_t i = 0; i < points.size() - step; ++i) + points[i] += (points[i+1] - points[i]) * t2; // p_i <- (1-t2)*p_i + t2*p_{i+1} + } + left.push_back(points[0]); + + // second subdivion at t1/t2 (take only the right curve) + double s = t1/t2; + for (size_t step = 1; step < left.size(); ++step) + { + right.push_back(left[left.size() - step]); + for (size_t i = 0; i < left.size() - step; ++i) + left[i] += (left[i+1] - left[i]) * s; + } + right.push_back(left[0]); + + // control points for right curve were added in reversed order + std::reverse(right.begin(), right.end()); + return new BezierCurve(right); +} + vector BezierCurve::render(int accuracy) const { if (controlPoints.size() < 2) diff --git a/src/modules/math/BezierCurve.h b/src/modules/math/BezierCurve.h index 1dad02a2a..0b30d4654 100644 --- a/src/modules/math/BezierCurve.h +++ b/src/modules/math/BezierCurve.h @@ -113,6 +113,15 @@ public: **/ Vector evaluate(double t) const; + /** + * Get curve segment starting at t1 and ending at t2. + * The new curve will be parametrized from 0 <= t <= 1. + * @param t1 Start of the segment. + * @param t2 End of the segment. + * @returns Bezier curve covering the segment. + */ + BezierCurve* getSegment(double t1, double t2) const; + /** * Renders the curve by subdivision. * @param accuracy The 'fineness' of the curve. diff --git a/src/modules/math/wrap_BezierCurve.cpp b/src/modules/math/wrap_BezierCurve.cpp index e7524b1b2..620d5b281 100644 --- a/src/modules/math/wrap_BezierCurve.cpp +++ b/src/modules/math/wrap_BezierCurve.cpp @@ -157,6 +157,20 @@ int w_BezierCurve_evaluate(lua_State *L) } +int w_BezierCurve_getSegment(lua_State *L) +{ + BezierCurve *curve = luax_checkbeziercurve(L, 1); + double t1 = luaL_checknumber(L, 2); + double t2 = luaL_checknumber(L, 3); + + BezierCurve *segment; + luax_catchexcept(L, [&](){ segment = curve->getSegment(t1, t2); }); + luax_pushtype(L, MATH_BEZIER_CURVE_ID, segment); + segment->release(); + + return 1; +} + int w_BezierCurve_render(lua_State *L) { BezierCurve *curve = luax_checkbeziercurve(L, 1); @@ -212,6 +226,7 @@ static const luaL_Reg functions[] = {"rotate", w_BezierCurve_rotate}, {"scale", w_BezierCurve_scale}, {"evaluate", w_BezierCurve_evaluate}, + {"getSegment", w_BezierCurve_getSegment}, {"render", w_BezierCurve_render}, {"renderSegment", w_BezierCurve_renderSegment}, { 0, 0 } diff --git a/src/modules/math/wrap_BezierCurve.h b/src/modules/math/wrap_BezierCurve.h index 110f7751a..e5aae3dae 100644 --- a/src/modules/math/wrap_BezierCurve.h +++ b/src/modules/math/wrap_BezierCurve.h @@ -42,6 +42,7 @@ int w_BezierCurve_translate(lua_State *L); int w_BezierCurve_rotate(lua_State *L); int w_BezierCurve_scale(lua_State *L); int w_BezierCurve_evaluate(lua_State *L); +int w_BezierCurve_getSegment(lua_State *L); int w_BezierCurve_render(lua_State *L); int w_BezierCurve_renderSegment(lua_State *L); extern "C" int luaopen_beziercurve(lua_State *L);