fabric.js/src/util/arc.js
Andrea Bogazzi 7cbe8a965e Update arc.js
Fix useless optimization of code that was rendering arcs not correctly
2014-09-09 14:17:44 +02:00

139 lines
4.2 KiB
JavaScript

(function() {
var arcToSegmentsCache = { },
segmentToBezierCache = { },
_join = Array.prototype.join;
/* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
* by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
* http://mozilla.org/MPL/2.0/
*/
function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
var argsString = _join.call(arguments);
if (arcToSegmentsCache[argsString]) {
return arcToSegmentsCache[argsString];
}
var PI = Math.PI, th = rotateX * PI / 180,
sinTh = Math.sin(th),
cosTh = Math.cos(th),
fromX = 0, fromY = 0;
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
root = 0;
if (pl < 0) {
var s = Math.sqrt(1 - pl/(rx2 * ry2));
rx *= s;
ry *= s;
}
else {
root = (large === sweep ? -1.0 : 1.0) *
Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
}
var cx = root * rx * py / ry,
cy = -root * ry * px / rx,
cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
if (sweep === 0 && dtheta > 0) {
dtheta -= 2 * PI;
}
else if (sweep === 1 && dtheta < 0) {
dtheta += 2 * PI;
}
// Convert into cubic bezier segments <= 90deg
var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
result = [], mDelta = dtheta / segments,
mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
th3 = mTheta + mDelta;
for (var i = 0; i < segments; i++) {
result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
fromX = result[i][4];
fromY = result[i][5];
mTheta = th3;
th3 += mDelta;
}
arcToSegmentsCache[argsString] = result;
return result;
}
function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
var argsString2 = _join.call(arguments);
if (segmentToBezierCache[argsString2]) {
return segmentToBezierCache[argsString2];
}
var costh2 = Math.cos(th2),
sinth2 = Math.sin(th2),
costh3 = Math.cos(th3),
sinth3 = Math.sin(th3),
toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
segmentToBezierCache[argsString2] = [
cp1X, cp1Y,
cp2X, cp2Y,
toX, toY
];
return segmentToBezierCache[argsString2];
}
/*
* Private
*/
function calcVectorAngle(ux, uy, vx, vy) {
var ta = Math.atan2(uy, ux),
tb = Math.atan2(vy, vx);
if (tb >= ta) {
return tb - ta;
}
else {
return 2 * Math.PI - (ta - tb);
}
}
/**
* Draws arc
* @param {CanvasRenderingContext2D} ctx
* @param {Number} fx
* @param {Number} fy
* @param {Array} coords
*/
fabric.util.drawArc = function(ctx, fx, fy, coords) {
var rx = coords[0],
ry = coords[1],
rot = coords[2],
large = coords[3],
sweep = coords[4],
tx = coords[5],
ty = coords[6],
segs = [[ ], [ ], [ ], [ ]],
segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
for (var i = 0, len = segsNorm.length; i < len; i++) {
segs[i][0] = segsNorm[i][0] + fx;
segs[i][1] = segsNorm[i][1] + fy;
segs[i][2] = segsNorm[i][2] + fx;
segs[i][3] = segsNorm[i][3] + fy;
segs[i][4] = segsNorm[i][4] + fx;
segs[i][5] = segsNorm[i][5] + fy;
ctx.bezierCurveTo.apply(ctx, segs[i]);
}
};
})();