2013-07-24 05:51:07 +00:00
|
|
|
Savage.plugin(function (Savage, Element, Paper, glob) {
|
|
|
|
var elproto = Element.prototype,
|
|
|
|
is = Savage.is,
|
|
|
|
clone = Savage._.clone,
|
|
|
|
has = "hasOwnProperty",
|
|
|
|
p2s = /,?([a-z]),?/gi,
|
|
|
|
toFloat = parseFloat,
|
|
|
|
math = Math,
|
|
|
|
mmin = math.min,
|
|
|
|
mmax = math.max,
|
|
|
|
pow = math.pow,
|
|
|
|
abs = math.abs;
|
|
|
|
function paths(ps) {
|
|
|
|
var p = paths.ps = paths.ps || {};
|
|
|
|
if (p[ps]) {
|
|
|
|
p[ps].sleep = 100;
|
|
|
|
} else {
|
|
|
|
p[ps] = {
|
|
|
|
sleep: 100
|
|
|
|
};
|
|
|
|
}
|
|
|
|
setTimeout(function () {
|
|
|
|
for (var key in p) if (p[has](key) && key != ps) {
|
|
|
|
p[key].sleep--;
|
|
|
|
!p[key].sleep && delete p[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return p[ps];
|
|
|
|
}
|
|
|
|
function box(x, y, width, height) {
|
|
|
|
if (x == null) {
|
|
|
|
x = y = width = height = 0;
|
|
|
|
}
|
|
|
|
if (y == null) {
|
|
|
|
y = x.y;
|
|
|
|
width = x.width;
|
|
|
|
height = x.height;
|
|
|
|
x = x.x;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
width: width,
|
|
|
|
w: width,
|
|
|
|
height: height,
|
|
|
|
h: height,
|
|
|
|
x2: x + width,
|
|
|
|
y2: y + height,
|
|
|
|
cx: x + width / 2,
|
|
|
|
cy: y + height / 2,
|
|
|
|
rx: width / 2,
|
|
|
|
ry: height / 2,
|
|
|
|
r1: math.min(width, height) / 2,
|
|
|
|
r2: math.max(width, height) / 2,
|
|
|
|
r: math.sqrt(width * width + height * height) / 2,
|
|
|
|
path: rectPath(x, y, width, height),
|
|
|
|
vb: [x, y, width, height].join(" ")
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function toString() {
|
|
|
|
return this.join(",").replace(p2s, "$1");
|
|
|
|
}
|
|
|
|
function pathClone(pathArray) {
|
|
|
|
var res = clone(pathArray);
|
|
|
|
res.toString = toString;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
|
|
|
|
if (length == null) {
|
|
|
|
return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
|
|
|
|
} else {
|
|
|
|
return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
|
|
|
|
getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function getLengthFactory(istotal, subpath) {
|
|
|
|
return function (path, length, onlystart) {
|
|
|
|
path = path2curve(path);
|
|
|
|
var x, y, p, l, sp = "", subpaths = {}, point,
|
|
|
|
len = 0;
|
|
|
|
for (var i = 0, ii = path.length; i < ii; i++) {
|
|
|
|
p = path[i];
|
|
|
|
if (p[0] == "M") {
|
|
|
|
x = +p[1];
|
|
|
|
y = +p[2];
|
|
|
|
} else {
|
|
|
|
l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
|
|
|
|
if (len + l > length) {
|
|
|
|
if (subpath && !subpaths.start) {
|
|
|
|
point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
|
|
|
|
sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
|
|
|
|
if (onlystart) {return sp;}
|
|
|
|
subpaths.start = sp;
|
|
|
|
sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
|
|
|
|
len += l;
|
|
|
|
x = +p[5];
|
|
|
|
y = +p[6];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!istotal && !subpath) {
|
|
|
|
point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
|
|
|
|
return {x: point.x, y: point.y, alpha: point.alpha};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
len += l;
|
|
|
|
x = +p[5];
|
|
|
|
y = +p[6];
|
|
|
|
}
|
|
|
|
sp += p.shift() + p;
|
|
|
|
}
|
|
|
|
subpaths.end = sp;
|
|
|
|
point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
|
|
|
|
point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
|
|
|
|
return point;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
var getTotalLength = getLengthFactory(1),
|
|
|
|
getPointAtLength = getLengthFactory(),
|
|
|
|
getSubpathsAtLength = getLengthFactory(0, 1);
|
|
|
|
function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
|
|
|
var t1 = 1 - t,
|
|
|
|
t13 = pow(t1, 3),
|
|
|
|
t12 = pow(t1, 2),
|
|
|
|
t2 = t * t,
|
|
|
|
t3 = t2 * t,
|
|
|
|
x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
|
|
|
|
y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
|
|
|
|
mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
|
|
|
|
my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
|
|
|
|
nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
|
|
|
|
ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
|
|
|
|
ax = t1 * p1x + t * c1x,
|
|
|
|
ay = t1 * p1y + t * c1y,
|
|
|
|
cx = t1 * c2x + t * p2x,
|
|
|
|
cy = t1 * c2y + t * p2y,
|
|
|
|
alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
|
|
|
|
(mx > nx || my < ny) && (alpha += 180);
|
|
|
|
return {
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
m: {x: mx, y: my},
|
|
|
|
n: {x: nx, y: ny},
|
|
|
|
start: {x: ax, y: ay},
|
|
|
|
end: {x: cx, y: cy},
|
|
|
|
alpha: alpha
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
|
if (!Savage.is(p1x, "array")) {
|
|
|
|
p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y];
|
|
|
|
}
|
|
|
|
var bbox = curveDim.apply(null, p1x);
|
|
|
|
return box(
|
|
|
|
bbox.min.x,
|
|
|
|
bbox.min.y,
|
|
|
|
bbox.max.x - bbox.min.x,
|
|
|
|
bbox.max.y - bbox.min.y
|
|
|
|
);
|
|
|
|
}
|
|
|
|
function isPointInsideBBox(bbox, x, y) {
|
|
|
|
return x >= bbox.x && x <= bbox.x2 && y >= bbox.y && y <= bbox.y2;
|
|
|
|
}
|
|
|
|
function isBBoxIntersect(bbox1, bbox2) {
|
|
|
|
var i = isPointInsideBBox;
|
|
|
|
return i(bbox2, bbox1.x, bbox1.y)
|
|
|
|
|| i(bbox2, bbox1.x2, bbox1.y)
|
|
|
|
|| i(bbox2, bbox1.x, bbox1.y2)
|
|
|
|
|| i(bbox2, bbox1.x2, bbox1.y2)
|
|
|
|
|| i(bbox1, bbox2.x, bbox2.y)
|
|
|
|
|| i(bbox1, bbox2.x2, bbox2.y)
|
|
|
|
|| i(bbox1, bbox2.x, bbox2.y2)
|
|
|
|
|| i(bbox1, bbox2.x2, bbox2.y2)
|
|
|
|
|| (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x
|
|
|
|
|| bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
|
|
|
|
&& (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y
|
|
|
|
|| bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
|
|
|
|
}
|
|
|
|
function base3(t, p1, p2, p3, p4) {
|
|
|
|
var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
|
|
|
|
t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
|
|
|
|
return t * t2 - 3 * p1 + 3 * p2;
|
|
|
|
}
|
|
|
|
function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
|
|
|
if (z == null) {
|
|
|
|
z = 1;
|
|
|
|
}
|
|
|
|
z = z > 1 ? 1 : z < 0 ? 0 : z;
|
|
|
|
var z2 = z / 2,
|
|
|
|
n = 12,
|
|
|
|
Tvalues = [-0.1252,0.1252,-0.3678,0.3678,-0.5873,0.5873,-0.7699,0.7699,-0.9041,0.9041,-0.9816,0.9816],
|
|
|
|
Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472],
|
|
|
|
sum = 0;
|
|
|
|
for (var i = 0; i < n; i++) {
|
|
|
|
var ct = z2 * Tvalues[i] + z2,
|
|
|
|
xbase = base3(ct, x1, x2, x3, x4),
|
|
|
|
ybase = base3(ct, y1, y2, y3, y4),
|
|
|
|
comb = xbase * xbase + ybase * ybase;
|
|
|
|
sum += Cvalues[i] * math.sqrt(comb);
|
|
|
|
}
|
|
|
|
return z2 * sum;
|
|
|
|
}
|
|
|
|
function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
|
|
|
|
if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var t = 1,
|
|
|
|
step = t / 2,
|
|
|
|
t2 = t - step,
|
|
|
|
l,
|
|
|
|
e = .01;
|
|
|
|
l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
|
|
|
|
while (abs(l - ll) > e) {
|
|
|
|
step /= 2;
|
|
|
|
t2 += (l < ll ? 1 : -1) * step;
|
|
|
|
l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
|
|
|
|
}
|
|
|
|
return t2;
|
|
|
|
}
|
|
|
|
function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
|
|
|
|
if (
|
|
|
|
mmax(x1, x2) < mmin(x3, x4) ||
|
|
|
|
mmin(x1, x2) > mmax(x3, x4) ||
|
|
|
|
mmax(y1, y2) < mmin(y3, y4) ||
|
|
|
|
mmin(y1, y2) > mmax(y3, y4)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
|
|
|
|
ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
|
|
|
|
denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
|
|
|
|
|
|
if (!denominator) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var px = nx / denominator,
|
|
|
|
py = ny / denominator,
|
|
|
|
px2 = +px.toFixed(2),
|
|
|
|
py2 = +py.toFixed(2);
|
|
|
|
if (
|
|
|
|
px2 < +mmin(x1, x2).toFixed(2) ||
|
|
|
|
px2 > +mmax(x1, x2).toFixed(2) ||
|
|
|
|
px2 < +mmin(x3, x4).toFixed(2) ||
|
|
|
|
px2 > +mmax(x3, x4).toFixed(2) ||
|
|
|
|
py2 < +mmin(y1, y2).toFixed(2) ||
|
|
|
|
py2 > +mmax(y1, y2).toFixed(2) ||
|
|
|
|
py2 < +mmin(y3, y4).toFixed(2) ||
|
|
|
|
py2 > +mmax(y3, y4).toFixed(2)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return {x: px, y: py};
|
|
|
|
}
|
|
|
|
function inter(bez1, bez2) {
|
|
|
|
return interHelper(bez1, bez2);
|
|
|
|
}
|
|
|
|
function interCount(bez1, bez2) {
|
|
|
|
return interHelper(bez1, bez2, 1);
|
|
|
|
}
|
|
|
|
function interHelper(bez1, bez2, justCount) {
|
|
|
|
var bbox1 = bezierBBox(bez1),
|
|
|
|
bbox2 = bezierBBox(bez2);
|
|
|
|
if (!isBBoxIntersect(bbox1, bbox2)) {
|
|
|
|
return justCount ? 0 : [];
|
|
|
|
}
|
|
|
|
var l1 = bezlen.apply(0, bez1),
|
|
|
|
l2 = bezlen.apply(0, bez2),
|
|
|
|
n1 = ~~(l1 / 5),
|
|
|
|
n2 = ~~(l2 / 5),
|
|
|
|
dots1 = [],
|
|
|
|
dots2 = [],
|
|
|
|
xy = {},
|
|
|
|
res = justCount ? 0 : [];
|
|
|
|
for (var i = 0; i < n1 + 1; i++) {
|
|
|
|
var p = findDotsAtSegment.apply(0, bez1.concat(i / n1));
|
|
|
|
dots1.push({x: p.x, y: p.y, t: i / n1});
|
|
|
|
}
|
|
|
|
for (i = 0; i < n2 + 1; i++) {
|
|
|
|
p = findDotsAtSegment.apply(0, bez2.concat(i / n2));
|
|
|
|
dots2.push({x: p.x, y: p.y, t: i / n2});
|
|
|
|
}
|
|
|
|
for (i = 0; i < n1; i++) {
|
|
|
|
for (var j = 0; j < n2; j++) {
|
|
|
|
var di = dots1[i],
|
|
|
|
di1 = dots1[i + 1],
|
|
|
|
dj = dots2[j],
|
|
|
|
dj1 = dots2[j + 1],
|
|
|
|
ci = abs(di1.x - di.x) < .001 ? "y" : "x",
|
|
|
|
cj = abs(dj1.x - dj.x) < .001 ? "y" : "x",
|
|
|
|
is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y);
|
|
|
|
if (is) {
|
|
|
|
if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
xy[is.x.toFixed(4)] = is.y.toFixed(4);
|
|
|
|
var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
|
|
|
|
t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
|
|
|
|
if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
|
|
|
|
if (justCount) {
|
|
|
|
res++;
|
|
|
|
} else {
|
|
|
|
res.push({
|
|
|
|
x: is.x,
|
|
|
|
y: is.y,
|
|
|
|
t1: t1,
|
|
|
|
t2: t2
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function pathIntersection(path1, path2) {
|
|
|
|
return interPathHelper(path1, path2);
|
|
|
|
}
|
|
|
|
function pathIntersectionNumber(path1, path2) {
|
|
|
|
return interPathHelper(path1, path2, 1);
|
|
|
|
}
|
|
|
|
function interPathHelper(path1, path2, justCount) {
|
|
|
|
path1 = path2curve(path1);
|
|
|
|
path2 = path2curve(path2);
|
|
|
|
var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
|
|
|
|
res = justCount ? 0 : [];
|
|
|
|
for (var i = 0, ii = path1.length; i < ii; i++) {
|
|
|
|
var pi = path1[i];
|
|
|
|
if (pi[0] == "M") {
|
|
|
|
x1 = x1m = pi[1];
|
|
|
|
y1 = y1m = pi[2];
|
|
|
|
} else {
|
|
|
|
if (pi[0] == "C") {
|
|
|
|
bez1 = [x1, y1].concat(pi.slice(1));
|
|
|
|
x1 = bez1[6];
|
|
|
|
y1 = bez1[7];
|
|
|
|
} else {
|
|
|
|
bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
|
|
|
|
x1 = x1m;
|
|
|
|
y1 = y1m;
|
|
|
|
}
|
|
|
|
for (var j = 0, jj = path2.length; j < jj; j++) {
|
|
|
|
var pj = path2[j];
|
|
|
|
if (pj[0] == "M") {
|
|
|
|
x2 = x2m = pj[1];
|
|
|
|
y2 = y2m = pj[2];
|
|
|
|
} else {
|
|
|
|
if (pj[0] == "C") {
|
|
|
|
bez2 = [x2, y2].concat(pj.slice(1));
|
|
|
|
x2 = bez2[6];
|
|
|
|
y2 = bez2[7];
|
|
|
|
} else {
|
|
|
|
bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
|
|
|
|
x2 = x2m;
|
|
|
|
y2 = y2m;
|
|
|
|
}
|
|
|
|
var intr = interHelper(bez1, bez2, justCount);
|
|
|
|
if (justCount) {
|
|
|
|
res += intr;
|
|
|
|
} else {
|
|
|
|
for (var k = 0, kk = intr.length; k < kk; k++) {
|
|
|
|
intr[k].segment1 = i;
|
|
|
|
intr[k].segment2 = j;
|
|
|
|
intr[k].bez1 = bez1;
|
|
|
|
intr[k].bez2 = bez2;
|
|
|
|
}
|
|
|
|
res = res.concat(intr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function isPointInsidePath(path, x, y) {
|
|
|
|
var bbox = pathBBox(path);
|
|
|
|
return isPointInsideBBox(bbox, x, y) &&
|
|
|
|
interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1;
|
|
|
|
}
|
|
|
|
function pathBBox(path) {
|
|
|
|
var pth = paths(path);
|
|
|
|
if (pth.bbox) {
|
|
|
|
return clone(pth.bbox);
|
|
|
|
}
|
|
|
|
if (!path) {
|
|
|
|
return box();
|
|
|
|
}
|
|
|
|
path = path2curve(path);
|
|
|
|
var x = 0,
|
|
|
|
y = 0,
|
|
|
|
X = [],
|
|
|
|
Y = [],
|
|
|
|
p;
|
|
|
|
for (var i = 0, ii = path.length; i < ii; i++) {
|
|
|
|
p = path[i];
|
|
|
|
if (p[0] == "M") {
|
|
|
|
x = p[1];
|
|
|
|
y = p[2];
|
|
|
|
X.push(x);
|
|
|
|
Y.push(y);
|
|
|
|
} else {
|
|
|
|
var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
|
|
|
|
X = X.concat(dim.min.x, dim.max.x);
|
|
|
|
Y = Y.concat(dim.min.y, dim.max.y);
|
|
|
|
x = p[5];
|
|
|
|
y = p[6];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var xmin = mmin.apply(0, X),
|
|
|
|
ymin = mmin.apply(0, Y),
|
|
|
|
xmax = mmax.apply(0, X),
|
|
|
|
ymax = mmax.apply(0, Y),
|
|
|
|
bb = box(xmin, ymin, xmax - xmin, ymax - ymin);
|
|
|
|
pth.bbox = clone(bb);
|
|
|
|
return bb;
|
|
|
|
}
|
|
|
|
function rectPath(x, y, w, h, r) {
|
|
|
|
if (r) {
|
|
|
|
return [
|
|
|
|
["M", x + r, y],
|
|
|
|
["l", w - r * 2, 0],
|
|
|
|
["a", r, r, 0, 0, 1, r, r],
|
|
|
|
["l", 0, h - r * 2],
|
|
|
|
["a", r, r, 0, 0, 1, -r, r],
|
|
|
|
["l", r * 2 - w, 0],
|
|
|
|
["a", r, r, 0, 0, 1, -r, -r],
|
|
|
|
["l", 0, r * 2 - h],
|
|
|
|
["a", r, r, 0, 0, 1, r, -r],
|
|
|
|
["z"]
|
|
|
|
];
|
|
|
|
}
|
|
|
|
var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
|
|
|
|
res.toString = toString;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function ellipsePath(x, y, rx, ry, a) {
|
|
|
|
if (a == null && ry == null) {
|
|
|
|
ry = rx;
|
|
|
|
}
|
|
|
|
if (a != null) {
|
|
|
|
var rad = Math.PI / 180,
|
|
|
|
x1 = x + rx * Math.cos(-ry * rad),
|
|
|
|
x2 = x + rx * Math.cos(-a * rad),
|
|
|
|
y1 = y + rx * Math.sin(-ry * rad),
|
|
|
|
y2 = y + rx * Math.sin(-a * rad),
|
|
|
|
res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
|
|
|
|
} else {
|
|
|
|
res = [
|
|
|
|
["M", x, y],
|
|
|
|
["m", 0, -ry],
|
|
|
|
["a", rx, ry, 0, 1, 1, 0, 2 * ry],
|
|
|
|
["a", rx, ry, 0, 1, 1, 0, -2 * ry],
|
|
|
|
["z"]
|
|
|
|
];
|
|
|
|
}
|
|
|
|
res.toString = toString;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
var getPath = {
|
|
|
|
path: function (el) {
|
|
|
|
return el.attr("path");
|
|
|
|
},
|
|
|
|
circle: function (el) {
|
|
|
|
var attr = unit2px(el);
|
|
|
|
return ellipsePath(attr.cx, attr.cy, attr.r);
|
|
|
|
},
|
|
|
|
ellipse: function (el) {
|
|
|
|
var attr = unit2px(el);
|
|
|
|
return ellipsePath(attr.cx, attr.cy, attr.rx, attr.ry);
|
|
|
|
},
|
|
|
|
rect: function (el) {
|
|
|
|
var attr = unit2px(el);
|
|
|
|
return rectPath(attr.x, attr.y, attr.width, attr.height, attr.rx, attr.ry);
|
|
|
|
},
|
|
|
|
image: function (el) {
|
|
|
|
var attr = unit2px(el);
|
|
|
|
return rectPath(attr.x, attr.y, attr.width, attr.height);
|
|
|
|
},
|
|
|
|
text: function (el) {
|
|
|
|
var bbox = el.node.getBBox();
|
|
|
|
return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
|
|
|
|
},
|
|
|
|
g: function (el) {
|
|
|
|
var bbox = el.node.getBBox();
|
|
|
|
return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
|
|
|
|
},
|
|
|
|
symbol: function (el) {
|
|
|
|
var bbox = el.getBBox();
|
|
|
|
return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function pathToRelative(pathArray) {
|
|
|
|
var pth = paths(pathArray);
|
|
|
|
if (pth.rel) {
|
|
|
|
return pathClone(pth.rel);
|
|
|
|
}
|
|
|
|
if (!Savage.is(pathArray, "array") || !Savage.is(pathArray && pathArray[0], "array")) {
|
|
|
|
pathArray = Savage.parsePathString(pathArray);
|
|
|
|
}
|
|
|
|
var res = [],
|
|
|
|
x = 0,
|
|
|
|
y = 0,
|
|
|
|
mx = 0,
|
|
|
|
my = 0,
|
|
|
|
start = 0;
|
|
|
|
if (pathArray[0][0] == "M") {
|
|
|
|
x = pathArray[0][1];
|
|
|
|
y = pathArray[0][2];
|
|
|
|
mx = x;
|
|
|
|
my = y;
|
|
|
|
start++;
|
|
|
|
res.push(["M", x, y]);
|
|
|
|
}
|
|
|
|
for (var i = start, ii = pathArray.length; i < ii; i++) {
|
|
|
|
var r = res[i] = [],
|
|
|
|
pa = pathArray[i];
|
|
|
|
if (pa[0] != lowerCase.call(pa[0])) {
|
|
|
|
r[0] = lowerCase.call(pa[0]);
|
|
|
|
switch (r[0]) {
|
|
|
|
case "a":
|
|
|
|
r[1] = pa[1];
|
|
|
|
r[2] = pa[2];
|
|
|
|
r[3] = pa[3];
|
|
|
|
r[4] = pa[4];
|
|
|
|
r[5] = pa[5];
|
|
|
|
r[6] = +(pa[6] - x).toFixed(3);
|
|
|
|
r[7] = +(pa[7] - y).toFixed(3);
|
|
|
|
break;
|
|
|
|
case "v":
|
|
|
|
r[1] = +(pa[1] - y).toFixed(3);
|
|
|
|
break;
|
|
|
|
case "m":
|
|
|
|
mx = pa[1];
|
|
|
|
my = pa[2];
|
|
|
|
default:
|
|
|
|
for (var j = 1, jj = pa.length; j < jj; j++) {
|
|
|
|
r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r = res[i] = [];
|
|
|
|
if (pa[0] == "m") {
|
|
|
|
mx = pa[1] + x;
|
|
|
|
my = pa[2] + y;
|
|
|
|
}
|
|
|
|
for (var k = 0, kk = pa.length; k < kk; k++) {
|
|
|
|
res[i][k] = pa[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var len = res[i].length;
|
|
|
|
switch (res[i][0]) {
|
|
|
|
case "z":
|
|
|
|
x = mx;
|
|
|
|
y = my;
|
|
|
|
break;
|
|
|
|
case "h":
|
|
|
|
x += +res[i][len - 1];
|
|
|
|
break;
|
|
|
|
case "v":
|
|
|
|
y += +res[i][len - 1];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
x += +res[i][len - 2];
|
|
|
|
y += +res[i][len - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res.toString = toString;
|
|
|
|
pth.rel = pathClone(res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function pathToAbsolute(pathArray) {
|
|
|
|
var pth = paths(pathArray);
|
|
|
|
if (pth.abs) {
|
|
|
|
return pathClone(pth.abs);
|
|
|
|
}
|
|
|
|
if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption
|
|
|
|
pathArray = Savage.parsePathString(pathArray);
|
|
|
|
}
|
|
|
|
if (!pathArray || !pathArray.length) {
|
|
|
|
return [["M", 0, 0]];
|
|
|
|
}
|
|
|
|
var res = [],
|
|
|
|
x = 0,
|
|
|
|
y = 0,
|
|
|
|
mx = 0,
|
|
|
|
my = 0,
|
|
|
|
start = 0,
|
|
|
|
pa0;
|
|
|
|
if (pathArray[0][0] == "M") {
|
|
|
|
x = +pathArray[0][1];
|
|
|
|
y = +pathArray[0][2];
|
|
|
|
mx = x;
|
|
|
|
my = y;
|
|
|
|
start++;
|
|
|
|
res[0] = ["M", x, y];
|
|
|
|
}
|
|
|
|
var crz = pathArray.length == 3 &&
|
|
|
|
pathArray[0][0] == "M" &&
|
|
|
|
pathArray[1][0].toUpperCase() == "R" &&
|
|
|
|
pathArray[2][0].toUpperCase() == "Z";
|
|
|
|
for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
|
|
|
|
res.push(r = []);
|
|
|
|
pa = pathArray[i];
|
|
|
|
pa0 = pa[0];
|
|
|
|
if (pa0 != pa0.toUpperCase()) {
|
|
|
|
r[0] = pa0.toUpperCase();
|
|
|
|
switch (r[0]) {
|
|
|
|
case "A":
|
|
|
|
r[1] = pa[1];
|
|
|
|
r[2] = pa[2];
|
|
|
|
r[3] = pa[3];
|
|
|
|
r[4] = pa[4];
|
|
|
|
r[5] = pa[5];
|
|
|
|
r[6] = +(pa[6] + x);
|
|
|
|
r[7] = +(pa[7] + y);
|
|
|
|
break;
|
|
|
|
case "V":
|
|
|
|
r[1] = +pa[1] + y;
|
|
|
|
break;
|
|
|
|
case "H":
|
|
|
|
r[1] = +pa[1] + x;
|
|
|
|
break;
|
|
|
|
case "R":
|
|
|
|
var dots = [x, y].concat(pa.slice(1));
|
|
|
|
for (var j = 2, jj = dots.length; j < jj; j++) {
|
|
|
|
dots[j] = +dots[j] + x;
|
|
|
|
dots[++j] = +dots[j] + y;
|
|
|
|
}
|
|
|
|
res.pop();
|
|
|
|
res = res.concat(catmullRom2bezier(dots, crz));
|
|
|
|
break;
|
|
|
|
case "O":
|
|
|
|
res.pop();
|
|
|
|
dots = ellipsePath(x, y, pa[1], pa[2]);
|
|
|
|
dots.push(dots[0]);
|
|
|
|
res = res.concat(dots);
|
|
|
|
break;
|
|
|
|
case "U":
|
|
|
|
res.pop();
|
|
|
|
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
|
|
|
|
r = ["U"].concat(res[res.length - 1].slice(-2));
|
|
|
|
break;
|
|
|
|
case "M":
|
|
|
|
mx = +pa[1] + x;
|
|
|
|
my = +pa[2] + y;
|
|
|
|
default:
|
|
|
|
for (j = 1, jj = pa.length; j < jj; j++) {
|
|
|
|
r[j] = +pa[j] + ((j % 2) ? x : y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (pa0 == "R") {
|
|
|
|
dots = [x, y].concat(pa.slice(1));
|
|
|
|
res.pop();
|
|
|
|
res = res.concat(catmullRom2bezier(dots, crz));
|
|
|
|
r = ["R"].concat(pa.slice(-2));
|
|
|
|
} else if (pa0 == "O") {
|
|
|
|
res.pop();
|
|
|
|
dots = ellipsePath(x, y, pa[1], pa[2]);
|
|
|
|
dots.push(dots[0]);
|
|
|
|
res = res.concat(dots);
|
|
|
|
} else if (pa0 == "U") {
|
|
|
|
res.pop();
|
|
|
|
res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
|
|
|
|
r = ["U"].concat(res[res.length - 1].slice(-2));
|
|
|
|
} else {
|
|
|
|
for (var k = 0, kk = pa.length; k < kk; k++) {
|
|
|
|
r[k] = pa[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pa0 = pa0.toUpperCase();
|
|
|
|
if (pa0 != "O") {
|
|
|
|
switch (r[0]) {
|
|
|
|
case "Z":
|
|
|
|
x = mx;
|
|
|
|
y = my;
|
|
|
|
break;
|
|
|
|
case "H":
|
|
|
|
x = r[1];
|
|
|
|
break;
|
|
|
|
case "V":
|
|
|
|
y = r[1];
|
|
|
|
break;
|
|
|
|
case "M":
|
|
|
|
mx = r[r.length - 2];
|
|
|
|
my = r[r.length - 1];
|
|
|
|
default:
|
|
|
|
x = r[r.length - 2];
|
|
|
|
y = r[r.length - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res.toString = toString;
|
|
|
|
pth.abs = pathClone(res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
function l2c(x1, y1, x2, y2) {
|
|
|
|
return [x1, y1, x2, y2, x2, y2];
|
|
|
|
}
|
|
|
|
function q2c(x1, y1, ax, ay, x2, y2) {
|
|
|
|
var _13 = 1 / 3,
|
|
|
|
_23 = 2 / 3;
|
|
|
|
return [
|
|
|
|
_13 * x1 + _23 * ax,
|
|
|
|
_13 * y1 + _23 * ay,
|
|
|
|
_13 * x2 + _23 * ax,
|
|
|
|
_13 * y2 + _23 * ay,
|
|
|
|
x2,
|
|
|
|
y2
|
|
|
|
];
|
|
|
|
}
|
|
|
|
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 findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
|
|
|
var t1 = 1 - t;
|
|
|
|
return {
|
|
|
|
x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
|
|
|
|
y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function curveDim(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
|
var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
|
|
|
|
b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
|
|
|
|
c = p1x - c1x,
|
|
|
|
t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
|
|
|
|
t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
|
|
|
|
y = [p1y, p2y],
|
|
|
|
x = [p1x, p2x],
|
|
|
|
dot;
|
|
|
|
abs(t1) > "1e12" && (t1 = .5);
|
|
|
|
abs(t2) > "1e12" && (t2 = .5);
|
|
|
|
if (t1 > 0 && t1 < 1) {
|
|
|
|
dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
|
|
|
|
x.push(dot.x);
|
|
|
|
y.push(dot.y);
|
|
|
|
}
|
|
|
|
if (t2 > 0 && t2 < 1) {
|
|
|
|
dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
|
|
|
|
x.push(dot.x);
|
|
|
|
y.push(dot.y);
|
|
|
|
}
|
|
|
|
a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
|
|
|
|
b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
|
|
|
|
c = p1y - c1y;
|
|
|
|
t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
|
|
t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
|
|
abs(t1) > "1e12" && (t1 = .5);
|
|
|
|
abs(t2) > "1e12" && (t2 = .5);
|
|
|
|
if (t1 > 0 && t1 < 1) {
|
|
|
|
dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
|
|
|
|
x.push(dot.x);
|
|
|
|
y.push(dot.y);
|
|
|
|
}
|
|
|
|
if (t2 > 0 && t2 < 1) {
|
|
|
|
dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
|
|
|
|
x.push(dot.x);
|
|
|
|
y.push(dot.y);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
min: {x: mmin.apply(0, x), y: mmin.apply(0, y)},
|
|
|
|
max: {x: mmax.apply(0, x), y: mmax.apply(0, y)}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function path2curve(path, path2) {
|
|
|
|
var pth = !path2 && paths(path);
|
|
|
|
if (!path2 && pth.curve) {
|
|
|
|
return pathClone(pth.curve);
|
|
|
|
}
|
|
|
|
var p = pathToAbsolute(path),
|
|
|
|
p2 = path2 && pathToAbsolute(path2),
|
|
|
|
attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
|
|
|
|
attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
|
|
|
|
processPath = function (path, d) {
|
|
|
|
var nx, ny;
|
|
|
|
if (!path) {
|
|
|
|
return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
|
|
|
|
}
|
|
|
|
!(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
|
|
|
|
switch (path[0]) {
|
|
|
|
case "M":
|
|
|
|
d.X = path[1];
|
|
|
|
d.Y = path[2];
|
|
|
|
break;
|
|
|
|
case "A":
|
|
|
|
path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
|
|
|
|
break;
|
|
|
|
case "S":
|
|
|
|
nx = d.x + (d.x - (d.bx || d.x));
|
|
|
|
ny = d.y + (d.y - (d.by || d.y));
|
|
|
|
path = ["C", nx, ny].concat(path.slice(1));
|
|
|
|
break;
|
|
|
|
case "T":
|
|
|
|
d.qx = d.x + (d.x - (d.qx || d.x));
|
|
|
|
d.qy = d.y + (d.y - (d.qy || d.y));
|
|
|
|
path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
|
|
|
|
break;
|
|
|
|
case "Q":
|
|
|
|
d.qx = path[1];
|
|
|
|
d.qy = path[2];
|
|
|
|
path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
|
|
|
|
break;
|
|
|
|
case "L":
|
|
|
|
path = ["C"].concat(l2c(d.x, d.y, path[1], path[2]));
|
|
|
|
break;
|
|
|
|
case "H":
|
|
|
|
path = ["C"].concat(l2c(d.x, d.y, path[1], d.y));
|
|
|
|
break;
|
|
|
|
case "V":
|
|
|
|
path = ["C"].concat(l2c(d.x, d.y, d.x, path[1]));
|
|
|
|
break;
|
|
|
|
case "Z":
|
|
|
|
path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
},
|
|
|
|
fixArc = function (pp, i) {
|
|
|
|
if (pp[i].length > 7) {
|
|
|
|
pp[i].shift();
|
|
|
|
var pi = pp[i];
|
|
|
|
while (pi.length) {
|
|
|
|
pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
|
|
|
|
}
|
|
|
|
pp.splice(i, 1);
|
|
|
|
ii = mmax(p.length, p2 && p2.length || 0);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
fixM = function (path1, path2, a1, a2, i) {
|
|
|
|
if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
|
|
|
|
path2.splice(i, 0, ["M", a2.x, a2.y]);
|
|
|
|
a1.bx = 0;
|
|
|
|
a1.by = 0;
|
|
|
|
a1.x = path1[i][1];
|
|
|
|
a1.y = path1[i][2];
|
|
|
|
ii = mmax(p.length, p2 && p2.length || 0);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
|
|
|
|
p[i] = processPath(p[i], attrs);
|
|
|
|
fixArc(p, i);
|
|
|
|
p2 && (p2[i] = processPath(p2[i], attrs2));
|
|
|
|
p2 && fixArc(p2, i);
|
|
|
|
fixM(p, p2, attrs, attrs2, i);
|
|
|
|
fixM(p2, p, attrs2, attrs, i);
|
|
|
|
var seg = p[i],
|
|
|
|
seg2 = p2 && p2[i],
|
|
|
|
seglen = seg.length,
|
|
|
|
seg2len = p2 && seg2.length;
|
|
|
|
attrs.x = seg[seglen - 2];
|
|
|
|
attrs.y = seg[seglen - 1];
|
|
|
|
attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
|
|
|
|
attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
|
|
|
|
attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
|
|
|
|
attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
|
|
|
|
attrs2.x = p2 && seg2[seg2len - 2];
|
|
|
|
attrs2.y = p2 && seg2[seg2len - 1];
|
|
|
|
}
|
|
|
|
if (!p2) {
|
|
|
|
pth.curve = pathClone(p);
|
|
|
|
}
|
|
|
|
return p2 ? [p, p2] : p;
|
|
|
|
}
|
|
|
|
function mapPath(path, matrix) {
|
|
|
|
if (!matrix) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
var x, y, i, j, ii, jj, pathi;
|
|
|
|
path = path2curve(path);
|
|
|
|
for (i = 0, ii = path.length; i < ii; i++) {
|
|
|
|
pathi = path[i];
|
|
|
|
for (j = 1, jj = pathi.length; j < jj; j += 2) {
|
|
|
|
x = matrix.x(pathi[j], pathi[j + 1]);
|
|
|
|
y = matrix.y(pathi[j], pathi[j + 1]);
|
|
|
|
pathi[j] = x;
|
|
|
|
pathi[j + 1] = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
// http://schepers.cc/getting-to-the-point
|
|
|
|
function catmullRom2bezier(crp, z) {
|
|
|
|
var d = [];
|
|
|
|
for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
|
|
|
|
var p = [
|
|
|
|
{x: +crp[i - 2], y: +crp[i - 1]},
|
|
|
|
{x: +crp[i], y: +crp[i + 1]},
|
|
|
|
{x: +crp[i + 2], y: +crp[i + 3]},
|
|
|
|
{x: +crp[i + 4], y: +crp[i + 5]}
|
|
|
|
];
|
|
|
|
if (z) {
|
|
|
|
if (!i) {
|
|
|
|
p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
|
|
|
|
} else if (iLen - 4 == i) {
|
|
|
|
p[3] = {x: +crp[0], y: +crp[1]};
|
|
|
|
} else if (iLen - 2 == i) {
|
|
|
|
p[2] = {x: +crp[0], y: +crp[1]};
|
|
|
|
p[3] = {x: +crp[2], y: +crp[3]};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (iLen - 4 == i) {
|
|
|
|
p[3] = p[2];
|
|
|
|
} else if (!i) {
|
|
|
|
p[0] = {x: +crp[i], y: +crp[i + 1]};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d.push(["C",
|
|
|
|
(-p[0].x + 6 * p[1].x + p[2].x) / 6,
|
|
|
|
(-p[0].y + 6 * p[1].y + p[2].y) / 6,
|
|
|
|
(p[1].x + 6 * p[2].x - p[3].x) / 6,
|
|
|
|
(p[1].y + 6*p[2].y - p[3].y) / 6,
|
|
|
|
p[2].x,
|
|
|
|
p[2].y
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
// export
|
|
|
|
Savage.path = paths;
|
|
|
|
|
|
|
|
/*\
|
|
|
|
* Savage.path.getTotalLength
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Returns length of the given path in pixels.
|
|
|
|
**
|
|
|
|
> Parameters
|
|
|
|
**
|
|
|
|
- path (string) SVG path string.
|
|
|
|
**
|
|
|
|
= (number) length.
|
|
|
|
\*/
|
|
|
|
Savage.path.getTotalLength = getTotalLength;
|
|
|
|
/*\
|
|
|
|
* Savage.path.getPointAtLength
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Return coordinates of the point located at the given length on the given path.
|
|
|
|
**
|
|
|
|
> Parameters
|
|
|
|
**
|
|
|
|
- path (string) SVG path string
|
|
|
|
- length (number)
|
|
|
|
**
|
|
|
|
= (object) representation of the point:
|
|
|
|
o {
|
|
|
|
o x: (number) x coordinate
|
|
|
|
o y: (number) y coordinate
|
|
|
|
o alpha: (number) angle of derivative
|
|
|
|
o }
|
|
|
|
\*/
|
|
|
|
Savage.path.getPointAtLength = getPointAtLength;
|
|
|
|
/*\
|
|
|
|
* Savage.path.getSubpath
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Return subpath of a given path from given length to given length.
|
|
|
|
**
|
|
|
|
> Parameters
|
|
|
|
**
|
|
|
|
- path (string) SVG path string
|
|
|
|
- from (number) position of the start of the segment
|
|
|
|
- to (number) position of the end of the segment
|
|
|
|
**
|
|
|
|
= (string) pathstring for the segment
|
|
|
|
\*/
|
|
|
|
Savage.path.getSubpath = function (path, from, to) {
|
|
|
|
if (this.getTotalLength(path) - to < 1e-6) {
|
|
|
|
return getSubpathsAtLength(path, from).end;
|
|
|
|
}
|
|
|
|
var a = getSubpathsAtLength(path, to, 1);
|
|
|
|
return from ? getSubpathsAtLength(a, from).end : a;
|
|
|
|
};
|
|
|
|
/*\
|
|
|
|
* Element.getTotalLength
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Returns length of the path in pixels. Only works for element of “path” type.
|
|
|
|
= (number) length.
|
|
|
|
\*/
|
|
|
|
elproto.getTotalLength = function () {
|
|
|
|
if (this.node.getTotalLength) {
|
|
|
|
return this.node.getTotalLength();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/*\
|
|
|
|
* Element.getPointAtLength
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Return coordinates of the point located at the given length on the given path. Only works for element of “path” type.
|
|
|
|
**
|
|
|
|
> Parameters
|
|
|
|
**
|
|
|
|
- length (number)
|
|
|
|
**
|
|
|
|
= (object) representation of the point:
|
|
|
|
o {
|
|
|
|
o x: (number) x coordinate
|
|
|
|
o y: (number) y coordinate
|
|
|
|
o alpha: (number) angle of derivative
|
|
|
|
o }
|
|
|
|
\*/
|
|
|
|
elproto.getPointAtLength = function (length) {
|
|
|
|
return getPointAtLength(this.attr("d"), length);
|
|
|
|
};
|
|
|
|
/*\
|
|
|
|
* Element.getSubpath
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Return subpath of a given element from given length to given length. Only works for element of “path” type.
|
|
|
|
**
|
|
|
|
> Parameters
|
|
|
|
**
|
|
|
|
- from (number) position of the start of the segment
|
|
|
|
- to (number) position of the end of the segment
|
|
|
|
**
|
|
|
|
= (string) pathstring for the segment
|
|
|
|
\*/
|
|
|
|
elproto.getSubpath = function (from, to) {
|
|
|
|
return getSubpath(this.attr("d"), from, to);
|
|
|
|
};
|
|
|
|
Savage._.box = box;
|
|
|
|
/*\
|
|
|
|
* Savage.findDotsAtSegment
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Find dot coordinates on the given cubic bezier curve at the given t.
|
|
|
|
> Parameters
|
|
|
|
- p1x (number) x of the first point of the curve
|
|
|
|
- p1y (number) y of the first point of the curve
|
|
|
|
- c1x (number) x of the first anchor of the curve
|
|
|
|
- c1y (number) y of the first anchor of the curve
|
|
|
|
- c2x (number) x of the second anchor of the curve
|
|
|
|
- c2y (number) y of the second anchor of the curve
|
|
|
|
- p2x (number) x of the second point of the curve
|
|
|
|
- p2y (number) y of the second point of the curve
|
|
|
|
- t (number) position on the curve (0..1)
|
|
|
|
= (object) point information in format:
|
|
|
|
o {
|
|
|
|
o x: (number) x coordinate of the point
|
|
|
|
o y: (number) y coordinate of the point
|
|
|
|
o m: {
|
|
|
|
o x: (number) x coordinate of the left anchor
|
|
|
|
o y: (number) y coordinate of the left anchor
|
|
|
|
o }
|
|
|
|
o n: {
|
|
|
|
o x: (number) x coordinate of the right anchor
|
|
|
|
o y: (number) y coordinate of the right anchor
|
|
|
|
o }
|
|
|
|
o start: {
|
|
|
|
o x: (number) x coordinate of the start of the curve
|
|
|
|
o y: (number) y coordinate of the start of the curve
|
|
|
|
o }
|
|
|
|
o end: {
|
|
|
|
o x: (number) x coordinate of the end of the curve
|
|
|
|
o y: (number) y coordinate of the end of the curve
|
|
|
|
o }
|
|
|
|
o alpha: (number) angle of the curve derivative at the point
|
|
|
|
o }
|
|
|
|
\*/
|
|
|
|
Savage.path.findDotsAtSegment = findDotsAtSegment;
|
|
|
|
/*\
|
|
|
|
* Savage.path.bezierBBox
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Return bounding box of a given cubic bezier curve
|
|
|
|
> Parameters
|
|
|
|
- p1x (number) x of the first point of the curve
|
|
|
|
- p1y (number) y of the first point of the curve
|
|
|
|
- c1x (number) x of the first anchor of the curve
|
|
|
|
- c1y (number) y of the first anchor of the curve
|
|
|
|
- c2x (number) x of the second anchor of the curve
|
|
|
|
- c2y (number) y of the second anchor of the curve
|
|
|
|
- p2x (number) x of the second point of the curve
|
|
|
|
- p2y (number) y of the second point of the curve
|
|
|
|
* or
|
|
|
|
- bez (array) array of six points for bezier curve
|
|
|
|
= (object) point information in format:
|
|
|
|
o {
|
|
|
|
o min: {
|
|
|
|
o x: (number) x coordinate of the left point
|
|
|
|
o y: (number) y coordinate of the top point
|
|
|
|
o }
|
|
|
|
o max: {
|
|
|
|
o x: (number) x coordinate of the right point
|
|
|
|
o y: (number) y coordinate of the bottom point
|
|
|
|
o }
|
|
|
|
o }
|
|
|
|
\*/
|
|
|
|
Savage.path.bezierBBox = bezierBBox;
|
|
|
|
/*\
|
|
|
|
* Savage.path.isPointInsideBBox
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Returns `true` if given point is inside bounding boxes.
|
|
|
|
> Parameters
|
|
|
|
- bbox (string) bounding box
|
|
|
|
- x (string) x coordinate of the point
|
|
|
|
- y (string) y coordinate of the point
|
|
|
|
= (boolean) `true` if point inside
|
|
|
|
\*/
|
|
|
|
Savage.path.isPointInsideBBox = isPointInsideBBox;
|
|
|
|
/*\
|
|
|
|
* Savage.path.isBBoxIntersect
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Returns `true` if two bounding boxes intersect
|
|
|
|
> Parameters
|
|
|
|
- bbox1 (string) first bounding box
|
|
|
|
- bbox2 (string) second bounding box
|
|
|
|
= (boolean) `true` if they intersect
|
|
|
|
\*/
|
|
|
|
Savage.path.isBBoxIntersect = isBBoxIntersect;
|
|
|
|
/*\
|
|
|
|
* Savage.path.intersection
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Finds intersections of two paths
|
|
|
|
> Parameters
|
|
|
|
- path1 (string) path string
|
|
|
|
- path2 (string) path string
|
|
|
|
= (array) dots of intersection
|
|
|
|
o [
|
|
|
|
o {
|
|
|
|
o x: (number) x coordinate of the point
|
|
|
|
o y: (number) y coordinate of the point
|
|
|
|
o t1: (number) t value for segment of path1
|
|
|
|
o t2: (number) t value for segment of path2
|
|
|
|
o segment1: (number) order number for segment of path1
|
|
|
|
o segment2: (number) order number for segment of path2
|
|
|
|
o bez1: (array) eight coordinates representing beziér curve for the segment of path1
|
|
|
|
o bez2: (array) eight coordinates representing beziér curve for the segment of path2
|
|
|
|
o }
|
|
|
|
o ]
|
|
|
|
\*/
|
|
|
|
Savage.path.intersection = pathIntersection;
|
|
|
|
Savage.path.intersectionNumber = pathIntersectionNumber;
|
|
|
|
/*\
|
|
|
|
* Savage.path.isPointInside
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Returns `true` if given point is inside a given closed path.
|
|
|
|
> Parameters
|
|
|
|
- path (string) path string
|
|
|
|
- x (number) x of the point
|
|
|
|
- y (number) y of the point
|
|
|
|
= (boolean) true, if point is inside the path
|
|
|
|
\*/
|
|
|
|
Savage.path.isPointInside = isPointInsidePath;
|
|
|
|
/*\
|
|
|
|
* Savage.pathBBox
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Return bounding box of a given path
|
|
|
|
> Parameters
|
|
|
|
- path (string) path string
|
|
|
|
= (object) bounding box
|
|
|
|
o {
|
|
|
|
o x: (number) x coordinate of the left top point of the box
|
|
|
|
o y: (number) y coordinate of the left top point of the box
|
|
|
|
o x2: (number) x coordinate of the right bottom point of the box
|
|
|
|
o y2: (number) y coordinate of the right bottom point of the box
|
|
|
|
o width: (number) width of the box
|
|
|
|
o height: (number) height of the box
|
|
|
|
o }
|
|
|
|
\*/
|
|
|
|
Savage.path.getBBox = pathBBox;
|
|
|
|
// TODO add doc
|
|
|
|
Savage.path.get = getPath;
|
2013-07-26 03:47:26 +00:00
|
|
|
/*\
|
|
|
|
* Savage.path.toRelative
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Converts path coordinates into relative values.
|
|
|
|
> Parameters
|
|
|
|
- path (string) path string
|
|
|
|
= (array) path string
|
|
|
|
\*/
|
2013-07-24 05:51:07 +00:00
|
|
|
Savage.path.toRelative = pathToRelative;
|
2013-07-26 03:47:26 +00:00
|
|
|
/*\
|
|
|
|
* Savage.path.toAbsolute
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Converts path coordinates into absolute values.
|
|
|
|
> Parameters
|
|
|
|
- path (string) path string
|
|
|
|
= (array) path string
|
|
|
|
\*/
|
2013-07-24 05:51:07 +00:00
|
|
|
Savage.path.toAbsolute = pathToAbsolute;
|
2013-07-26 03:47:26 +00:00
|
|
|
/*\
|
|
|
|
* Savage.path.toCubic
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Utility method
|
|
|
|
**
|
|
|
|
* Converts path to a new path where all segments are cubic bezier curves.
|
|
|
|
> Parameters
|
|
|
|
- pathString (string|array) path string or array of segments
|
|
|
|
= (array) array of segments.
|
|
|
|
\*/
|
2013-07-24 05:51:07 +00:00
|
|
|
Savage.path.toCubic = path2curve;
|
2013-07-26 03:47:26 +00:00
|
|
|
/*\
|
|
|
|
* Savage.path.map
|
|
|
|
[ method ]
|
|
|
|
**
|
|
|
|
* Transform the path string with given matrix.
|
|
|
|
> Parameters
|
|
|
|
- path (string) path string
|
|
|
|
- matrix (object) see @Matrix
|
|
|
|
= (string) transformed path string
|
|
|
|
\*/
|
2013-07-24 05:51:07 +00:00
|
|
|
Savage.path.map = mapPath;
|
|
|
|
Savage.path.toString = toString;
|
|
|
|
Savage.path.clone = pathClone;
|
|
|
|
});
|