forked from enviPath/enviPy
128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
// Borrowed from https://github.com/PPvG/svg-path/blob/master/lib/Path.js#L208
|
|
// that were borrowed from https://github.com/DmitryBaranovskiy/raphael/blob/4d97d4ff5350bb949b88e6d78b877f76ea8b5e24/raphael.js#L2216-L2304
|
|
// (MIT licensed; http://raphaeljs.com/license.html).
|
|
|
|
module.exports = a2c;
|
|
|
|
// LOL
|
|
var PI = Math.PI;
|
|
var math = Math;
|
|
var abs = Math.abs;
|
|
var split = "split";
|
|
var concat = "concat";
|
|
var apply = "apply";
|
|
var has = "hasOwnProperty";
|
|
|
|
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
|
|
// for more information of where this math came from visit:
|
|
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
var _120 = PI * 120 / 180,
|
|
rad = PI / 180 * (+angle || 0),
|
|
res = [],
|
|
xy,
|
|
rotate = cacher(function (x, y, rad) {
|
|
var X = x * math.cos(rad) - y * math.sin(rad),
|
|
Y = x * math.sin(rad) + y * math.cos(rad);
|
|
return {x: X, y: Y};
|
|
});
|
|
if (!recursive) {
|
|
xy = rotate(x1, y1, -rad);
|
|
x1 = xy.x;
|
|
y1 = xy.y;
|
|
xy = rotate(x2, y2, -rad);
|
|
x2 = xy.x;
|
|
y2 = xy.y;
|
|
var cos = math.cos(PI / 180 * angle),
|
|
sin = math.sin(PI / 180 * angle),
|
|
x = (x1 - x2) / 2,
|
|
y = (y1 - y2) / 2;
|
|
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
|
if (h > 1) {
|
|
h = math.sqrt(h);
|
|
rx = h * rx;
|
|
ry = h * ry;
|
|
}
|
|
var rx2 = rx * rx,
|
|
ry2 = ry * ry,
|
|
k = (large_arc_flag == sweep_flag ? -1 : 1) *
|
|
math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
|
|
cx = k * rx * y / ry + (x1 + x2) / 2,
|
|
cy = k * -ry * x / rx + (y1 + y2) / 2,
|
|
f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
|
|
f2 = math.asin(((y2 - cy) / ry).toFixed(9));
|
|
|
|
f1 = x1 < cx ? PI - f1 : f1;
|
|
f2 = x2 < cx ? PI - f2 : f2;
|
|
f1 < 0 && (f1 = PI * 2 + f1);
|
|
f2 < 0 && (f2 = PI * 2 + f2);
|
|
if (sweep_flag && f1 > f2) {
|
|
f1 = f1 - PI * 2;
|
|
}
|
|
if (!sweep_flag && f2 > f1) {
|
|
f2 = f2 - PI * 2;
|
|
}
|
|
} else {
|
|
f1 = recursive[0];
|
|
f2 = recursive[1];
|
|
cx = recursive[2];
|
|
cy = recursive[3];
|
|
}
|
|
var df = f2 - f1;
|
|
if (abs(df) > _120) {
|
|
var f2old = f2,
|
|
x2old = x2,
|
|
y2old = y2;
|
|
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
|
|
x2 = cx + rx * math.cos(f2);
|
|
y2 = cy + ry * math.sin(f2);
|
|
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
|
|
}
|
|
df = f2 - f1;
|
|
var c1 = math.cos(f1),
|
|
s1 = math.sin(f1),
|
|
c2 = math.cos(f2),
|
|
s2 = math.sin(f2),
|
|
t = math.tan(df / 4),
|
|
hx = 4 / 3 * rx * t,
|
|
hy = 4 / 3 * ry * t,
|
|
m1 = [x1, y1],
|
|
m2 = [x1 + hx * s1, y1 - hy * c1],
|
|
m3 = [x2 + hx * s2, y2 - hy * c2],
|
|
m4 = [x2, y2];
|
|
m2[0] = 2 * m1[0] - m2[0];
|
|
m2[1] = 2 * m1[1] - m2[1];
|
|
if (recursive) {
|
|
return [m2, m3, m4][concat](res);
|
|
} else {
|
|
res = [m2, m3, m4][concat](res).join()[split](",");
|
|
var newres = [];
|
|
for (var i = 0, ii = res.length; i < ii; i++) {
|
|
newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
|
|
}
|
|
return newres;
|
|
}
|
|
}
|
|
function cacher(f, scope, postprocessor) {
|
|
function newf() {
|
|
var arg = Array.prototype.slice.call(arguments, 0),
|
|
args = arg.join("\u2400"),
|
|
cache = newf.cache = newf.cache || {},
|
|
count = newf.count = newf.count || [];
|
|
if (cache[has](args)) {
|
|
repush(count, args);
|
|
return postprocessor ? postprocessor(cache[args]) : cache[args];
|
|
}
|
|
count.length >= 1e3 && delete cache[count.shift()];
|
|
count.push(args);
|
|
cache[args] = f[apply](scope, arg);
|
|
return postprocessor ? postprocessor(cache[args]) : cache[args];
|
|
}
|
|
return newf;
|
|
}
|
|
|
|
function repush(array, item) {
|
|
for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
|
|
return array.push(array.splice(i, 1)[0]);
|
|
}
|
|
}
|