Browse Source

Add BezierCurve:getSegment(t1, t2)

Returns a BezierCurve that corresponds to the curve-segment between t1 and t2
on the original curve.
vrld 10 years ago
parent
commit
f32d770cf6

+ 39 - 0
src/modules/math/BezierCurve.cpp

@@ -23,6 +23,7 @@
 #include "common/Exception.h"
 
 #include <cmath>
+#include <algorithm>
 
 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<Vector> points(controlPoints);
+	vector<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<Vector> BezierCurve::render(int accuracy) const
 {
 	if (controlPoints.size() < 2)

+ 9 - 0
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.

+ 15 - 0
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 }

+ 1 - 0
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);