This example splits cubic and bezier curves in two.
The function splitCurveAt
splits the curve at position
where 0.0
= start, 0.5
= middle, and 1
= end. It can split quadratic and cubic curves. The curve type is determined by the last x argument x4
. If not undefined
or null
then it assumes the curve is cubic else the curve is a quadratic
Splitting quadratic bezier curve in two
var p1 = {x : 10 , y : 100};
var p2 = {x : 100, y : 200};
var p3 = {x : 200, y : 0};
var newCurves = splitCurveAt(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
var i = 0;
var p = newCurves
// Draw the 2 new curves
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p[i++],p[i++]);
ctx.quadraticCurveTo(p[i++], p[i++], p[i++], p[i++]);
ctx.quadraticCurveTo(p[i++], p[i++], p[i++], p[i++]);
ctx.stroke();
Splitting cubic bezier curve in two
var p1 = {x : 10 , y : 100};
var p2 = {x : 100, y : 200};
var p3 = {x : 200, y : 0};
var p4 = {x : 300, y : 100};
var newCurves = splitCurveAt(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)
var i = 0;
var p = newCurves
// Draw the 2 new curves
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p[i++],p[i++]);
ctx.bezierCurveTo(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]);
ctx.bezierCurveTo(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]);
ctx.stroke();
splitCurveAt = function(position, x1, y1, x2, y2, x3, y3, [x4, y4])
Note: Arguments inside [x4, y4] are optional.
Note: The function has some optional commented
/* */
code that deals with edge cases where the resulting curves may have zero length, or fall outside the start or ends of the original curve. As is attempting to split a curve outside the valid range forposition >= 0
orposition >= 1
will throw a range error. This can be removed and will work just fine, though you may have resulting curves that have zero length.
// With throw RangeError if not 0 < position < 1
// x1, y1, x2, y2, x3, y3 for quadratic curves
// x1, y1, x2, y2, x3, y3, x4, y4 for cubic curves
// Returns an array of points representing 2 curves. The curves are the same type as the split curve
var splitCurveAt = function(position, x1, y1, x2, y2, x3, y3, x4, y4){
var v1, v2, v3, v4, quad, retPoints, i, c;
// =============================================================================================
// you may remove this as the function will still work and resulting curves will still render
// but other curve functions may not like curves with 0 length
// =============================================================================================
if(position <= 0 || position >= 1){
throw RangeError("spliteCurveAt requires position > 0 && position < 1");
}
// =============================================================================================
// If you remove the above range error you may use one or both of the following commented sections
// Splitting curves position < 0 or position > 1 will still create valid curves but they will
// extend past the end points
// =============================================================================================
// Lock the position to split on the curve.
/* optional A
position = position < 0 ? 0 : position > 1 ? 1 : position;
optional A end */
// =============================================================================================
// the next commented section will return the original curve if the split results in 0 length curve
// You may wish to uncomment this If you desire such functionality
/* optional B
if(position <= 0 || position >= 1){
if(x4 === undefined || x4 === null){
return [x1, y1, x2, y2, x3, y3];
}else{
return [x1, y1, x2, y2, x3, y3, x4, y4];
}
}
optional B end */
retPoints = []; // array of coordinates
i = 0;
quad = false; // presume cubic bezier
v1 = {};
v2 = {};
v4 = {};
v1.x = x1;
v1.y = y1;
v2.x = x2;
v2.y = y2;
if(x4 === undefined || x4 === null){
quad = true; // this is a quadratic bezier
v4.x = x3;
v4.y = y3;
}else{
v3 = {};
v3.x = x3;
v3.y = y3;
v4.x = x4;
v4.y = y4;
}
c = position;
retPoints[i++] = v1.x; // start point
retPoints[i++] = v1.y;
if(quad){ // split quadratic bezier
retPoints[i++] = (v1.x += (v2.x - v1.x) * c); // new control point for first curve
retPoints[i++] = (v1.y += (v2.y - v1.y) * c);
v2.x += (v4.x - v2.x) * c;
v2.y += (v4.y - v2.y) * c;
retPoints[i++] = v1.x + (v2.x - v1.x) * c; // new end and start of first and second curves
retPoints[i++] = v1.y + (v2.y - v1.y) * c;
retPoints[i++] = v2.x; // new control point for second curve
retPoints[i++] = v2.y;
retPoints[i++] = v4.x; // new endpoint of second curve
retPoints[i++] = v4.y;
//=======================================================
// return array with 2 curves
return retPoints;
}
retPoints[i++] = (v1.x += (v2.x - v1.x) * c); // first curve first control point
retPoints[i++] = (v1.y += (v2.y - v1.y) * c);
v2.x += (v3.x - v2.x) * c;
v2.y += (v3.y - v2.y) * c;
v3.x += (v4.x - v3.x) * c;
v3.y += (v4.y - v3.y) * c;
retPoints[i++] = (v1.x += (v2.x - v1.x) * c); // first curve second control point
retPoints[i++] = (v1.y += (v2.y - v1.y) * c);
v2.x += (v3.x - v2.x) * c;
v2.y += (v3.y - v2.y) * c;
retPoints[i++] = v1.x + (v2.x - v1.x) * c; // end and start point of first second curves
retPoints[i++] = v1.y + (v2.y - v1.y) * c;
retPoints[i++] = v2.x; // second curve first control point
retPoints[i++] = v2.y;
retPoints[i++] = v3.x; // second curve second control point
retPoints[i++] = v3.y;
retPoints[i++] = v4.x; // endpoint of second curve
retPoints[i++] = v4.y;
//=======================================================
// return array with 2 curves
return retPoints;
}