Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing a curve through a set of N points, where N>2

I need to draw a curve smoothly through N points with N>2 with Javascript. I am currently using the bezierCurveTo() within an html5 canvas object. I've invested a good deal of time into trying to find the best method of accomplishing this, and while Catmull-Rom Splines sound promising, I am unaware of a way to draw them as lines. I therefore am left with Poly-line Bézier curves and need to find all intermediate control points.

It took me relearning a good deal of math but I have a partially working example here. The line is not particularly smooth at times, and there is an issue with my control points being off for certain vectors. Here is my initial math.stackexchange question that I asked to get the bulk of the math for the control points.

I would also be open to the Catmull-Rom Splines but would need some help as to how I can convert them to lines on the canvas. I have found this resource, but again am lost in applying it to my needs.

Heres an image created from my example link above illustrating how to replicate the bad control points:

enter image description here

Thank you very much for any help in advance!

like image 823
RANGER Avatar asked Jul 23 '11 01:07

RANGER


1 Answers

Original code:

var a = new Array(points[p-1].x,points[p-1].y);
var b = new Array(points[p].x,points[p].y);
var c = new Array(points[p+1].x,points[p+1].y);

var delta_a = subtract_v(a, b);
var delta_c = subtract_v(c, b);

// Get vector (m) perpendicular bisector
var m = normalize_v( add_v( normalize_v(delta_a),normalize_v(delta_c) ) );

// Get ma and mc
var ma = normalize_v( subtract_v(delta_a,multiply_v(multiply_v(delta_a,m),m) ) );
var mc = normalize_v( subtract_v(delta_c,multiply_v(multiply_v(delta_c,m),m) ) );

// Get the coordinates
points[p].c2x = resolution( b[0] + ( (Math.sqrt( sqr(delta_a[0]) + sqr(delta_a[1]) ) / tightness) * ma[0] ) );
points[p].c2y = resolution( b[1] + ( (Math.sqrt( sqr(delta_a[0]) + sqr(delta_a[1]) ) / tightness) * ma[1] ) );
points[p+1].c1x = resolution( b[0] + ( (Math.sqrt( sqr(delta_c[0]) + sqr(delta_c[1]) ) / tightness) * mc[0] ) );
points[p+1].c1y = resolution( b[1] + ( (Math.sqrt( sqr(delta_c[0]) + sqr(delta_c[1]) ) / tightness) * mc[1] ) );

I have no idea what "Get ma and mc" is supposed to do here. What you need is a vector orthogonal to the angle bisector (m), and its negative.

Sorry for the horrible drawing

So this should be fine:

var delta_a = subtract_v(b, a); // note that we're calculating AB instead of BA
var delta_c = subtract_v(c, b);

// Get an orthogonal to the angle bisector
var m = normalize_v( add_v( normalize_v(delta_a),normalize_v(delta_c) ) );

var ma = [-m[0],-m[1]];
var mc = m;

Also make sure to remove the resolution() for your control points.

Edit:
You should also add a fallback for edge cases (for example, A==B or A==C, in which case your script will throw an exception, trying to normalize the 0 vector)

like image 177
user123444555621 Avatar answered Oct 09 '22 03:10

user123444555621