From eee1b0f8c31588a458e7478826cb61bd2af98a21 Mon Sep 17 00:00:00 2001 From: Dmitry Baranovskiy Date: Tue, 18 Aug 2009 10:19:50 +1000 Subject: [PATCH] Refactor path methods and some other stuff --- raphael.js | 782 +++++++++++++++++++++-------------------------------- 1 file changed, 301 insertions(+), 481 deletions(-) diff --git a/raphael.js b/raphael.js index c9ded94..5bd0c35 100644 --- a/raphael.js +++ b/raphael.js @@ -1,5 +1,5 @@ /* - * Raphael 0.8.6 - JavaScript Vector Library + * Raphael 1.0 - JavaScript Vector Library * * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. @@ -21,7 +21,7 @@ window.Raphael = (function () { availableAttrs = {cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0}, availableAnimAttrs = {cx: "number", cy: "number", fill: "colour", "fill-opacity": "number", "font-size": "number", height: "number", opacity: "number", path: "path", r: "number", rotation: "csv", rx: "number", ry: "number", scale: "csv", stroke: "colour", "stroke-opacity": "number", "stroke-width": "number", translation: "csv", width: "number", x: "number", y: "number"}, events = ["click", "dblclick", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup"]; - R.version = "0.8.6"; + R.version = "1.0"; R.type = (window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML"); R.svg = !(R.vml = R.type == "VML"); R.idGenerator = 0; @@ -216,26 +216,94 @@ window.Raphael = (function () { delete this.start; }; // path utilities + var pathMethods = { + absolutely: function () { + this.isAbsolute = true; + return this; + }, + relatively: function () { + this.isAbsolute = false; + return this; + }, + moveTo: function (x, y) { + var d = this.isAbsolute ? "M" : "m"; + d += +parseFloat(x).toFixed(3) + " " + (+parseFloat(y).toFixed(3)) + " "; + this.attr({path: this.attrs.path + d}); + return this; + }, + lineTo: function (x, y) { + var d = this.isAbsolute ? "L" : "l"; + d += +parseFloat(x).toFixed(3) + " " + (+parseFloat(y).toFixed(3)) + " "; + this.attr({path: this.attrs.path + d}); + return this; + }, + arcTo: function (rx, ry, large_arc_flag, sweep_flag, x, y) { + var d = this.isAbsolute ? "A" : "a"; + d += [+parseFloat(rx).toFixed(3), +parseFloat(ry).toFixed(3), 0, large_arc_flag, sweep_flag, +parseFloat(x).toFixed(3), +parseFloat(y).toFixed(3)].join(" "); + this.attr({path: this.attrs.path + d}); + return this; + }, + curveTo: function () { + var args = Array.prototype.splice.call(arguments, 0, arguments.length), + d = [0, 0, 0, 0, "s", 0, "c"][args.length] || ""; + if (this.isAbsolute) { + d = d.toUpperCase(); + } + this.attr({path: this.attrs.path + d + args}); + return this; + }, + qcurveTo: function () { + var d = [0, 1, "t", 3, "q"][arguments.length], + args = Array.prototype.splice.call(arguments, 0, arguments.length); + if (this.isAbsolute) { + d = d.toUpperCase(); + } + this.attr({path: this.attrs.path + d + args}); + return this; + }, + addRoundedCorner: function (r, dir) { + var rollback = this.isAbsolute, + o = this; + if (rollback) { + this.relatively(); + rollback = function () { + o.absolutely(); + }; + } else { + rollback = function () {}; + } + this.arcTo(r, r, 0, {"lu": 1, "rd": 1, "ur": 1, "dl": 1}[dir] || 0, r * (!!(dir.indexOf("r") + 1) * 2 - 1), r * (!!(dir.indexOf("d") + 1) * 2 - 1)); + rollback(); + return this; + }, + andClose: function () { + this.attr({path: this.attrs.path + "z"}); + return this; + }, + getPathArray: function () { + return R.parsePathString(this.attrs.path); + } + }; var pathcache = {}, + path2string = function () { + var res = ""; + for (var i = 0, ii = this.length; i < ii; i++) { + res += this[i][0] + this[i].join(",").substring(2); + } + return res.replace(/,(?=-)/g, ""); + }, pathcount = []; R.parsePathString = function (pathString) { if (pathString in pathcache) { return pathcache[pathString]; } var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}, - data = [], - toString = function () { - var res = ""; - for (var i = 0, ii = this.length; i < ii; i++) { - res += this[i][0] + this[i].join(",").substring(2); - } - return res; - }; - if (pathString.toString.toString() == toString.toString()) { + data = []; + if (pathString.toString == path2string) { data = pathString; } if (!data.length) { - pathString.replace(/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, function (a, b, c) { + (pathString + "").replace(/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, function (a, b, c) { var params = [], name = b.toLowerCase(); c.replace(/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig, function (a, b) { @@ -248,7 +316,7 @@ window.Raphael = (function () { }; } }); - data.toString = toString; + data.toString = path2string; } if (pathcount.length > 20) { delete pathcache[pathcount.unshift()]; @@ -259,9 +327,6 @@ window.Raphael = (function () { }; var pathDimensions = function (path) { var pathArray = path; - if (typeof path == "string") { - pathArray = R.parsePathString(path); - } pathArray = pathToAbsolute(pathArray); var x = [], y = [], @@ -303,64 +368,6 @@ window.Raphael = (function () { }; } }, - addRoundedCorner = function (r, dir) { - var R = .5522 * r, - rollback = this.isAbsolute, - o = this; - if (rollback) { - this.relatively(); - rollback = function () { - o.absolutely(); - }; - } else { - rollback = function () {}; - } - var actions = { - l: function () { - return { - u: function () { - o.curveTo(-R, 0, -r, -(r - R), -r, -r); - }, - d: function () { - o.curveTo(-R, 0, -r, r - R, -r, r); - } - }; - }, - r: function () { - return { - u: function () { - o.curveTo(R, 0, r, -(r - R), r, -r); - }, - d: function () { - o.curveTo(R, 0, r, r - R, r, r); - } - }; - }, - u: function () { - return { - r: function () { - o.curveTo(0, -R, -(R - r), -r, r, -r); - }, - l: function () { - o.curveTo(0, -R, R - r, -r, -r, -r); - } - }; - }, - d: function () { - return { - r: function () { - o.curveTo(0, R, -(R - r), r, r, r); - }, - l: function () { - o.curveTo(0, R, R - r, r, -r, r); - } - }; - } - }; - actions[dir.charAt(0)]()[dir.charAt(1)](); - rollback(); - return o; - }, pathToRelative = function (pathArray) { var res = [], x = 0, @@ -423,13 +430,13 @@ window.Raphael = (function () { return res; }, pathToAbsolute = function (pathArray) { - var res = []; + var res = [], + x = 0, + y = 0, + start = 0; if (typeof pathArray == "string") { pathArray = R.parsePathString(pathArray); } - var x = 0, - y = 0, - start = 0; if (pathArray[0][0] == "M") { x = +pathArray[0][1]; y = +pathArray[0][2]; @@ -485,22 +492,121 @@ window.Raphael = (function () { res.toString = pathArray.toString; return res; }, - pecache = {}, pecount = [], - pathEqualiser = function (path1, path2) { - if ((path1 + path2) in pecache) { - return pecache[path1 + path2]; + l2c = function (x1, y1, x2, y2) { + return [x1, y1, x2, y2, x2, y2]; + }, + q2c = function (x1, y1, ax, ay, x2, y2) { + return [ + 2 / 3 * x1 + 1 / 3 * ax, + 2 / 3 * y1 + 1 / 3 * ay, + 2 / 3 * x1 + 1 / 3 * x2, + 2 / 3 * y1 + 1 / 3 * y2, + x2, + y2 + ]; + }, + a2c = function (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 = Math.PI * 120 / 180, + res = []; + if (!recursive) { + var x = (x1 - x2) / 2, + y = (y1 - y2) / 2, + rx2 = rx * rx, + ry2 = ry * ry, + k = (large_arc_flag == sweep_flag ? -1 : 1) * + Math.sqrt(Math.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), + f2 = Math.asin((y2 - cy) / ry); + + f1 = x1 < cx ? Math.PI - f1 : f1; + f2 = x2 < cx ? Math.PI - f2 : f2; + f1 < 0 && (f1 = Math.PI * 2 + f1); + f2 < 0 && (f2 = Math.PI * 2 + f2); + if (sweep_flag && f1 > f2) { + f1 = f1 - Math.PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - Math.PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + var df = f2 - f1; + if (Math.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 = arguments.callee(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + var c1 = Math.cos(f1), + s1 = Math.sin(f1), + c2 = Math.cos(f2), + s2 = Math.sin(f2), + df = f2 - f1, + 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(","); + for (var i = res.length; i--;) { + res[i] = +res[i]; + } + return res; } - var data = [pathToAbsolute(R.parsePathString(path1)), pathToAbsolute(R.parsePathString(path2))], - attrs = [{x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}, {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}], + }, + findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var x = Math.pow(1 - t, 3) * p1x + Math.pow(1 - t, 2) * 3 * t * c1x + (1 - t) * 3 * t * t * c2x + Math.pow(t, 3) * p2x, + y = Math.pow(1 - t, 3) * p1y + Math.pow(1 - t, 2) * 3 * t * c1y + (1 - t) * 3 * t * t * c2y + Math.pow(t, 3) * p2y, + mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y), + ax = (1 - t) * p1x + t * c1x, + ay = (1 - t) * p1y + t * c1y, + cx = (1 - t) * c2x + t * p2x, + cy = (1 - t) * c2y + t * p2y; + 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}}; + }, + path2curve = function (path, path2) { + path2curve.cache = path2curve.cache || {}; + path2curve.count = path2curve.count || []; + if (path in path2curve.cache) { + return path2curve.cache[path + "&" + path2]; + } + var p = pathToAbsolute(path), + p2 = path2 && pathToAbsolute(path2), + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}, + attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}, processPath = function (path, d) { if (!path) { - return ["U"]; + return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; } switch (path[0]) { case "M": d.X = path[1]; d.Y = path[2]; break; + case "A": + path = ["C"].concat(a2c(d.x, d.y, path[1], path[2], path[3], path[4], path[5], path[6], path[7])); + break; case "S": var nx = d.x + (d.x - (d.bx || d.x)), ny = d.y + (d.y - (d.by || d.y)); @@ -509,83 +615,72 @@ window.Raphael = (function () { case "T": var nx = d.x + (d.x - (d.bx || d.x)), ny = d.y + (d.y - (d.by || d.y)); - path = ["Q", nx, ny, path[1], path[2]]; + path = ["C"].concat(q2c(d.x, d.y, nx, ny, path[1], path[2])); + break; + case "Q": + 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 = ["L", path[1], d.y]; + path = ["C"].concat(l2c(d.x, d.y, path[1], d.y)); break; case "V": - path = ["L", d.x, path[1]]; + path = ["C"].concat(l2c(d.x, d.y, d.x, path[1])); break; case "Z": - path = ["L", d.X, d.Y]; + path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); break; } return path; }, - edgeCases = function (a, b, i) { - if (data[a][i][0] == "M" && data[b][i][0] != "M") { - data[b].splice(i, 0, ["M", attrs[b].x, attrs[b].y]); - attrs[a].bx = data[a][i][data[a][i].length - 4] || 0; - attrs[a].by = data[a][i][data[a][i].length - 3] || 0; - attrs[a].x = data[a][i][data[a][i].length - 2]; - attrs[a].y = data[a][i][data[a][i].length - 1]; - return true; - } else if (data[a][i][0] == "L" && data[b][i][0] == "C") { - data[a][i] = ["C", attrs[a].x, attrs[a].y, data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]]; - } else if (data[a][i][0] == "L" && data[b][i][0] == "Q") { - data[a][i] = ["Q", data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]]; - } else if (data[a][i][0] == "Q" && data[b][i][0] == "C") { - var x = data[b][i][data[b][i].length - 2], - y = data[b][i][data[b][i].length - 1]; - data[b].splice(i + 1, 0, ["Q", x, y, x, y]); - data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]); - i++; - attrs[b].bx = data[b][i][data[b][i].length - 4] || 0; - attrs[b].by = data[b][i][data[b][i].length - 3] || 0; - attrs[b].x = data[b][i][data[b][i].length - 2]; - attrs[b].y = data[b][i][data[b][i].length - 1]; - return true; - } else if (data[a][i][0] == "A" && data[b][i][0] == "C") { - var x = data[b][i][data[b][i].length - 2], - y = data[b][i][data[b][i].length - 1]; - data[b].splice(i + 1, 0, ["A", 0, 0, data[a][i][3], data[a][i][4], data[a][i][5], x, y]); - data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]); - i++; - attrs[b].bx = data[b][i][data[b][i].length - 4] || 0; - attrs[b].by = data[b][i][data[b][i].length - 3] || 0; - attrs[b].x = data[b][i][data[b][i].length - 2]; - attrs[b].y = data[b][i][data[b][i].length - 1]; - return true; - } else if (data[a][i][0] == "U") { - data[a][i][0] = data[b][i][0]; - for (var j = 1, jj = data[b][i].length; j < jj; j++) { - data[a][i][j] = (j % 2) ? attrs[a].x : attrs[a].y; + 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 = Math.max(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 = Math.max(p.length, p2 && p2.length || 0); } - return false; }; - for (var i = 0; i < Math.max(data[0].length, data[1].length); i++) { - data[0][i] = processPath(data[0][i], attrs[0]); - data[1][i] = processPath(data[1][i], attrs[1]); - if (data[0][i][0] != data[1][i][0] && (edgeCases(0, 1, i) || edgeCases(1, 0, i))) { - continue; - } - attrs[0].bx = data[0][i][data[0][i].length - 4] || 0; - attrs[0].by = data[0][i][data[0][i].length - 3] || 0; - attrs[0].x = data[0][i][data[0][i].length - 2]; - attrs[0].y = data[0][i][data[0][i].length - 1]; - attrs[1].bx = data[1][i][data[1][i].length - 4] || 0; - attrs[1].by = data[1][i][data[1][i].length - 3] || 0; - attrs[1].x = data[1][i][data[1][i].length - 2]; - attrs[1].y = data[1][i][data[1][i].length - 1]; - } - if (pecount.length > 20) { - delete pecache[pecount.unshift()]; - } - pecount.push(path1 + path2); - pecache[path1 + path2] = data; - return data; + for (var i = 0, ii = Math.max(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.bx = seg[seglen - 4] || 0; + attrs.by = seg[seglen - 3] || 0; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs2.bx = p2 && (seg2[seg2len - 4] || 0); + attrs2.by = p2 && (seg2[seg2len - 3] || 0); + attrs2.x = p2 && seg2[seg2len - 2]; + attrs2.y = p2 && seg2[seg2len - 1]; + } + if (path2curve.count.length > 100) { + delete path2curve.cache[path2curve.count.unshift()]; + } + path2curve.count.push(path + "&" + path2); + return (path2curve.cache[path + "&" + path2] = p2 ? [p, p2] : p); }, toGradient = function (gradient) { if (typeof gradient == "string") { @@ -701,117 +796,7 @@ window.Raphael = (function () { R.toString = function () { return "Your browser supports SVG.\nYou are running Rapha\u00ebl " + this.version; }; - var pathMethods = { - absolutely: function () { - this.isAbsolute = true; - return this; - }, - relatively: function () { - this.isAbsolute = false; - return this; - }, - moveTo: function (x, y) { - var d = this.isAbsolute ? "M" : "m"; - d += parseFloat(x).toFixed(3) + " " + parseFloat(y).toFixed(3) + " "; - var oldD = this[0].getAttribute("d") || ""; - (oldD == "M0,0") && (oldD = ""); - this[0].setAttribute("d", oldD + d); - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y); - this.attrs.path = oldD + d; - return this; - }, - lineTo: function (x, y) { - this.last.x = (!this.isAbsolute * this.last.x) + parseFloat(x); - this.last.y = (!this.isAbsolute * this.last.y) + parseFloat(y); - var d = this.isAbsolute ? "L" : "l"; - d += parseFloat(x).toFixed(3) + " " + parseFloat(y).toFixed(3) + " "; - var oldD = this.node.getAttribute("d") || ""; - this.node.setAttribute("d", oldD + d); - this.attrs.path = oldD + d; - return this; - }, - arcTo: function (rx, ry, large_arc_flag, sweep_flag, x, y) { - var d = this.isAbsolute ? "A" : "a"; - d += [parseFloat(rx).toFixed(3), parseFloat(ry).toFixed(3), 0, large_arc_flag, sweep_flag, parseFloat(x).toFixed(3), parseFloat(y).toFixed(3)].join(" "); - var oldD = this[0].getAttribute("d") || ""; - this.node.setAttribute("d", oldD + d); - this.last.x = parseFloat(x); - this.last.y = parseFloat(y); - this.attrs.path = oldD + d; - return this; - }, - cplineTo: function (x1, y1, w1) { - if (!w1) { - return this.lineTo(x1, y1); - } else { - var p = {}, - x = parseFloat(x1), - y = parseFloat(y1), - w = parseFloat(w1), - d = this.isAbsolute ? "C" : "c", - attr = [+this.last.x + w, +this.last.y, x - w, y, x, y]; - for (var i = 0, ii = attr.length; i < ii; i++) { - d += attr[i] + " "; - } - this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4]; - this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5]; - this.last.bx = attr[2]; - this.last.by = attr[3]; - var oldD = this.node.getAttribute("d") || ""; - this.node.setAttribute("d", oldD + d); - this.attrs.path = oldD + d; - return this; - } - }, - curveTo: function () { - var p = {}, - d = [0, 1, 2, 3, "s", 5, "c"][arguments.length]; - if (this.isAbsolute) { - d = d.toUpperCase(); - } - for (var i = 0, ii = arguments.length; i < ii; i++) { - d += parseFloat(arguments[i]).toFixed(3) + " "; - } - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2]); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1]); - this.last.bx = parseFloat(arguments[arguments.length - 4]); - this.last.by = parseFloat(arguments[arguments.length - 3]); - var oldD = this.node.getAttribute("d") || ""; - this.node.setAttribute("d", oldD + d); - this.attrs.path = oldD + d; - return this; - }, - qcurveTo: function () { - var p = {}, - d = [0, 1, "t", 3, "q"][arguments.length]; - if (this.isAbsolute) { - d = d.toUpperCase(); - } - for (var i = 0, ii = arguments.length; i < ii; i++) { - d += parseFloat(arguments[i]).toFixed(3) + " "; - } - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2]); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1]); - if (arguments.length != 2) { - this.last.qx = parseFloat(arguments[arguments.length - 4]); - this.last.qy = parseFloat(arguments[arguments.length - 3]); - } - var oldD = this.node.getAttribute("d") || ""; - this.node.setAttribute("d", oldD + d); - this.attrs.path = oldD + d; - return this; - }, - addRoundedCorner: addRoundedCorner, - andClose: function () { - var oldD = this[0].getAttribute("d") || ""; - this[0].setAttribute("d", oldD + "Z "); - this.attrs.path = oldD + "Z "; - return this; - } - }; var thePath = function (params, pathString, SVG) { - params = params || {}; var el = doc.createElementNS(SVG.svgns, "path"); if (SVG.canvas) { SVG.canvas.appendChild(el); @@ -822,12 +807,8 @@ window.Raphael = (function () { p[method] = pathMethods[method]; } p.type = "path"; - p.last = {x: 0, y: 0, bx: 0, by: 0}; - if (pathString) { - p.attrs.path = "" + pathString; - p.absolutely(); - paper.pathfinder(p, p.attrs.path); - } + p.attrs.path = "" + (pathString || ""); + pathString && p.node.setAttribute("d", R.parsePathString(pathString)); if (params) { !params.gradient && (params.fill = params.fill || "none"); params.stroke = params.stroke || "#000"; @@ -894,7 +875,7 @@ window.Raphael = (function () { attrs = o.attrs, rot = attrs.rotation, addDashes = function (o, value) { - value = dasharray[value.toString().toLowerCase()]; + value = dasharray[(value + "").toLowerCase()]; if (value) { var width = o.attrs["stroke-width"] || "1", butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0, @@ -929,8 +910,7 @@ window.Raphael = (function () { break; case "path": if (o.type == "path") { - node.setAttribute("d", "M0,0"); - paper.pathfinder(o, value); + node.setAttribute("d", R.parsePathString(value)); } case "width": node.setAttribute(att, value); @@ -1131,10 +1111,13 @@ window.Raphael = (function () { }; Element.prototype.rotate = function (deg, cx, cy) { if (deg == null) { + if (this._.rt.cx) { + return [this._.rt.deg, this._.rt.cx, this._.rt.cy].join(" "); + } return this._.rt.deg; } var bbox = this.getBBox(); - deg = deg.toString().split(separator); + deg = (deg + "").split(separator); if (deg.length - 1) { cx = parseFloat(deg[1]); cy = parseFloat(deg[2]); @@ -1145,9 +1128,9 @@ window.Raphael = (function () { } else { this._.rt.deg += deg; } - if (cy == null) { - cx = null; - } + (cy == null) && (cx = null); + this._.rt.cx = cx; + this._.rt.cy = cy; cx = cx == null ? bbox.x + bbox.width / 2 : cx; cy = cy == null ? bbox.y + bbox.height / 2 : cy; if (this._.rt.deg) { @@ -1198,6 +1181,12 @@ window.Raphael = (function () { if (arguments[0] == "translation") { return this.translate(); } + if (arguments[0] == "rotation") { + return this.rotate(); + } + if (arguments[0] == "scale") { + return this.scale(); + } return this.attrs[arguments[0]]; } if (arguments.length == 1 && R.isArray(arguments[0])) { @@ -1320,6 +1309,7 @@ window.Raphael = (function () { } var res = new Element(el, svg); res.attrs = res.attrs || {}; + res.attrs.src = src; res.attrs.x = x; res.attrs.y = y; res.attrs.width = w; @@ -1337,6 +1327,7 @@ window.Raphael = (function () { } var res = new Element(el, svg); res.attrs = res.attrs || {}; + res.attrs.text = text; res.attrs.x = x; res.attrs.y = y; res.type = "text"; @@ -1398,7 +1389,7 @@ window.Raphael = (function () { return container; }; paper.remove = function () { - this.canvas.parentNode.removeChild(this.canvas); + this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); }; paper.svgns = "http://www.w3.org/2000/svg"; paper.xlink = "http://www.w3.org/1999/xlink"; @@ -1412,160 +1403,19 @@ window.Raphael = (function () { // VML if (R.vml) { - R.toString = function () { - return "Your browser doesn\u2019t support SVG. Assuming it is Internet Explorer and falling down to VML.\nYou are running Rapha\u00ebl " + this.version; - }; - var pathMethods = { - absolutely: function () { - this.isAbsolute = true; - return this; - }, - relatively: function () { - this.isAbsolute = false; - return this; - }, - moveTo: function (x, y) { - var X = Math.round(parseFloat(x)) - 1, - Y = Math.round(parseFloat(y)) - 1, - d = this.isAbsolute ? "m" : "t"; - d += X + " " + Y; - this.node.path = this.Path += d; - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y); - this.last.isAbsolute = this.isAbsolute; - this.attrs.path += (this.isAbsolute ? "M" : "m") + [x, y]; - return this; - }, - lineTo: function (x, y) { - var X = Math.round(parseFloat(x)) - 1, - Y = Math.round(parseFloat(y)) - 1, - d = this.isAbsolute ? "l" : "r"; - d += X + " " + Y; - this.node.path = this.Path += d; - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y); - this.last.isAbsolute = this.isAbsolute; - this.attrs.path += (this.isAbsolute ? "L" : "l") + [x, y]; - return this; - }, - arcTo: function (rx, ry, large_arc_flag, sweep_flag, x2, y2) { - // for more information of where this math came from visit: - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - var x22 = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x2) - 1, - y22 = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y2) - 1, - x1 = this.last.x - 1, - y1 = this.last.y - 1, - x = (x1 - x22) / 2, - y = (y1 - y22) / 2, - k = (large_arc_flag == sweep_flag ? -1 : 1) * - Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * y * y - ry * ry * x * x) / (rx * rx * y * y + ry * ry * x * x)), - cx = k * rx * y / ry + (x1 + x22) / 2, - cy = k * -ry * x / rx + (y1 + y22) / 2, - d = sweep_flag ? (this.isAbsolute ? "wa" : "wr") : (this.isAbsolute ? "at" : "ar"), - left = Math.round(cx - rx), - top = Math.round(cy - ry); - d += [left, top, Math.round(left + rx * 2), Math.round(top + ry * 2), Math.round(x1), Math.round(y1), Math.round(x22), Math.round(y22)].join(", "); - this.node.path = this.Path += d; - this.last.x = (this.isAbsolute ? 0 : this.last.x) + x2; - this.last.y = (this.isAbsolute ? 0 : this.last.y) + y2; - this.last.isAbsolute = this.isAbsolute; - this.attrs.path += (this.isAbsolute ? "A" : "a") + [rx, ry, 0, large_arc_flag, sweep_flag, x2, y2]; - return this; - }, - cplineTo: function (x1, y1, w1) { - if (!w1) { - return this.lineTo(x1, y1); - } else { - var x = Math.round(parseFloat(x1)) - 1, - y = Math.round(parseFloat(y1)) - 1, - w = Math.round(parseFloat(w1)), - d = this.isAbsolute ? "c" : "v", - attr = [Math.round(this.last.x) - 1 + w, Math.round(this.last.y) - 1, x - w, y, x, y], - svgattr = [this.last.x + w1, this.last.y, x1 - w1, y1, x1, y1]; - d += attr.join(" ") + " "; - this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4]; - this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5]; - this.last.bx = attr[2]; - this.last.by = attr[3]; - this.node.path = this.Path += d; - this.attrs.path += (this.isAbsolute ? "C" : "c") + svgattr; - return this; + var path2vml = function (path) { + var pa = path2curve(path); + for (var i = 0, ii = pa.length; i < ii; i++) { + for (var j = 0, jj = pa[i].length; j < jj; j++) { + pa[i][j] = isNaN(pa[i][j]) ? pa[i][j] : Math.round(pa[i][j]); } - }, - curveTo: function () { - var d = this.isAbsolute ? "c" : "v"; - if (arguments.length == 6) { - this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2]); - this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3]); - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[4]); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[5]); - d += [Math.round(parseFloat(arguments[0])) - 1, - Math.round(parseFloat(arguments[1])) - 1, - Math.round(parseFloat(arguments[2])) - 1, - Math.round(parseFloat(arguments[3])) - 1, - Math.round(parseFloat(arguments[4])) - 1, - Math.round(parseFloat(arguments[5])) - 1].join(" ") + " "; - this.last.isAbsolute = this.isAbsolute; - this.attrs.path += (this.isAbsolute ? "C" : "c") + Array.prototype.splice.call(arguments, 0, arguments.length); - } - if (arguments.length == 4) { - var bx = this.last.x * 2 - this.last.bx, - by = this.last.y * 2 - this.last.by; - this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[0]); - this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[1]); - this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2]); - this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3]); - d += [Math.round(bx) - 1, Math.round(by) - 1, - Math.round(parseFloat(arguments[0])) - 1, - Math.round(parseFloat(arguments[1])) - 1, - Math.round(parseFloat(arguments[2])) - 1, - Math.round(parseFloat(arguments[3])) - 1].join(" ") + " "; - this.attrs.path += (this.isAbsolute ? "S" : "s") + Array.prototype.splice.call(arguments, 0, arguments.length); - } - this.node.path = this.Path += d; - return this; - }, - qcurveTo: function () { - var lx = Math.round(this.last.x) - 1, - ly = Math.round(this.last.y) - 1, - res = []; - if (arguments.length == 4) { - this.last.qx = (!this.isAbsolute * this.last.x) + parseFloat(arguments[0]); - this.last.qy = (!this.isAbsolute * this.last.y) + parseFloat(arguments[1]); - this.last.x = (!this.isAbsolute * this.last.x) + parseFloat(arguments[2]); - this.last.y = (!this.isAbsolute * this.last.y) + parseFloat(arguments[3]); - res = [this.last.qx, this.last.qy, this.last.x, this.last.y]; - this.last.isAbsolute = this.isAbsolute; - this.attrs.path += (this.isAbsolute ? "Q" : "q") + Array.prototype.splice.call(arguments, 0, arguments.length); - } - if (arguments.length == 2) { - this.last.qx = this.last.x * 2 - this.last.qx; - this.last.qy = this.last.y * 2 - this.last.qy; - this.last.x = (!this.isAbsolute * this.last.x) + parseFloat(arguments[2]); - this.last.y = (!this.isAbsolute * this.last.y) + parseFloat(arguments[3]); - res = [this.last.qx, this.last.qy, this.last.x, this.last.y]; - this.attrs.path += (this.isAbsolute ? "T" : "t") + Array.prototype.splice.call(arguments, 0, arguments.length); - } - var d = "c" + [ - Math.round(2 / 3 * res[0] + 1 / 3 * lx) - 1, - Math.round(2 / 3 * res[1] + 1 / 3 * ly) - 1, - Math.round(2 / 3 * res[0] + 1 / 3 * res[2]) - 1, - Math.round(2 / 3 * res[1] + 1 / 3 * res[3]) - 1, - Math.round(res[2]) - 1, - Math.round(res[3]) - 1 - ].join(" ") + " "; - this.node.path = this.Path += d; - return this; - }, - addRoundedCorner: addRoundedCorner, - andClose: function () { - this.node.path = (this.Path += "x"); - this.attrs.path += "z"; - return this; } + return (pa + "").toLowerCase().replace(/z/g, "x"); + }; + R.toString = function () { + return "Your browser doesn\u2019t support SVG. Assuming it is Internet Explorer and falling down to VML.\nYou are running Rapha\u00ebl " + this.version; }; var thePath = function (params, pathString, VML) { - params = params || {}; var g = createNode("group"), gl = g.style; gl.position = "absolute"; gl.left = 0; @@ -1594,17 +1444,16 @@ window.Raphael = (function () { p[method] = pathMethods[method]; } - if (pathString) { - p.absolutely(); - p.attrs.path = ""; - paper.pathfinder(p, "" + pathString); - } if (params) { params.fill = params.fill || "none"; params.stroke = params.stroke || "#000"; } else { params = {fill: "none", stroke: "#000"}; } + if (pathString) { + p.node.path = path2vml(pathString); + p.attrs.path = pathString; + } setFillAndStroke(p, params); if (params.gradient) { addGradientFill(p, params.gradient); @@ -1626,10 +1475,7 @@ window.Raphael = (function () { params.title && (node.title = params.title); params.target && (node.target = params.target); if (params.path && o.type == "path") { - o.Path = ""; - o.path = []; - o.attrs.path = ""; - paper.pathfinder(o, params.path); + o.node.path = path2vml(params.path); } if (params.rotation != null) { o.rotate(params.rotation, true); @@ -1722,7 +1568,8 @@ window.Raphael = (function () { a["font-size"] && (s.fontSize = a["font-size"]); a["font-weight"] && (s.fontWeight = a["font-weight"]); a["font-style"] && (s.fontStyle = a["font-style"]); - paper.span.innerText = res.node.string; + paper.span.innerHTML = ""; + paper.span.appendChild(doc.createTextNode(res.node.string)); res.W = a.w = paper.span.offsetWidth; res.H = a.h = paper.span.offsetHeight; res.X = a.x; @@ -1824,6 +1671,9 @@ window.Raphael = (function () { }; Element.prototype.rotate = function (deg, cx, cy) { if (deg == null) { + if (this._.rt.cx) { + return [this._.rt.deg, this._.rt.cx, this._.rt.cy].join(" "); + } return this._.rt.deg; } deg = (deg + "").split(separator); @@ -1837,9 +1687,7 @@ window.Raphael = (function () { } else { this._.rt.deg += deg; } - if (cy == null) { - cx = null; - } + (cy == null) && (cx = null); this._.rt.cx = cx; this._.rt.cy = cy; this.setBox(this.attrs, cx, cy); @@ -1981,6 +1829,12 @@ window.Raphael = (function () { if (arguments[0] == "translation") { return this.translate(); } + if (arguments[0] == "rotation") { + return this.rotate(); + } + if (arguments[0] == "scale") { + return this.scale(); + } return this.attrs[arguments[0]]; } if (this.attrs && arguments.length == 1 && R.isArray(arguments[0])) { @@ -2117,6 +1971,7 @@ window.Raphael = (function () { g.appendChild(o); var res = new Element(o, g, vml); res.type = "image"; + res.attrs.src = src; res.attrs.x = x; res.attrs.y = y; res.attrs.w = w; @@ -2158,6 +2013,7 @@ window.Raphael = (function () { res.shape = el; res.textpath = path; res.type = "text"; + res.attrs.text = text; res.attrs.x = x; res.attrs.y = y; res.attrs.w = 1; @@ -2335,6 +2191,11 @@ window.Raphael = (function () { return theEllipse(this, x, y, rx, ry); }; paper.path = function (params, pathString) { + if (typeof params == "string" && !pathString) { + pathString = params; + params = {}; + } + params = params || {}; return thePath(params, pathString, this); }; paper.image = function (src, x, y, w, h) { @@ -2356,47 +2217,6 @@ window.Raphael = (function () { } return this.path({stroke: color, "stroke-width": 1}, path.join(",")); }; - paper.pathfinder = function (p, path) { - var commands = { - M: function (x, y) { - this.moveTo(x, y); - }, - C: function (x1, y1, x2, y2, x3, y3) { - this.curveTo(x1, y1, x2, y2, x3, y3); - }, - Q: function (x1, y1, x2, y2) { - this.qcurveTo(x1, y1, x2, y2); - }, - T: function (x, y) { - this.qcurveTo(x, y); - }, - S: function (x1, y1, x2, y2) { - p.curveTo(x1, y1, x2, y2); - }, - L: function (x, y) { - p.lineTo(x, y); - }, - H: function (x) { - this.lineTo(x, this.last.y); - }, - V: function (y) { - this.lineTo(this.last.x, y); - }, - A: function (rx, ry, xaxisrotation, largearcflag, sweepflag, x, y) { - this.arcTo(rx, ry, largearcflag, sweepflag, x, y); - }, - Z: function () { - this.andClose(); - } - }; - - path = pathToAbsolute(path); - for (var i = 0, ii = path.length; i < ii; i++) { - var b = path[i].shift(); - commands[b].apply(p, path[i]); - path[i].unshift(b); - } - }; paper.set = function (itemsArray) { return new Set(itemsArray); }; @@ -2406,7 +2226,7 @@ window.Raphael = (function () { }; Element.prototype.scale = function (x, y, cx, cy) { if (x == null && y == null) { - return {x: this._.sx, y: this._.sy, toString: function () { return this.x.toFixed(3) + " " + this.y.toFixed(3); }}; + return {x: this._.sx, y: this._.sy, toString: function () { return +this.x.toFixed(3) + " " + (+this.y.toFixed(3)); }}; } y = y || x; !+y && (y = x); @@ -2610,7 +2430,7 @@ window.Raphael = (function () { }; break; case "path": - var pathes = pathEqualiser(from[attr], to[attr]); + var pathes = path2curve(from[attr], to[attr]); from[attr] = pathes[0]; to[attr] = pathes[1]; diff[attr] = []; @@ -2668,7 +2488,7 @@ window.Raphael = (function () { for (var i = 0, ii = from[attr].length; i < ii; i++) { now[i] = [from[attr][i][0]]; for (var j = 1, jj = from[attr][i].length; j < jj; j++) { - now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j]; + now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j]; } now[i] = now[i].join(" "); } @@ -2705,7 +2525,7 @@ window.Raphael = (function () { (t.x || t.y) && that.translate(-t.x, -t.y); that.attr(params); clearTimeout(that.animation_in_progress); - paper.safari(); + R.svg && paper.safari(); (typeof callback == "function") && callback.call(that); } prev = time; -- 2.39.2