UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

171 lines (157 loc) 4.45 kB
define([ "./_base" ], function(gfx){ var bu = gfx.bezierutils = {}, error = 0.1; var tAtLength = bu.tAtLength = function(points, length){ // summary: // Returns the t corresponding to the given length for the specified bezier curve. // points: Number[] // The bezier points. Should be [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y] for a cubic // bezier curve or [p1x, p1y, cx, cy, p2x, p2y] for a quadratic bezier curve. // length: Number // The length. var t = 0, quadratic = points.length == 6, currentLen = 0, splitCount = 0, splitFunc = quadratic ? splitQBezierAtT : splitBezierAtT; var _compute = function(p, error){ // control points polygon length var pLen = 0; for(var i = 0; i < p.length-2; i+=2) pLen += distance(p[i],p[i+1],p[i+2],p[i+3]); // chord length var chord = quadratic ? distance(points[0],points[1],points[4],points[5]) : distance(points[0],points[1],points[6],points[7]); // if needs more approx. or if currentLen is greater than the target length, // split the curve one more time if(pLen - chord > error || currentLen + pLen > length + error){ ++splitCount; var newbezier = splitFunc(p, .5); // check 1st subpath _compute(newbezier[0], error); // the 1st subcurve was the good one, we stop if(Math.abs(currentLen - length) <= error){ return; } // need to continue with the 2nde subcurve _compute(newbezier[1], error); return ; } currentLen += pLen; t += 1.0 / (1 << splitCount); }; if(length) _compute(points, 0.5); return t; }; var computeLength = bu.computeLength = function(/*Array*/points){ // summary: // Returns the length of the given bezier curve. // points: Number[] // The bezier points. Should be [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y] for a cubic // bezier curve or [p1x, p1y, cx, cy, p2x, p2y] for a quadratic bezier curve. var quadratic = points.length == 6, pLen=0; // control points polygon length for(var i = 0; i < points.length-2; i+=2) pLen += distance(points[i],points[i+1],points[i+2],points[i+3]); // chord length var chord = quadratic ? distance(points[0],points[1],points[4],points[5]) : distance(points[0],points[1],points[6],points[7]); // split polygons until the polygon and the chord are "the same" if(pLen-chord>error){ var newBeziers = quadratic ? splitQBezierAtT(points,.5) : splitCBezierAtT(points,.5); var length = computeLength(newBeziers[0], quadratic); length += computeLength(newBeziers[1], quadratic); return length; } // pLen is close enough, done. return pLen; }; var distance = bu.distance = function(x1, y1, x2, y2){ // summary: // Returns the distance between the specified points. return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); }; var splitQBezierAtT = function(points, t){ // summary: // Split a quadratic bezier curve into 2 sub-quadratic beziers at the specified t. // de Casteljau var r = 1-t, r2 = r*r, t2 = t*t, p1x = points[0], p1y = points[1], cx = points[2], cy = points[3], p2x = points[4], p2y = points[5], ax = r*p1x + t*cx, ay = r*p1y + t*cy, bx = r*cx + t*p2x, by = r*cy + t*p2y, px = r2*p1x + 2*r*t*cx + t2*p2x, py = r2*p1y + 2*r*t*cy + t2*p2y; return [ [ p1x, p1y, ax, ay, px, py ], [ px, py, bx, by, p2x, p2y ] ]; }; var splitCBezierAtT = function(points, t){ // summary: // Split a cubic bezier curve into 2 sub-cubic beziers at the specified t. // de Casteljau var r = 1-t, r2 = r*r, r3 = r2*r, t2 = t*t, t3 = t2*t, p1x = points[0], p1y = points[1], c1x = points[2], c1y = points[3], c2x = points[4], c2y = points[5], p2x = points[6], p2y = points[7], ax = r*p1x + t*c1x, ay = r*p1y + t*c1y, cx = r*c2x + t*p2x, cy = r*c2y + t*p2y, mx = r2*p1x + 2*r*t*c1x + t2*c2x, my = r2*p1y + 2*r*t*c1y + t2*c2y, nx = r2*c1x + 2*r*t*c2x + t2*p2x, ny = r2*c1y + 2*r*t*c2y + t2*p2y, px = r3*p1x + 3*r2*t*c1x + 3*r*t2*c2x+t3*p2x, py = r3*p1y + 3*r2*t*c1y + 3*r*t2*c2y+t3*p2y; return [ [ p1x, p1y, ax, ay, mx, my, px, py ], [ px, py, nx, ny, cx, cy, p2x, p2y ] ]; }; var splitBezierAtT = bu.splitBezierAtT = function(points, t){ return points.length == 6 ? splitQBezierAtT(points, t) : splitCBezierAtT(points, t); }; return bu; });