From 356657146a5df8c293c7030a0866cee61f88b4b6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20T=C3=A9treault?= Date: Tue, 20 Dec 2016 04:09:22 -0500 Subject: [PATCH] Implement the morph method of SVG.PathArray Also add methods to SVG.Point that allow to perform operations between two points. --- dist/svg.js | 650 +++++++++++++++++++++++++++++++- dist/svg.min.js | 5 +- gulpfile.js | 1 + spec/spec/array.js | 91 ++++- spec/spec/makepathsmorphable.js | 225 +++++++++++ spec/spec/point.js | 145 ++++++- src/makepathsmorphable.js | 536 ++++++++++++++++++++++++++ src/patharray.js | 69 +++- src/point.js | 45 ++- 9 files changed, 1745 insertions(+), 22 deletions(-) create mode 100644 spec/spec/makepathsmorphable.js create mode 100644 src/makepathsmorphable.js diff --git a/dist/svg.js b/dist/svg.js index 6fcb165..c693656 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,12 +1,12 @@ /*! * svg.js - A lightweight library for manipulating and animating SVG. -* @version 2.3.6 +* @version 2.3.7 * https://svgdotjs.github.io/ * * @copyright Wout Fierens * @license MIT * -* BUILT: Sat Jan 14 2017 05:53:26 GMT+0100 (CET) +* BUILT: Sat Jan 14 2017 07:22:23 GMT+0100 (CET) */; (function(root, factory) { if (typeof define === 'function' && define.amd) { @@ -705,6 +705,71 @@ SVG.extend(SVG.PathArray, { return this } + // Test if the passed path array use the same commands as this path array +, haveSameCommands: function(pathArray) { + var i, il, haveSameCommands + + pathArray = new SVG.PathArray(pathArray) + + haveSameCommands = this.value.length === pathArray.value.length + for(i = 0, il = this.value.length; haveSameCommands && i < il; i++) { + haveSameCommands = this.value[i][0] === pathArray.value[i][0] + } + + return haveSameCommands + } + // Make path array morphable +, morph: function(pathArray) { + var pathsMorphable + + this.destination = new SVG.PathArray(pathArray) + + if(this.haveSameCommands(this.destination)) { + this.sourceMorphable = this + this.destinationMorphable = this.destination + } else { + pathsMorphable = SVG.utils.makePathsMorphable(this.value, this.destination) + this.sourceMorphable = pathsMorphable[0] + this.destinationMorphable = pathsMorphable[1] + } + + return this + } + // Get morphed path array at given position +, at: function(pos) { + if(pos === 1) { + return this.destination + } else if(pos === 0) { + return this + } else { + var sourceArray = this.sourceMorphable.value + , destinationArray = this.destinationMorphable.value + , array = [], pathArray = new SVG.PathArray() + , i, il, j, jl + + // Animate has specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/paths.html#PathElement + for (i = 0, il = sourceArray.length; i < il; i++) { + array[i] = [sourceArray[i][0]] + for(j=1, jl = sourceArray[i].length; j < jl; j++) { + array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos + } + // For the two flags of the elliptical arc command, the SVG spec say: + // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true + // Elliptical arc command as an array followed by corresponding indexes: + // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + // 0 1 2 3 4 5 6 7 + if(array[i][0] === 'A') { + array[i][4] = +(array[i][4] != 0) + array[i][5] = +(array[i][5] != 0) + } + } + + // Directly modify the value of a path array, this is done this way for performance + pathArray.value = array + return pathArray + } + } // Absolutize and parse path to array , parse: function(array) { // if it's already a patharray, no need to parse it @@ -846,6 +911,7 @@ SVG.extend(SVG.PathArray, { } }) + // Module for unit convertions SVG.Number = SVG.invent({ // Initialize @@ -2484,9 +2550,9 @@ SVG.Point = SVG.invent({ {x:x[0], y:x[1]} : typeof x === 'object' ? {x:x.x, y:x.y} : - y != null ? - {x:x, y:y} : base - + x != null ? + {x:x, y:(y != null ? y : x)} : base // If y has no value, then x is used has its value + // This allow element-wise operations to be passed a single number // merge source this.x = source.x this.y = source.y @@ -2499,9 +2565,9 @@ SVG.Point = SVG.invent({ return new SVG.Point(this) } // Morph one point into another - , morph: function(point) { + , morph: function(x, y) { // store new destination - this.destination = new SVG.Point(point) + this.destination = new SVG.Point(x, y) return this } @@ -2533,7 +2599,38 @@ SVG.Point = SVG.invent({ , transform: function(matrix) { return new SVG.Point(this.native().matrixTransform(matrix.native())) } - + // return an array of the x and y coordinates + , toArray: function() { + return [this.x, this.y] + } + // perform an element-wise addition with the passed point or number + , plus: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x + point.x, this.y + point.y) + } + // perform an element-wise subtraction with the passed point or number + , minus: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x - point.x, this.y - point.y) + } + // perform an element-wise multiplication with the passed point or number + , times: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x * point.x, this.y * point.y) + } + // perform an element-wise division with the passed point or number + , divide: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x / point.x, this.y / point.y) + } + // calculate the Euclidean norm + , norm: function() { + return Math.sqrt(this.x*this.x + this.y*this.y) + } + // calculate the distance to the passed point + , distance: function(x, y) { + return this.minus(x, y).norm() + } } }) @@ -5366,6 +5463,543 @@ function idFromReference(url) { // Create matrix array for looping var abcdef = 'abcdef'.split('') +// Take two path array that don't have the same commands (which mean that they +// cannot be morphed in one another) and return 2 equivalent path array (meaning +// that they produce the same shape as the passed path array) that have the +// same commands (moveto and curveto) +// +// Algorithm used: +// First, convert every path segment of the two passed paths into equivalent cubic Bezier curves. +// Then, calculate the positions relative to the total length of the path of the endpoint of all those cubic Bezier curves. +// After that, split the Bezier curves of the source at the positions that the destination have that are not common to the source and vice versa. +// Finally, make the source and destination have the same number of subpaths. +SVG.utils.makePathsMorphable = function (sourcePathArray, destinationPathArray) { + var source, sourcePositions, sourcePositionsToSplitAt + , destination, destinationPositions, destinationPositionsToSplitAt + , i, il, j, jl + , s, d + , sourceSubpath, destinationSubpath, lastSegPt + + // Convert every path segments into equivalent cubic Bezier curves + source = cubicSuperPath(sourcePathArray) + destination = cubicSuperPath(destinationPathArray) + + // The positions relative to the total length of the path is calculated for the endpoint of all those cubic bezier curves + sourcePositions = cspPositions(source) + destinationPositions = cspPositions(destination) + + // Find the positions that the destination have that are not in the source and vice versa + sourcePositionsToSplitAt = [] + destinationPositionsToSplitAt = [] + i = 0, il = sourcePositions.length + j = 0, jl = destinationPositions.length + while(i < il && j < jl) { + // Test if the two values are equal taking into account the imprecision of floating point number + if (Math.abs(sourcePositions[i] - destinationPositions[j]) < 0.000001) { + i++ + j++ + } else if(sourcePositions[i] > destinationPositions[j]){ + sourcePositionsToSplitAt.push(destinationPositions[j++]) + } else { + destinationPositionsToSplitAt.push(sourcePositions[i++]) + } + } + // If there are still some destination positions left, they all are not in the source and vice versa + sourcePositionsToSplitAt = sourcePositionsToSplitAt.concat(destinationPositions.slice(j)) + destinationPositionsToSplitAt = destinationPositionsToSplitAt.concat(sourcePositions.slice(i)) + + // Split the source and the destination at the positions they don't have in common + cspSplitAtPositions(source, sourcePositions, sourcePositionsToSplitAt) + cspSplitAtPositions(destination, destinationPositions, destinationPositionsToSplitAt) + + + // Break paths so that corresponding subpaths have an equal number of segments + s = source, source = [], sourceSubpath = s[i = 0] + d = destination, destination = [], destinationSubpath = d[j = 0] + while (sourceSubpath && destinationSubpath) { + // Push REFERENCES to the current subpath arrays in their respective array + source.push(sourceSubpath) + destination.push(destinationSubpath) + + il = sourceSubpath.length + jl = destinationSubpath.length + + // If the current subpath of the source and the current subpath of the destination don't + // have the same length, that mean that the biggest of the two must be split in two + if(il > jl) { + lastSegPt = sourceSubpath[jl-1] + // Perform the split using splice that change the content of the array by removing elements and returning them in an array + sourceSubpath = sourceSubpath.splice(jl) + sourceSubpath.unshift(lastSegPt) // The last segment point is duplicated since these two segments must be joined together + destinationSubpath = d[++j] // This subpath has been accounted for, past to the next + } else if(il < jl) { + lastSegPt = destinationSubpath[il-1] + destinationSubpath = destinationSubpath.splice(il) + destinationSubpath.unshift(lastSegPt) + sourceSubpath = s[++i] + } else { + sourceSubpath = s[++i] + destinationSubpath = d[++j] + } + } + + // Convert in path array and return + return [uncubicSuperPath(source), uncubicSuperPath(destination)] +} + + + + + + +// This function converts every segment of a path array into equivalent cubic Bezier curves +// and return the results in a 3 dimensional array that have the following hierarchy: +// Cubic super path: [ ] +// Segments: [ ] ... +// Segment points: [SVG.Point, SVG.Point, SVG.Point] ... +// +// A segment point is a point with the two control points that are attached to it: +// [First control point, Point, Second control point] +// +// If the passed path array cannot be converted in a cubic super path, this function return an empty array. +function cubicSuperPath(pathArray) { + pathArray = new SVG.PathArray(pathArray) + + var cubicSP = [] + , subpath = null + , subpathStartPt = null + , lastPt = null + , lastCtrlPt = null + , i, il, cmd = null, params, lastCmd + , start, control, end + , arcSegPoints, segPt + + for (i = 0, il = pathArray.value.length; i < il; i++) { + lastCmd = cmd + cmd = pathArray.value[i][0] + params = pathArray.value[i].slice(1) + + switch (cmd) { + case 'M': // moveto + // Parameters: x y + if (lastPt) { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + subpath = [] + cubicSP.push(subpath) // Push a reference to the current subpath array in the cubic super path array + subpathStartPt = new SVG.Point(params) + lastPt = subpathStartPt.clone() + lastCtrlPt = subpathStartPt.clone() + break + + case 'L': // lineto + // Parameters: x y + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(params) + lastCtrlPt = lastPt.clone() + break + + case 'H': // horizontal lineto + // Parameters: x + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(params[0], lastPt.y) + lastCtrlPt = lastPt.clone() + break + + case 'V': // vertical lineto + // Parameters: y + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(lastPt.x, params[0]) + lastCtrlPt = lastPt.clone() + break + + case 'C': // curveto + // Parameters: x1 y1 x2 y2 x y + subpath.push([lastCtrlPt, lastPt, new SVG.Point(params.slice(0,2))]) + lastPt = new SVG.Point(params.slice(4,6)) + lastCtrlPt = new SVG.Point(params.slice(2,4)) + break + + case 'S': // shorthand/smooth curveto + // Parameters: x2 y2 x y + // For this version of curveto, the first control point is the reflection of the second control point on the previous command relative to the current point + // If the previous command is not a curveto command, then the first control point is the same as the current point + if(lastCmd === 'C' || lastCmd === 'S') { + subpath.push([lastCtrlPt, lastPt, lastPt.times(2).minus(lastCtrlPt)]) + } else { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + lastPt = new SVG.Point(params.slice(2,4)) + lastCtrlPt = new SVG.Point(params.slice(0,2)) + break + + case 'Q': // quadratic Bezier curveto + // Parameters: x1 y1 x y + // For an explanation of the method used, see: https://pomax.github.io/bezierinfo/#reordering + start = lastPt + control = new SVG.Point(params.slice(0,2)) + end = new SVG.Point(params.slice(2,4)) + + subpath.push([lastCtrlPt, start, start.times(1/3).plus(control.times(2/3))]) + lastPt = end + lastCtrlPt = control.times(2/3).plus(end.times(1/3)) + break + + case 'T': // shorthand/smooth quadratic Bézier curveto + // Parameters: x y + // For this version of quadratic Bézier curveto, the control point is the reflection of the control point on the previous command relative to the current point + // If the previous command is not a quadratic Bézier curveto command, then the control point is the same as the current point + start = lastPt + if(lastCmd === 'Q' || lastCmd === 'T') { + control = start.times(2).minus(control) + } else { + control = start + } + end = new SVG.Point(params.slice(0,2)) + + subpath.push([lastCtrlPt, start, start.times(1/3).plus(control.times(2/3))]) + lastPt = end + lastCtrlPt = control.times(2/3).plus(end.times(1/3)) + break + + case 'A': // elliptical arc + // Parameters: rx ry x-axis-rotation large-arc-flag sweep-flag x y + arcSegPoints = arcToPath(lastPt, params) + arcSegPoints[0][0] = lastCtrlPt + segPt = arcSegPoints.pop() + lastPt = segPt[1] + lastCtrlPt = segPt[0] + Array.prototype.push.apply(subpath, arcSegPoints) + break + + case 'Z': // closepath + // Parameters: none + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + // Close the path only if it is not already closed + if(lastPt.x != subpathStartPt.x && lastPt.y != subpathStartPt.y) { + lastPt = subpathStartPt + lastCtrlPt = subpathStartPt.clone() + } else { + lastPt = null + lastCtrlPt = null + } + break + } + } + + // Push final segment point if any + if(lastPt) { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + + return cubicSP +} + + +// This function convert a cubic super path into a path array +function uncubicSuperPath (cubicSP) { + var i, il, j, jl, array = [], pathArray = new SVG.PathArray, subpath + + for (i = 0, il = cubicSP.length; i < il; i++) { + subpath = cubicSP[i] + + if (subpath.length) { + array.push(['M'].concat(subpath[0][1].toArray())) + + for (j = 1, jl = subpath.length; j < jl; j++) { + array.push(['C'].concat(subpath[j-1][2].toArray(), subpath[j][0].toArray(), subpath[j][1].toArray())) + } + } + } + + // Directly modify the value of a path array, this is done this way for performance + pathArray.value = array + return pathArray +} + +// Convert an arc segment into equivalent cubic Bezier curves +// Depending on the arc, up to 4 curves might be used to represent it since a +// curve gives a good approximation for only a quarter of an ellipse +// The curves are returned as an array of segment points: +// [ [SVG.Point, SVG.Point, SVG.Point] ... ] +function arcToPath(lastPt, params) { + // Parameters extraction, handle out-of-range parameters as specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters + var rx = Math.abs(params[0]), ry = Math.abs(params[1]), xAxisRotation = params[2] % 360 + , largeArcFlag = params[3], sweepFlag = params[4], x2 = params[5], y2 = params[6] + , A = lastPt, B = new SVG.Point(x2, y2) + , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd + , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il + + // Ensure radii are non-zero + if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) { + // treat this arc as a straight line segment + return [[A, A.clone(), A.clone()], [B, B.clone(), B.clone()]] + } + + // Ensure radii are large enough using the algorithm provided in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii + primedCoord = A.minus(B).divide(2).transform(new SVG.Matrix().rotate(xAxisRotation)) + lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry) + if(lambda > 1) { + lambda = Math.sqrt(lambda) + rx = lambda*rx + ry = lambda*ry + } + + // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation) + A = A.transform(mat) + B = B.transform(mat) + + // Calculate the horizontal and vertical distance between the initial and final point of the arc + k = [B.x-A.x, B.y-A.y] + + // Find the length of the chord formed by A and B + cSquare = k[0]*k[0] + k[1]*k[1] + c = Math.sqrt(cSquare) + + // Calculate the ratios of the horizontal and vertical distance on the length of the chord + k[0] /= c + k[1] /= c + + // Calculate the distance between the circle center and the chord midpoint + // using this formula: t = sqrt(r^2 - c^2 / 4) + // where t is the distance between the cirle center and the chord midpoint, + // r is the rayon of the circle and c is the chord length + // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php + // Because of the imprecision of floating point numbers, cSquare might end + // up being slightly above 4 which would result in a negative radicand + // To prevent that, a test is made before computing the square root + t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0 + + // For most situations, there are actually two different ellipses that + // satisfy the constraints imposed by the points A and B, the radii rx and ry, + // and the xAxisRotation + // When the flags largeArcFlag and sweepFlag are equal, it means that the + // second ellipse is used as a solution + // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands + if(largeArcFlag === sweepFlag) { + t *= -1 + } + + // Calculate the coordinates of the center of the circle from the midpoint of the chord + // This is done by multiplying the ratios calculated previously by the distance between + // the circle center and the chord midpoint and using these values to go from the midpoint + // to the center of the circle + // The negative of the vertical distance ratio is used to modify the x coordinate while + // the horizontal distance ratio is used to modify the y coordinate + // That is because the center of the circle is perpendicular to the chord and perpendicular + // lines are negative reciprocals + O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0]) + // Move the center of the circle at the origin + OA = A.minus(O) + OB = B.minus(O) + + // Calculate the start and end angle + tetaStart = Math.acos(OA.x/OA.norm()) + if (OA.y < 0) { + tetaStart *= -1 + } + tetaEnd = Math.acos(OB.x/OB.norm()) + if (OB.y < 0) { + tetaEnd *= -1 + } + + // If sweep-flag is '1', then the arc will be drawn in a "positive-angle" direction, + // make sure that the end angle is above the start angle + if (sweepFlag && tetaStart > tetaEnd) { + tetaEnd += 2*Math.PI + } + // If sweep-flag is '0', then the arc will be drawn in a "negative-angle" direction, + // make sure that the end angle is below the start angle + if (!sweepFlag && tetaStart < tetaEnd) { + tetaEnd -= 2*Math.PI + } + + // Find the number of Bezier curves that are required to represent the arc + // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle + nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI) + + // Calculate the coordinates of the points of all the Bezier curves required to represent the arc + // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic + arcSegPoints = [] + angle = tetaStart + deltaTeta = (tetaEnd-tetaStart)/nbSectors + f = 4*Math.tan(deltaTeta/4)/3 + for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint + cosAngle = Math.cos(angle) + sinAngle = Math.sin(angle) + + pt = O.plus(cosAngle, sinAngle) + arcSegPoints[i] = [pt.plus(+f*sinAngle, -f*cosAngle), pt, pt.plus(-f*sinAngle, +f*cosAngle)] + + angle += deltaTeta + } + + // Remove the first control point of the first segment point and remove the second control point of the last segment point + // These two control points are not used in the approximation of the arc, that is why they are removed + arcSegPoints[0][0] = arcSegPoints[0][1].clone() + arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone() + + // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation) + for (i = 0, il = arcSegPoints.length; i < il; i++) { + arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat) + arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat) + arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat) + } + + return arcSegPoints +} + + +// Use de Casteljau's algorithm to split a cubic Bezier curve +// For a description of the algorithm, see: https://pomax.github.io/bezierinfo/#decasteljau +// Return an array of 3 segment points +function cspSegSplit(segPt1, segPt2, t) { + segPt1 = [segPt1[0].clone(), segPt1[1].clone(), segPt1[2].clone()] + segPt2 = [segPt2[0].clone(), segPt2[1].clone(), segPt2[2].clone()] + + var m1 = segPt1[1].morph(segPt1[2]).at(t) + , m2 = segPt1[2].morph(segPt2[0]).at(t) + , m3 = segPt2[0].morph(segPt2[1]).at(t) + , m4 = m1.morph(m2).at(t) + , m5 = m2.morph(m3).at(t) + , m = m4.morph(m5).at(t) + + return [[segPt1[0], segPt1[1], m1], [m4, m, m5], [m3, segPt2[1], segPt2[2]]] +} + + +// Find the length of a cubic Bezier curve using the built-in method getTotalLength of SVGPathElement +// For more info, see: https://www.w3.org/TR/SVG11/paths.html#InterfaceSVGPathElement +function cspSegLength(segPt1, segPt2) { + var path = document.createElementNS(SVG.ns, "path") + , d = ['M', segPt1[1].toArray(), 'C', segPt1[2].toArray(), segPt2[0].toArray(), segPt2[1].toArray()].join(' ') + + path.setAttribute('d', d) + + return path.getTotalLength() +} + + +// Find the length of all the cubic Bezier curves of a cubic super path and return +// the results in a 2 dimensional array that have the following hierarchy: +// Cubic super path lengths: [ ] +// Segments lengths: [ ] ... +// Cubic Bezier curves length: Number ... +// +// On the returned array, the property total is set to the sum of all the lengths +function cspLengths(cubicSP) { + var total = 0 + , subpath, lengths = [], lengthsSubpath, length + , i, il, j, jl + + for (i = 0, il = cubicSP.length; i < il; i++) { + subpath = cubicSP[i] + lengthsSubpath = [] + lengths[i] = lengthsSubpath // Save a reference to the current subpath lengths array in the cubic super path lengths array + + for (j = 1, jl = subpath.length; j < jl; j++) { + length = cspSegLength(subpath[j-1], subpath[j]) + lengthsSubpath[j-1] = length + total += length + } + } + + lengths.total = total + return lengths +} + + +// Split a cubic Bezier curve at the given length ratio +// Return an array of 3 segment points +function cspSegSplitAtLengthRatio(segPt1, segPt2, lengthRatio) { + var t = 1.0 + , tdiv = t + , currentLength = cspSegLength(segPt1, segPt2) + , targetLength = lengthRatio * currentLength + , diff = currentLength - targetLength + , split = cspSegSplit(segPt1, segPt2, t) + , maxNbLoops = 4096 // For not getting stuck in an infinite loop + + while (Math.abs(diff) > 0.001 && maxNbLoops--) { + tdiv /= 2 + t += (diff < 0) ? tdiv : -tdiv + split = cspSegSplit(segPt1, segPt2, t) + currentLength = cspSegLength(split[0], split[1]) + diff = currentLength - targetLength + } + + return split +} + + + +// Find the position relative to the total length of the endpoint of all the cubic Bezier curves +// of a cubic super path and return the results in a 1 dimensional array +function cspPositions(cubicSP) { + var lengths = cspLengths(cubicSP), total = lengths.total + , pos = 0, positions = [] + , i, il, j, jl + + for (i = 0, il = lengths.length; i < il; i++) { + for (j = 0, jl = lengths[i].length; j < jl; j++) { + pos += lengths[i][j] / total + positions.push(pos) + } + } + + return positions +} + +// Split the passed cubic super path at the specified positions and return the results as a new cubic super path +// For performance reasons, the positions of the passed cubic super path must also be provided +function cspSplitAtPositions(cubicSP, positions, positionsToSplitAt){ + var subpath, newSubpath + , accumNbPositions = 0, segPt, lengthRatio, split, pos, prevPos + , i, il, j, jl // indexes on the cubicSP array + , k = 0 // index on the positions array + , l = 0, ll = positionsToSplitAt.length + + for (i = 0, il = cubicSP.length; i < il && l < ll; i++) { + subpath = cubicSP[i] + // The positions are only for the endpoints of the cubic Bezier curves, so + // a subpath need at least 2 segment points for a position to be on it + if(subpath.length < 2) {continue} + // Test if there are splits to be performed on the current subpath + if(positionsToSplitAt[l] < positions[accumNbPositions + subpath.length-2]) { + k = accumNbPositions + newSubpath = [] + cubicSP[i] = newSubpath // Save a reference to the new current subpath array in the cubic super path array + pos = positions[k-1] || 0 + + // Recopy the content of the current subpath, performing splits where necessary + newSubpath.push(subpath[0]) + for (j = 1, jl = subpath.length; j < jl; j++) { + prevPos = pos + pos = positions[k++] + segPt = subpath[j] + + while(l < ll && positionsToSplitAt[l] < pos) { + lengthRatio = (positionsToSplitAt[l] - prevPos) / (pos - prevPos) + split = cspSegSplitAtLengthRatio(newSubpath[newSubpath.length-1], segPt, lengthRatio) + newSubpath[newSubpath.length-1] = split[0] + newSubpath.push(split[1]) + segPt = split[2] + prevPos = positionsToSplitAt[l++] + } + + newSubpath.push(segPt) + } + } + + // -1 because positions are only for endpoints of Bezier curves + accumNbPositions += subpath.length - 1 + } +} + // Add CustomEvent to IE9 and IE10 if (typeof CustomEvent !== 'function') { // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent diff --git a/dist/svg.min.js b/dist/svg.min.js index 2a273bd..ba28d7b 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,3 @@ -/*! svg.js v2.3.6 MIT*/;!function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t,t.document)}):"object"==typeof exports?module.exports=t.document?e(t,t.document):function(t){return e(t,t.document)}:t.SVG=e(t,t.document)}("undefined"!=typeof window?window:this,function(t,e){function i(t,e){return t instanceof e}function n(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}function s(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function r(t){return t.charAt(0).toUpperCase()+t.slice(1)}function a(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t}function o(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function h(t,e,i){if(null==e||null==i){var n=t.bbox();null==e?e=n.width/n.height*i:null==i&&(i=n.height/n.width*e)}return{width:e,height:i}}function u(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function l(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function c(t){return t instanceof g.Matrix||(t=new g.Matrix(t)),t}function f(t,e){t.cx=null==t.cx?e.bbox().cx:t.cx,t.cy=null==t.cy?e.bbox().cy:t.cy}function d(t){return t=t.replace(g.regex.whitespace,"").replace(g.regex.matrix,"").split(g.regex.matrixElements),l(g.utils.map(t,function(t){return parseFloat(t)}))}function p(t){for(var e=0,i=t.length,n="";e=0;e--)t.childNodes[e]instanceof SVGElement&&m(t.childNodes[e]);return g.adopt(t).id(g.eid(t.nodeName))}function x(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){var e=t.toString().match(g.regex.reference);if(e)return e[1]}var g=this.SVG=function(t){if(g.supported)return t=new g.Doc(t),g.parser.draw||g.prepare(),t};if(g.ns="http://www.w3.org/2000/svg",g.xmlns="http://www.w3.org/2000/xmlns/",g.xlink="http://www.w3.org/1999/xlink",g.svgjs="http://svgjs.com/svgjs",g.supported=function(){return!!e.createElementNS&&!!e.createElementNS(g.ns,"svg").createSVGRect}(),!g.supported)return!1;g.did=1e3,g.eid=function(t){return"Svgjs"+r(t)+g.did++},g.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute("id",this.eid(t)),i},g.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];g.Set&&g.Set.inherit&&g.Set.inherit()},g.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,g.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&g.extend(e,t.extend),t.construct&&g.extend(t.parent||g.Container,t.construct),e},g.adopt=function(t){if(!t)return null;if(t.instance)return t.instance;var e;return e="svg"==t.nodeName?t.parentNode instanceof SVGElement?new g.Nested:new g.Doc:"linearGradient"==t.nodeName?new g.Gradient("linear"):"radialGradient"==t.nodeName?new g.Gradient("radial"):g[r(t.nodeName)]?new(g[r(t.nodeName)]):new g.Element(t),e.type=t.nodeName,e.node=t,t.instance=e,e instanceof g.Doc&&e.namespace().defs(),e.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}),e},g.prepare=function(){var t=e.getElementsByTagName("body")[0],i=(t?new g.Doc(t):new g.Doc(e.documentElement).nested()).size(2,0);g.parser={body:t||e.documentElement,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:i.path().node,native:g.create("svg")}},g.parser={native:g.create("svg")},e.addEventListener("DOMContentLoaded",function(){g.parser.draw||g.prepare()},!1),g.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,matrix:/matrix\(|\)/g,matrixElements:/,*\s+|,/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,negExp:/e\-/gi,comma:/,/g,hyphen:/\-/g,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,whitespaces:/\s+/,X:/X/g},g.utils={map:function(t,e){var i,n=t.length,s=[];for(i=0;i1?1:t,new g.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),g.Color.test=function(t){return t+="",g.regex.isHex.test(t)||g.regex.isRgb.test(t)},g.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},g.Color.isColor=function(t){return g.Color.isRgb(t)||g.Color.test(t)},g.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},g.extend(g.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.length=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y;return this},bbox:function(){return g.parser.poly.setAttribute("points",this.toString()),g.parser.poly.getBBox()}}),g.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},g.PathArray.prototype=new g.Array,g.extend(g.PathArray,{toString:function(){return p(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,s=this.value.length-1;s>=0;s--)n=this.value[s][0],"M"==n||"L"==n||"T"==n?(this.value[s][1]+=t,this.value[s][2]+=e):"H"==n?this.value[s][1]+=t:"V"==n?this.value[s][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[s][1]+=t,this.value[s][2]+=e,this.value[s][3]+=t,this.value[s][4]+=e,"C"==n&&(this.value[s][5]+=t,this.value[s][6]+=e)):"A"==n&&(this.value[s][6]+=t,this.value[s][7]+=e);return this},size:function(t,e){var i,n,s=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-s.x)*t/s.width+s.x,this.value[i][2]=(this.value[i][2]-s.y)*e/s.height+s.y):"H"==n?this.value[i][1]=(this.value[i][1]-s.x)*t/s.width+s.x:"V"==n?this.value[i][1]=(this.value[i][1]-s.y)*e/s.height+s.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-s.x)*t/s.width+s.x,this.value[i][2]=(this.value[i][2]-s.y)*e/s.height+s.y,this.value[i][3]=(this.value[i][3]-s.x)*t/s.width+s.x,this.value[i][4]=(this.value[i][4]-s.y)*e/s.height+s.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-s.x)*t/s.width+s.x,this.value[i][6]=(this.value[i][6]-s.y)*e/s.height+s.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/s.width,this.value[i][2]=this.value[i][2]*e/s.height,this.value[i][6]=(this.value[i][6]-s.x)*t/s.width+s.x,this.value[i][7]=(this.value[i][7]-s.y)*e/s.height+s.y);return this},parse:function(t){if(t instanceof g.PathArray)return t.valueOf();var e,i,n,s,r,a,o=0,h=0,u={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7};if("string"==typeof t){for(t=t.replace(g.regex.negExp,"X").replace(g.regex.pathLetters," $& ").replace(g.regex.hyphen," -").replace(g.regex.comma," ").replace(g.regex.X,"e-").trim().split(g.regex.whitespaces),e=t.length;--e;)if(t[e].indexOf(".")!=t[e].lastIndexOf(".")){var l=t[e].split("."),c=[l.shift(),l.shift()].join(".");t.splice.apply(t,[e,1].concat(c,l.map(function(t){return"."+t})))}}else t=t.reduce(function(t,e){return[].concat.apply(t,e)},[]);var a=[];do{for(g.regex.isPathLetter.test(t[0])?(s=t[0],t.shift()):"M"==s?s="L":"m"==s&&(s="l"),r=[s.toUpperCase()],e=0;ei.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2>")+"";for(var n=0,s=i.firstChild.childNodes.length;n":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return-Math.cos(t*Math.PI/2)+1}},g.morph=function(t){return function(e,i){return new g.MorphObj(e,i).at(t)}},g.Situation=g.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new g.Number(t.duration).valueOf(),this.delay=new g.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),g.FX=g.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,i){"object"==typeof t&&(e=t.ease,i=t.delay,t=t.duration);var n=new g.Situation({duration:t||1e3,delay:i||0,ease:g.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var e=new g.Situation({duration:t,delay:0,ease:g.easing["-"]});return this.queue(e)},target:function(t){return t&&t instanceof g.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof g.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.situation&&this.situation.stop&&this.situation.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof g.Situation?this.startCurrent():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(t in e.animations)"viewbox"==t?e.animations[t]=this.target().viewbox().morph(e.animations[t]):(e.animations[t].value="plot"==t?this.target().array().value:this.target()[t](),e.animations[t].value.value&&(e.animations[t].value=e.animations[t].value.value),e.animations[t].relative&&(e.animations[t].destination.value=e.animations[t].destination.value+e.animations[t].value));for(t in e.attrs)if(e.attrs[t]instanceof g.Color){var i=new g.Color(this.target().attr(t));e.attrs[t].r=i.r,e.attrs[t].g=i.g,e.attrs[t].b=i.b}else e.attrs[t].value=this.target().attr(t);for(t in e.styles)e.styles[t].value=this.target().style(t);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){return this.active||this.start(),e&&this.clearQueue(),this.active=!1,t&&this.situation&&this.atEnd(),this.stopAnimFrame(),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.atStart()}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},atStart:function(){return this.at(0,!0)},atEnd:function(){return this.situation.loops===!0?this.at(this.situation.loop+1,!0):"number"==typeof this.situation.loops?this.at(this.situation.loops,!0):this.at(1,!0)},at:function(t,e){var i=this.situation.duration/this._speed;return this.absPos=t,e||(this.situation.reversed&&(this.absPos=1-this.absPos),this.absPos+=this.situation.loop),this.situation.start=+new Date-this.absPos*i,this.situation.finish=this.situation.start+i,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.absPos,!0)):this._speed},loop:function(t,e){var i=this.last();return i.loops=null==t||t,i.loop=0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return"undefined"==typeof t?e.reversed=!e.reversed:e.reversed=t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){var e=this.last(),i=function i(n){n.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))};return this.target().on("finished.fx",i),this},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,g.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after(function(){this.off("during.fx",i)})},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,g.morph(e.detail.pos),e.detail.eased,e.detail.situation)};return this.target().off("during.fx",e).on("during.fx",e),this.afterAll(function(){this.off("during.fx",e)})},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,setTimeout(function(){this.start()}.bind(this),0),this},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),this.situation.loops!==!1){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),this.situation.loops===!0||ithis.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire("during",{pos:this.pos,eased:s,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.target().off(".fx"),this.active=!1),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e,i=this,n=this.target(),s=this.situation;for(t in s.animations)e=[].concat(s.animations[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(i.pos),i.pos):t}),n[t].apply(n,e);for(t in s.attrs)e=[t].concat(s.attrs[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(i.pos),i.pos):t}),n.attr.apply(n,e);for(t in s.styles)e=[t].concat(s.styles[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(i.pos),i.pos):t}),n.style.apply(n,e);if(s.transforms.length){for(e=s.initialTransformation,t=0,len=s.transforms.length;t=0;--e)this[w[e]]=t&&"number"==typeof t[w[e]]?t[w[e]]:i[w[e]]},extend:{extract:function(){var t=u(this,0,1),e=u(this,1,0),i=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(i*Math.PI/180)+this.f*Math.sin(i*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(i*Math.PI/180)+this.e*Math.sin(-i*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),skewX:-i,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:i,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new g.Matrix(this)}},clone:function(){return new g.Matrix(this)},morph:function(t){return this.destination=new g.Matrix(t),this},at:function(t){if(!this.destination)return this;var e=new g.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t});if(this.param&&this.param.to){var i={rotation:this.param.from.rotation+(this.param.to.rotation-this.param.from.rotation)*t,cx:this.param.from.cx,cy:this.param.from.cy};e=e.rotate((this.param.to.rotation-2*this.param.from.rotation)*t,i.cx,i.cy),e.param=i}return e},multiply:function(t){return new g.Matrix(this.native().multiply(c(t).native()))},inverse:function(){return new g.Matrix(this.native().inverse())},translate:function(t,e){return new g.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),this.around(i,n,new g.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=g.utils.radians(t),this.around(e,i,new g.Matrix(Math.cos(t),Math.sin(t),(-Math.sin(t)),Math.cos(t),0,0))},flip:function(t,e){return"x"==t?this.scale(-1,1,e,0):this.scale(1,-1,0,e)},skew:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),t=g.utils.radians(t),e=g.utils.radians(e),this.around(i,n,new g.Matrix(1,Math.tan(e),Math.tan(t),1,0,0))},skewX:function(t,e,i){return this.skew(t,0,e,i)},skewY:function(t,e,i){return this.skew(0,t,e,i)},around:function(t,e,i){return this.multiply(new g.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new g.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=g.parser.native.createSVGMatrix(),e=w.length-1;e>=0;e--)t[w[e]]=this[w[e]];return t},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:g.Element,construct:{ctm:function(){return new g.Matrix(this.node.getCTM())},screenCTM:function(){return new g.Matrix(this.node.getScreenCTM())}}}),g.Point=g.invent({create:function(t,e){var i,n={x:0,y:0};i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:null!=e?{x:t,y:e}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new g.Point(this)},morph:function(t){return this.destination=new g.Point(t),this},at:function(t){if(!this.destination)return this;var e=new g.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t});return e},native:function(){var t=g.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new g.Point(this.native().matrixTransform(t.native()))}}}),g.extend(g.Element,{point:function(t,e){return new g.Point(t,e).transform(this.screenCTM().inverse())}}),g.extend(g.Element,{attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=g.regex.isNumber.test(e[i].nodeValue)?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?g.defaults.attrs[t]:g.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),"fill"!=t&&"stroke"!=t||(g.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof g.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e); -}))),"number"==typeof e?e=new g.Number(e):g.Color.isColor(e)?e=new g.Color(e):Array.isArray(e)?e=new g.Array(e):e instanceof g.Matrix&&e.param&&(this.param=e.param),"leading"==t?this.leading&&this.leading(e):"string"==typeof i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this}}),g.extend(g.Element,{transform:function(t,e){var i,n=this;if("object"!=typeof t)return i=new g.Matrix(n).extract(),"string"==typeof t?i[t]:i;if(i=new g.Matrix(n),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new g.Matrix(t)):new g.Matrix(t);else if(null!=t.rotation)f(t,n),i=e?i.rotate(t.rotation,t.cx,t.cy):i.rotate(t.rotation-i.extract().rotation,t.cx,t.cy);else if(null!=t.scale||null!=t.scaleX||null!=t.scaleY){if(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,!e){var s=i.extract();t.scaleX=1*t.scaleX/s.scaleX,t.scaleY=1*t.scaleY/s.scaleY}i=i.scale(t.scaleX,t.scaleY,t.cx,t.cy)}else if(null!=t.skew||null!=t.skewX||null!=t.skewY){if(f(t,n),t.skewX=null!=t.skew?t.skew:null!=t.skewX?t.skewX:0,t.skewY=null!=t.skew?t.skew:null!=t.skewY?t.skewY:0,!e){var s=i.extract();i=i.multiply((new g.Matrix).skew(s.skewX,s.skewY,t.cx,t.cy).inverse())}i=i.skew(t.skewX,t.skewY,t.cx,t.cy)}else t.flip?i=i.flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset):null==t.x&&null==t.y||(e?i=i.translate(t.x,t.y):(null!=t.x&&(i.e=t.x),null!=t.y&&(i.f=t.y)));return this.attr("transform",i)}}),g.extend(g.FX,{transform:function(t,e){var i,n=this.target();return"object"!=typeof t?(i=new g.Matrix(n).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new g.Matrix(t):null!=t.rotation?(f(t,n),i=new g.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,i=new g.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new g.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?i=(new g.Matrix).morph((new g.Matrix).flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset)):null==t.x&&null==t.y||(i=new g.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),setTimeout(function(){this.start()}.bind(this),0),this):this)}}),g.extend(g.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){var t=(this.attr("transform")||"").split(/\)\s*,?\s*/).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(g.regex.matrixElements).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(l(e[1])):t[e[0]].apply(t,e[1])},new g.Matrix);return t},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.rect(1,1),n=i.screenCTM().inverse();return i.remove(),this.addTo(t).untransform().transform(n.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),g.Transformation=g.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.create([].slice.call(arguments));if("object"==typeof t)for(var i=0,n=this.arguments.length;i=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return g.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){var i,n,s=this.children();for(i=0,n=s.length;in/s?this.height/s:this.width/n,this.x=e,this.y=i,this.width=n,this.height=s)}else t="string"==typeof t?t.match(f).map(function(t){return parseFloat(t)}):Array.isArray(t)?t:"object"==typeof t?[t.x,t.y,t.width,t.height]:4==arguments.length?[].slice.call(arguments):u,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3]},extend:{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t){var t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments);return this.destination=new g.ViewBox(t),this},at:function(t){return this.destination?new g.ViewBox([this.x+(this.destination.x-this.x)*t,this.y+(this.destination.y-this.y)*t,this.width+(this.destination.width-this.width)*t,this.height+(this.destination.height-this.height)*t]):this}},parent:g.Container,construct:{viewbox:function(t){return 0==arguments.length?new g.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){g.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),g.listeners=[],g.handlerMap=[],g.listenerId=0,g.on=function(t,e,i,n){var s=i.bind(n||t.instance||t),r=(g.handlerMap.indexOf(t)+1||g.handlerMap.push(t))-1,a=e.split(".")[0],o=e.split(".")[1]||"*";g.listeners[r]=g.listeners[r]||{},g.listeners[r][a]=g.listeners[r][a]||{},g.listeners[r][a][o]=g.listeners[r][a][o]||{},i._svgjsListenerId||(i._svgjsListenerId=++g.listenerId),g.listeners[r][a][o][i._svgjsListenerId]=s,t.addEventListener(a,s,!1)},g.off=function(t,e,i){var n=g.handlerMap.indexOf(t),s=e&&e.split(".")[0],r=e&&e.split(".")[1];if(n!=-1)if(i){if("function"==typeof i&&(i=i._svgjsListenerId),!i)return;g.listeners[n][s]&&g.listeners[n][s][r||"*"]&&(t.removeEventListener(s,g.listeners[n][s][r||"*"][i],!1),delete g.listeners[n][s][r||"*"][i])}else if(r&&s){if(g.listeners[n][s]&&g.listeners[n][s][r]){for(i in g.listeners[n][s][r])g.off(t,[s,r].join("."),i);delete g.listeners[n][s][r]}}else if(r)for(e in g.listeners[n])for(namespace in g.listeners[n][e])r===namespace&&g.off(t,[e,r].join("."));else if(s){if(g.listeners[n][s]){for(namespace in g.listeners[n][s])g.off(t,[s,namespace].join("."));delete g.listeners[n][s]}}else{for(e in g.listeners[n])g.off(t,e);delete g.listeners[n]}},g.extend(g.Element,{on:function(t,e,i){return g.on(this.node,t,e,i),this},off:function(t,e){return g.off(this.node,t,e),this},fire:function(t,e){return t instanceof Event?this.node.dispatchEvent(t):this.node.dispatchEvent(new b(t,{detail:e})),this}}),g.Defs=g.invent({create:"defs",inherit:g.Container}),g.G=g.invent({create:"g",inherit:g.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)},y:function(t){return null==t?this.transform("y"):this.transform({y:t-this.y()},!0)},cx:function(t){return null==t?this.gbox().cx:this.x(t-this.gbox().width/2)},cy:function(t){return null==t?this.gbox().cy:this.y(t-this.gbox().height/2)},gbox:function(){var t=this.bbox(),e=this.transform();return t.x+=e.x,t.x2+=e.x,t.cx+=e.x,t.y+=e.y,t.y2+=e.y,t.cy+=e.y,t}},construct:{group:function(){return this.put(new g.G)}}}),g.extend(g.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof g.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof g.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),g.Mask=g.invent({create:function(){this.constructor.call(this,g.create("mask")),this.targets=[]},inherit:g.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new g.Mask)}}}),g.extend(g.Element,{maskWith:function(t){return this.masker=t instanceof g.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),g.ClipPath=g.invent({create:function(){this.constructor.call(this,g.create("clipPath")),this.targets=[]},inherit:g.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return this.targets=[],this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new g.ClipPath)}}}),g.extend(g.Element,{clipWith:function(t){return this.clipper=t instanceof g.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),g.Gradient=g.invent({create:function(t){this.constructor.call(this,g.create(t+"Gradient")),this.type=t},inherit:g.Container,extend:{at:function(t,e,i){return this.put(new g.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),g.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),g.extend(g.Gradient,g.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new g.Number(t),fy:new g.Number(e)}):this.attr({x1:new g.Number(t),y1:new g.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new g.Number(t),cy:new g.Number(e)}):this.attr({x2:new g.Number(t),y2:new g.Number(e)})}}),g.extend(g.Defs,{gradient:function(t,e){return this.put(new g.Gradient(t)).update(e)}}),g.Stop=g.invent({create:"stop",inherit:g.Element,extend:{update:function(t){return("number"==typeof t||t instanceof g.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new g.Number(t.offset)),this}}}),g.Pattern=g.invent({create:"pattern",inherit:g.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),g.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),g.extend(g.Defs,{pattern:function(t,e,i){return this.put(new g.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),g.Doc=g.invent({create:function(t){t&&(t="string"==typeof t?e.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,g.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:g.Container,extend:{namespace:function(){return this.attr({xmlns:g.ns,version:"1.1"}).attr("xmlns:xlink",g.xlink,g.xmlns).attr("xmlns:svgjs",g.svgjs,g.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=g.adopt(t):this._defs=new g.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode},spof:function(t){var e=this.node.getScreenCTM();return e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this}}}),g.Shape=g.invent({create:function(t){this.constructor.call(this,t)},inherit:g.Element}),g.Bare=g.invent({create:function(t,e){if(this.constructor.call(this,g.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:g.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),g.extend(g.Parent,{element:function(t,e){return this.put(new g.Bare(t,e))},symbol:function(){return this.defs().element("symbol",g.Container)}}),g.Use=g.invent({create:"use",inherit:g.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,g.xlink)}},construct:{use:function(t,e){return this.put(new g.Use).element(t,e)}}}),g.Rect=g.invent({create:"rect",inherit:g.Shape,construct:{rect:function(t,e){return this.put(new g.Rect).size(t,e)}}}),g.Circle=g.invent({create:"circle",inherit:g.Shape,construct:{circle:function(t){return this.put(new g.Circle).rx(new g.Number(t).divide(2)).move(0,0)}}}),g.extend(g.Circle,g.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),g.Ellipse=g.invent({create:"ellipse",inherit:g.Shape,construct:{ellipse:function(t,e){return this.put(new g.Ellipse).size(t,e).move(0,0)}}}),g.extend(g.Ellipse,g.Rect,g.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),g.extend(g.Circle,g.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new g.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new g.Number(t).divide(2))},size:function(t,e){var i=h(this,t,e);return this.rx(new g.Number(i.width).divide(2)).ry(new g.Number(i.height).divide(2))}}),g.Line=g.invent({create:"line",inherit:g.Shape,extend:{array:function(){return new g.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return t="undefined"!=typeof e?{x1:t,y1:e,x2:i,y2:n}:new g.PointArray(t).toLine(),this.attr(t)},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=h(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return this.put(new g.Line).plot(t,e,i,n)}}}),g.Polyline=g.invent({create:"polyline",inherit:g.Shape,construct:{polyline:function(t){return this.put(new g.Polyline).plot(t)}}}),g.Polygon=g.invent({create:"polygon",inherit:g.Shape,construct:{polygon:function(t){return this.put(new g.Polygon).plot(t)}}}),g.extend(g.Polyline,g.Polygon,{array:function(){return this._array||(this._array=new g.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new g.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=h(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),g.extend(g.Line,g.Polyline,g.Polygon,{morphArray:g.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),g.Path=g.invent({create:"path",inherit:g.Shape,extend:{morphArray:g.PathArray,array:function(){return this._array||(this._array=new g.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new g.PathArray(t))},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=h(this,t,e);return this.attr("d",this.array().size(i.width,i.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new g.Path).plot(t)}}}),g.Image=g.invent({create:"image",inherit:g.Shape,extend:{load:function(t){if(!t)return this;var i=this,n=e.createElement("img");return n.onload=function(){var e=i.parent(g.Pattern);null!==e&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),e&&0==e.width()&&0==e.height()&&e.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:n.width,height:n.height,ratio:n.width/n.height,url:t}))},n.onerror=function(t){"function"==typeof i._error&&i._error.call(i,t)},this.attr("href",n.src=this.src=t,g.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new g.Image).load(t).size(e||0,i||e||0)}}}),g.Text=g.invent({create:function(){this.constructor.call(this,g.create("text")),this.dom.leading=new g.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",g.defaults.attrs["font-family"])},inherit:g.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines().each(function(){this.dom.newLined&&this.x(t)}),this.attr("x",t))},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t?t+i:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if("undefined"==typeof t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[y[t][e]]&&this.attr(y.prefix(t,y[t][e]),i[y[t][e]]);return this},g.extend(g.Element,g.FX,i)}),g.extend(g.Element,g.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({skew:t,cx:e,cy:i}):this.transform({skewX:t,skewY:e,cx:i,cy:n})},scale:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({scale:t,cx:e,cy:i}):this.transform({scaleX:t,scaleY:e,cx:i,cy:n})},translate:function(t,e){return this.transform({x:t,y:e})},flip:function(t,e){return this.transform({flip:t,offset:e})},matrix:function(t){return this.attr("transform",new g.Matrix(t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x((this instanceof g.FX?0:this.x())+t,!0)},dy:function(t){return this.y((this instanceof g.FX?0:this.y())+t,!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),g.extend(g.Rect,g.Ellipse,g.Circle,g.Gradient,g.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new g.Number(t)):this.rx(t).ry(null==e?t:e)}}),g.extend(g.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),g.extend(g.Parent,g.Text,g.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),g.Set=g.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;t-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new g.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new g.Set(t)}}}),g.FX.Set=g.invent({create:function(t){this.set=t}}),g.Set.inherit=function(){var t,e=[];for(var t in g.Shape.prototype)"function"==typeof g.Shape.prototype[t]&&"function"!=typeof g.Set.prototype[t]&&e.push(t);e.forEach(function(t){g.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),g.get=function(t){var i=e.getElementById(v(t)||t);return g.adopt(i)},g.select=function(t,i){return new g.Set(g.utils.map((i||e).querySelectorAll(t),function(t){return g.adopt(t)}))},g.extend(g.Parent,{select:function(t){return g.select(t,this.node)}});var w="abcdef".split("");if("function"!=typeof b){var b=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var n=e.createEvent("CustomEvent");return n.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),n};b.prototype=t.Event.prototype,t.CustomEvent=b}return function(e){for(var i=0,n=["moz","webkit"],s=0;s=0;e--)t.childNodes[e]instanceof SVGElement&&m(t.childNodes[e]);return A.adopt(t).id(A.eid(t.nodeName))}function x(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){var e=t.toString().match(A.regex.reference);if(e)return e[1]}function y(t){t=new A.PathArray(t);var e,i,n,r,s,a,o,h,u,l=[],c=null,f=null,d=null,p=null,m=null;for(e=0,i=t.value.length;e1&&(n=Math.sqrt(n),C=n*C,k=n*k),r=(new A.Matrix).rotate(N).scale(1/C,1/k).rotate(-N),_=_.transform(r),F=F.transform(r),s=[F.x-_.x,F.y-_.y],o=s[0]*s[0]+s[1]*s[1],a=Math.sqrt(o),s[0]/=a,s[1]/=a,h=o<4?Math.sqrt(1-o/4):0,S===E&&(h*=-1),u=new A.Point((F.x+_.x)/2+h*-s[1],(F.y+_.y)/2+h*s[0]),l=_.minus(u),c=F.minus(u),f=Math.acos(l.x/l.norm()),l.y<0&&(f*=-1),d=Math.acos(c.x/c.norm()),c.y<0&&(d*=-1),E&&f>d&&(d+=2*Math.PI),!E&&f.001&&u--;)r/=2,n+=o<0?r:-r,h=b(t,e,n),s=M(h[0],h[1]),o=s-a;return h}function k(t){var e,i,n,r,s=P(t),a=s.total,o=0,h=[];for(e=0,i=s.length;e=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];A.Set&&A.Set.inherit&&A.Set.inherit()},A.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,A.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&A.extend(e,t.extend),t.construct&&A.extend(t.parent||A.Container,t.construct),e},A.adopt=function(t){if(!t)return null;if(t.instance)return t.instance;var e;return e="svg"==t.nodeName?t.parentNode instanceof SVGElement?new A.Nested:new A.Doc:"linearGradient"==t.nodeName?new A.Gradient("linear"):"radialGradient"==t.nodeName?new A.Gradient("radial"):A[s(t.nodeName)]?new(A[s(t.nodeName)]):new A.Element(t),e.type=t.nodeName,e.node=t,t.instance=e,e instanceof A.Doc&&e.namespace().defs(),e.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}),e},A.prepare=function(){var t=e.getElementsByTagName("body")[0],i=(t?new A.Doc(t):new A.Doc(e.documentElement).nested()).size(2,0);A.parser={body:t||e.documentElement,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:i.path().node,native:A.create("svg")}},A.parser={native:A.create("svg")},e.addEventListener("DOMContentLoaded",function(){A.parser.draw||A.prepare()},!1),A.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,matrix:/matrix\(|\)/g,matrixElements:/,*\s+|,/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,negExp:/e\-/gi,comma:/,/g,hyphen:/\-/g,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,whitespaces:/\s+/,X:/X/g},A.utils={map:function(t,e){var i,n=t.length,r=[];for(i=0;i1?1:t,new A.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),A.Color.test=function(t){return t+="",A.regex.isHex.test(t)||A.regex.isRgb.test(t)},A.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},A.Color.isColor=function(t){return A.Color.isRgb(t)||A.Color.test(t)},A.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},A.extend(A.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.length=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y;return this},bbox:function(){return A.parser.poly.setAttribute("points",this.toString()),A.parser.poly.getBBox()}}),A.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},A.PathArray.prototype=new A.Array,A.extend(A.PathArray,{toString:function(){return p(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,r=this.value.length-1;r>=0;r--)n=this.value[r][0],"M"==n||"L"==n||"T"==n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==n?this.value[r][1]+=t:"V"==n?this.value[r][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"==n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"==n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},haveSameCommands:function(t){var e,i,n;for(t=new A.PathArray(t),n=this.value.length===t.value.length,e=0,i=this.value.length;n&&ei.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2>")+"";for(var n=0,r=i.firstChild.childNodes.length;n":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return-Math.cos(t*Math.PI/2)+1}},A.morph=function(t){return function(e,i){return new A.MorphObj(e,i).at(t)}},A.Situation=A.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new A.Number(t.duration).valueOf(),this.delay=new A.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),A.FX=A.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,i){"object"==typeof t&&(e=t.ease,i=t.delay,t=t.duration);var n=new A.Situation({duration:t||1e3,delay:i||0,ease:A.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var e=new A.Situation({duration:t,delay:0,ease:A.easing["-"]});return this.queue(e)},target:function(t){return t&&t instanceof A.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof A.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.situation&&this.situation.stop&&this.situation.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof A.Situation?this.startCurrent():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(t in e.animations)"viewbox"==t?e.animations[t]=this.target().viewbox().morph(e.animations[t]):(e.animations[t].value="plot"==t?this.target().array().value:this.target()[t](),e.animations[t].value.value&&(e.animations[t].value=e.animations[t].value.value),e.animations[t].relative&&(e.animations[t].destination.value=e.animations[t].destination.value+e.animations[t].value));for(t in e.attrs)if(e.attrs[t]instanceof A.Color){var i=new A.Color(this.target().attr(t));e.attrs[t].r=i.r,e.attrs[t].g=i.g,e.attrs[t].b=i.b}else e.attrs[t].value=this.target().attr(t);for(t in e.styles)e.styles[t].value=this.target().style(t);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){return this.active||this.start(),e&&this.clearQueue(),this.active=!1,t&&this.situation&&this.atEnd(),this.stopAnimFrame(),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.atStart()}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},atStart:function(){return this.at(0,!0)},atEnd:function(){return this.situation.loops===!0?this.at(this.situation.loop+1,!0):"number"==typeof this.situation.loops?this.at(this.situation.loops,!0):this.at(1,!0)},at:function(t,e){var i=this.situation.duration/this._speed;return this.absPos=t,e||(this.situation.reversed&&(this.absPos=1-this.absPos),this.absPos+=this.situation.loop),this.situation.start=+new Date-this.absPos*i,this.situation.finish=this.situation.start+i,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.absPos,!0)):this._speed},loop:function(t,e){var i=this.last();return i.loops=null==t||t,i.loop=0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return"undefined"==typeof t?e.reversed=!e.reversed:e.reversed=t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){var e=this.last(),i=function i(n){n.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))};return this.target().on("finished.fx",i),this},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,A.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after(function(){this.off("during.fx",i)})},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,A.morph(e.detail.pos),e.detail.eased,e.detail.situation)};return this.target().off("during.fx",e).on("during.fx",e),this.afterAll(function(){this.off("during.fx",e)})},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,setTimeout(function(){this.start()}.bind(this),0),this},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),this.situation.loops!==!1){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),this.situation.loops===!0||ithis.lastPos&&s<=r&&(this.situation.once[s].call(this.target(),this.pos,r),delete this.situation.once[s]);return this.active&&this.target().fire("during",{pos:this.pos,eased:r,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.target().off(".fx"),this.active=!1),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=r,this):this},eachAt:function(){var t,e,i=this,n=this.target(),r=this.situation;for(t in r.animations)e=[].concat(r.animations[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n[t].apply(n,e);for(t in r.attrs)e=[t].concat(r.attrs[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n.attr.apply(n,e);for(t in r.styles)e=[t].concat(r.styles[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n.style.apply(n,e);if(r.transforms.length){for(e=r.initialTransformation,t=0,len=r.transforms.length;t=0;--e)this[E[e]]=t&&"number"==typeof t[E[e]]?t[E[e]]:i[E[e]]},extend:{extract:function(){var t=u(this,0,1),e=u(this,1,0),i=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(i*Math.PI/180)+this.f*Math.sin(i*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(i*Math.PI/180)+this.e*Math.sin(-i*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),skewX:-i,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:i,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new A.Matrix(this)}},clone:function(){return new A.Matrix(this)},morph:function(t){return this.destination=new A.Matrix(t),this},at:function(t){if(!this.destination)return this;var e=new A.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t});if(this.param&&this.param.to){var i={rotation:this.param.from.rotation+(this.param.to.rotation-this.param.from.rotation)*t,cx:this.param.from.cx,cy:this.param.from.cy};e=e.rotate((this.param.to.rotation-2*this.param.from.rotation)*t,i.cx,i.cy),e.param=i}return e},multiply:function(t){return new A.Matrix(this.native().multiply(c(t).native()))},inverse:function(){return new A.Matrix(this.native().inverse())},translate:function(t,e){return new A.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),this.around(i,n,new A.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=A.utils.radians(t),this.around(e,i,new A.Matrix(Math.cos(t),Math.sin(t),(-Math.sin(t)),Math.cos(t),0,0))},flip:function(t,e){return"x"==t?this.scale(-1,1,e,0):this.scale(1,-1,0,e)},skew:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),t=A.utils.radians(t),e=A.utils.radians(e),this.around(i,n,new A.Matrix(1,Math.tan(e),Math.tan(t),1,0,0))},skewX:function(t,e,i){return this.skew(t,0,e,i)},skewY:function(t,e,i){return this.skew(0,t,e,i)},around:function(t,e,i){return this.multiply(new A.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new A.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=A.parser.native.createSVGMatrix(),e=E.length-1;e>=0;e--)t[E[e]]=this[E[e]];return t},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:A.Element,construct:{ctm:function(){return new A.Matrix(this.node.getCTM())},screenCTM:function(){return new A.Matrix(this.node.getScreenCTM())}}}),A.Point=A.invent({create:function(t,e){var i,n={x:0,y:0};i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:null!=t?{x:t,y:null!=e?e:t}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new A.Point(this)},morph:function(t,e){return this.destination=new A.Point(t,e),this},at:function(t){if(!this.destination)return this;var e=new A.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t});return e},native:function(){var t=A.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new A.Point(this.native().matrixTransform(t.native()))},toArray:function(){return[this.x,this.y]},plus:function(t,e){var i=new A.Point(t,e);return new A.Point(this.x+i.x,this.y+i.y)},minus:function(t,e){var i=new A.Point(t,e);return new A.Point(this.x-i.x,this.y-i.y)},times:function(t,e){var i=new A.Point(t,e);return new A.Point(this.x*i.x,this.y*i.y)},divide:function(t,e){var i=new A.Point(t,e);return new A.Point(this.x/i.x,this.y/i.y)},norm:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},distance:function(t,e){return this.minus(t,e).norm()}}}),A.extend(A.Element,{point:function(t,e){return new A.Point(t,e).transform(this.screenCTM().inverse())}}),A.extend(A.Element,{attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=A.regex.isNumber.test(e[i].nodeValue)?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?A.defaults.attrs[t]:A.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),"fill"!=t&&"stroke"!=t||(A.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof A.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new A.Number(e):A.Color.isColor(e)?e=new A.Color(e):Array.isArray(e)?e=new A.Array(e):e instanceof A.Matrix&&e.param&&(this.param=e.param),"leading"==t?this.leading&&this.leading(e):"string"==typeof i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this}}),A.extend(A.Element,{transform:function(t,e){var i,n=this;if("object"!=typeof t)return i=new A.Matrix(n).extract(),"string"==typeof t?i[t]:i;if(i=new A.Matrix(n),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new A.Matrix(t)):new A.Matrix(t);else if(null!=t.rotation)f(t,n),i=e?i.rotate(t.rotation,t.cx,t.cy):i.rotate(t.rotation-i.extract().rotation,t.cx,t.cy);else if(null!=t.scale||null!=t.scaleX||null!=t.scaleY){if(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,!e){var r=i.extract();t.scaleX=1*t.scaleX/r.scaleX,t.scaleY=1*t.scaleY/r.scaleY}i=i.scale(t.scaleX,t.scaleY,t.cx,t.cy)}else if(null!=t.skew||null!=t.skewX||null!=t.skewY){if(f(t,n),t.skewX=null!=t.skew?t.skew:null!=t.skewX?t.skewX:0,t.skewY=null!=t.skew?t.skew:null!=t.skewY?t.skewY:0,!e){var r=i.extract();i=i.multiply((new A.Matrix).skew(r.skewX,r.skewY,t.cx,t.cy).inverse())}i=i.skew(t.skewX,t.skewY,t.cx,t.cy)}else t.flip?i=i.flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset):null==t.x&&null==t.y||(e?i=i.translate(t.x,t.y):(null!=t.x&&(i.e=t.x),null!=t.y&&(i.f=t.y)));return this.attr("transform",i)}}),A.extend(A.FX,{transform:function(t,e){var i,n=this.target();return"object"!=typeof t?(i=new A.Matrix(n).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new A.Matrix(t):null!=t.rotation?(f(t,n),i=new A.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,i=new A.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new A.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?i=(new A.Matrix).morph((new A.Matrix).flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset)):null==t.x&&null==t.y||(i=new A.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),setTimeout(function(){this.start()}.bind(this),0),this):this)}}),A.extend(A.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){var t=(this.attr("transform")||"").split(/\)\s*,?\s*/).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(A.regex.matrixElements).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(l(e[1])):t[e[0]].apply(t,e[1])},new A.Matrix);return t},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.rect(1,1),n=i.screenCTM().inverse();return i.remove(),this.addTo(t).untransform().transform(n.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),A.Transformation=A.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.create([].slice.call(arguments));if("object"==typeof t)for(var i=0,n=this.arguments.length;i=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return A.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;in/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)}else t="string"==typeof t?t.match(f).map(function(t){return parseFloat(t)}):Array.isArray(t)?t:"object"==typeof t?[t.x,t.y,t.width,t.height]:4==arguments.length?[].slice.call(arguments):u,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3]},extend:{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t){var t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments);return this.destination=new A.ViewBox(t),this},at:function(t){return this.destination?new A.ViewBox([this.x+(this.destination.x-this.x)*t,this.y+(this.destination.y-this.y)*t,this.width+(this.destination.width-this.width)*t,this.height+(this.destination.height-this.height)*t]):this}},parent:A.Container,construct:{viewbox:function(t){return 0==arguments.length?new A.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){A.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),A.listeners=[],A.handlerMap=[],A.listenerId=0,A.on=function(t,e,i,n){var r=i.bind(n||t.instance||t),s=(A.handlerMap.indexOf(t)+1||A.handlerMap.push(t))-1,a=e.split(".")[0],o=e.split(".")[1]||"*";A.listeners[s]=A.listeners[s]||{},A.listeners[s][a]=A.listeners[s][a]||{},A.listeners[s][a][o]=A.listeners[s][a][o]||{},i._svgjsListenerId||(i._svgjsListenerId=++A.listenerId),A.listeners[s][a][o][i._svgjsListenerId]=r,t.addEventListener(a,r,!1)},A.off=function(t,e,i){var n=A.handlerMap.indexOf(t),r=e&&e.split(".")[0],s=e&&e.split(".")[1];if(n!=-1)if(i){if("function"==typeof i&&(i=i._svgjsListenerId),!i)return;A.listeners[n][r]&&A.listeners[n][r][s||"*"]&&(t.removeEventListener(r,A.listeners[n][r][s||"*"][i],!1),delete A.listeners[n][r][s||"*"][i])}else if(s&&r){if(A.listeners[n][r]&&A.listeners[n][r][s]){for(i in A.listeners[n][r][s])A.off(t,[r,s].join("."),i);delete A.listeners[n][r][s]}}else if(s)for(e in A.listeners[n])for(namespace in A.listeners[n][e])s===namespace&&A.off(t,[e,s].join("."));else if(r){if(A.listeners[n][r]){for(namespace in A.listeners[n][r])A.off(t,[r,namespace].join("."));delete A.listeners[n][r]}}else{for(e in A.listeners[n])A.off(t,e);delete A.listeners[n]}},A.extend(A.Element,{on:function(t,e,i){return A.on(this.node,t,e,i),this},off:function(t,e){return A.off(this.node,t,e),this},fire:function(t,e){return t instanceof Event?this.node.dispatchEvent(t):this.node.dispatchEvent(new T(t,{detail:e})),this}}),A.Defs=A.invent({create:"defs",inherit:A.Container}),A.G=A.invent({create:"g",inherit:A.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)},y:function(t){return null==t?this.transform("y"):this.transform({y:t-this.y()},!0)},cx:function(t){return null==t?this.gbox().cx:this.x(t-this.gbox().width/2)},cy:function(t){return null==t?this.gbox().cy:this.y(t-this.gbox().height/2)},gbox:function(){var t=this.bbox(),e=this.transform();return t.x+=e.x,t.x2+=e.x,t.cx+=e.x,t.y+=e.y,t.y2+=e.y,t.cy+=e.y,t}},construct:{group:function(){return this.put(new A.G)}}}),A.extend(A.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof A.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof A.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),A.Mask=A.invent({create:function(){this.constructor.call(this,A.create("mask")),this.targets=[]},inherit:A.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new A.Mask)}}}),A.extend(A.Element,{maskWith:function(t){return this.masker=t instanceof A.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),A.ClipPath=A.invent({create:function(){this.constructor.call(this,A.create("clipPath")),this.targets=[]},inherit:A.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return this.targets=[],this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new A.ClipPath)}}}),A.extend(A.Element,{clipWith:function(t){return this.clipper=t instanceof A.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),A.Gradient=A.invent({create:function(t){this.constructor.call(this,A.create(t+"Gradient")),this.type=t},inherit:A.Container,extend:{at:function(t,e,i){return this.put(new A.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),A.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),A.extend(A.Gradient,A.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new A.Number(t),fy:new A.Number(e)}):this.attr({x1:new A.Number(t),y1:new A.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new A.Number(t),cy:new A.Number(e)}):this.attr({x2:new A.Number(t),y2:new A.Number(e)})}}),A.extend(A.Defs,{gradient:function(t,e){return this.put(new A.Gradient(t)).update(e)}}),A.Stop=A.invent({create:"stop",inherit:A.Element,extend:{update:function(t){return("number"==typeof t||t instanceof A.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new A.Number(t.offset)),this}}}),A.Pattern=A.invent({create:"pattern",inherit:A.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),A.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),A.extend(A.Defs,{pattern:function(t,e,i){return this.put(new A.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),A.Doc=A.invent({create:function(t){t&&(t="string"==typeof t?e.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,A.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:A.Container,extend:{namespace:function(){return this.attr({xmlns:A.ns,version:"1.1"}).attr("xmlns:xlink",A.xlink,A.xmlns).attr("xmlns:svgjs",A.svgjs,A.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=A.adopt(t):this._defs=new A.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode},spof:function(t){var e=this.node.getScreenCTM();return e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this}}}),A.Shape=A.invent({create:function(t){this.constructor.call(this,t)},inherit:A.Element}),A.Bare=A.invent({create:function(t,e){if(this.constructor.call(this,A.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:A.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),A.extend(A.Parent,{element:function(t,e){return this.put(new A.Bare(t,e))},symbol:function(){return this.defs().element("symbol",A.Container)}}),A.Use=A.invent({create:"use",inherit:A.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,A.xlink)}},construct:{use:function(t,e){return this.put(new A.Use).element(t,e)}}}),A.Rect=A.invent({create:"rect",inherit:A.Shape,construct:{rect:function(t,e){return this.put(new A.Rect).size(t,e)}}}),A.Circle=A.invent({create:"circle",inherit:A.Shape,construct:{circle:function(t){return this.put(new A.Circle).rx(new A.Number(t).divide(2)).move(0,0)}}}),A.extend(A.Circle,A.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),A.Ellipse=A.invent({create:"ellipse",inherit:A.Shape,construct:{ellipse:function(t,e){return this.put(new A.Ellipse).size(t,e).move(0,0)}}}),A.extend(A.Ellipse,A.Rect,A.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),A.extend(A.Circle,A.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new A.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new A.Number(t).divide(2))},size:function(t,e){var i=h(this,t,e);return this.rx(new A.Number(i.width).divide(2)).ry(new A.Number(i.height).divide(2))}}),A.Line=A.invent({create:"line",inherit:A.Shape,extend:{array:function(){return new A.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return t="undefined"!=typeof e?{x1:t,y1:e,x2:i,y2:n}:new A.PointArray(t).toLine(),this.attr(t)},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=h(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return this.put(new A.Line).plot(t,e,i,n)}}}),A.Polyline=A.invent({create:"polyline",inherit:A.Shape,construct:{polyline:function(t){return this.put(new A.Polyline).plot(t)}}}),A.Polygon=A.invent({create:"polygon",inherit:A.Shape,construct:{polygon:function(t){return this.put(new A.Polygon).plot(t)}}}),A.extend(A.Polyline,A.Polygon,{array:function(){return this._array||(this._array=new A.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new A.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=h(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),A.extend(A.Line,A.Polyline,A.Polygon,{morphArray:A.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),A.Path=A.invent({create:"path",inherit:A.Shape,extend:{morphArray:A.PathArray,array:function(){return this._array||(this._array=new A.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new A.PathArray(t))},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=h(this,t,e);return this.attr("d",this.array().size(i.width,i.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new A.Path).plot(t)}}}),A.Image=A.invent({create:"image",inherit:A.Shape,extend:{load:function(t){if(!t)return this;var i=this,n=e.createElement("img");return n.onload=function(){var e=i.parent(A.Pattern);null!==e&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),e&&0==e.width()&&0==e.height()&&e.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:n.width,height:n.height,ratio:n.width/n.height,url:t}))},n.onerror=function(t){"function"==typeof i._error&&i._error.call(i,t)},this.attr("href",n.src=this.src=t,A.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new A.Image).load(t).size(e||0,i||e||0)}}}),A.Text=A.invent({create:function(){this.constructor.call(this,A.create("text")),this.dom.leading=new A.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",A.defaults.attrs["font-family"])},inherit:A.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines().each(function(){this.dom.newLined&&this.x(t)}),this.attr("x",t))},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t?t+i:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if("undefined"==typeof t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[S[t][e]]&&this.attr(S.prefix(t,S[t][e]),i[S[t][e]]);return this},A.extend(A.Element,A.FX,i)}),A.extend(A.Element,A.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){ +return 1==arguments.length||3==arguments.length?this.transform({skew:t,cx:e,cy:i}):this.transform({skewX:t,skewY:e,cx:i,cy:n})},scale:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({scale:t,cx:e,cy:i}):this.transform({scaleX:t,scaleY:e,cx:i,cy:n})},translate:function(t,e){return this.transform({x:t,y:e})},flip:function(t,e){return this.transform({flip:t,offset:e})},matrix:function(t){return this.attr("transform",new A.Matrix(t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x((this instanceof A.FX?0:this.x())+t,!0)},dy:function(t){return this.y((this instanceof A.FX?0:this.y())+t,!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),A.extend(A.Rect,A.Ellipse,A.Circle,A.Gradient,A.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new A.Number(t)):this.rx(t).ry(null==e?t:e)}}),A.extend(A.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),A.extend(A.Parent,A.Text,A.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),A.Set=A.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;t-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new A.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new A.Set(t)}}}),A.FX.Set=A.invent({create:function(t){this.set=t}}),A.Set.inherit=function(){var t,e=[];for(var t in A.Shape.prototype)"function"==typeof A.Shape.prototype[t]&&"function"!=typeof A.Set.prototype[t]&&e.push(t);e.forEach(function(t){A.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),A.get=function(t){var i=e.getElementById(v(t)||t);return A.adopt(i)},A.select=function(t,i){return new A.Set(A.utils.map((i||e).querySelectorAll(t),function(t){return A.adopt(t)}))},A.extend(A.Parent,{select:function(t){return A.select(t,this.node)}});var E="abcdef".split("");if(A.utils.makePathsMorphable=function(t,e){var i,n,r,s,a,o,h,u,l,c,f,d,p,m,x;for(i=y(t),s=y(e),n=k(i),a=k(s),r=[],o=[],h=0,u=n.length,l=0,c=a.length;ha[l]?r.push(a[l++]):o.push(n[h++]);for(r=r.concat(a.slice(l)),o=o.concat(n.slice(h)),N(i,n,r),N(s,a,o),f=i,i=[],p=f[h=0],d=s,s=[],m=d[l=0];p&&m;)i.push(p),s.push(m),u=p.length,c=m.length,u>c?(x=p[c-1],p=p.splice(c),p.unshift(x),m=d[++l]):u destinationPositions[j]){ + sourcePositionsToSplitAt.push(destinationPositions[j++]) + } else { + destinationPositionsToSplitAt.push(sourcePositions[i++]) + } + } + // If there are still some destination positions left, they all are not in the source and vice versa + sourcePositionsToSplitAt = sourcePositionsToSplitAt.concat(destinationPositions.slice(j)) + destinationPositionsToSplitAt = destinationPositionsToSplitAt.concat(sourcePositions.slice(i)) + + // Split the source and the destination at the positions they don't have in common + cspSplitAtPositions(source, sourcePositions, sourcePositionsToSplitAt) + cspSplitAtPositions(destination, destinationPositions, destinationPositionsToSplitAt) + + + // Break paths so that corresponding subpaths have an equal number of segments + s = source, source = [], sourceSubpath = s[i = 0] + d = destination, destination = [], destinationSubpath = d[j = 0] + while (sourceSubpath && destinationSubpath) { + // Push REFERENCES to the current subpath arrays in their respective array + source.push(sourceSubpath) + destination.push(destinationSubpath) + + il = sourceSubpath.length + jl = destinationSubpath.length + + // If the current subpath of the source and the current subpath of the destination don't + // have the same length, that mean that the biggest of the two must be split in two + if(il > jl) { + lastSegPt = sourceSubpath[jl-1] + // Perform the split using splice that change the content of the array by removing elements and returning them in an array + sourceSubpath = sourceSubpath.splice(jl) + sourceSubpath.unshift(lastSegPt) // The last segment point is duplicated since these two segments must be joined together + destinationSubpath = d[++j] // This subpath has been accounted for, past to the next + } else if(il < jl) { + lastSegPt = destinationSubpath[il-1] + destinationSubpath = destinationSubpath.splice(il) + destinationSubpath.unshift(lastSegPt) + sourceSubpath = s[++i] + } else { + sourceSubpath = s[++i] + destinationSubpath = d[++j] + } + } + + // Convert in path array and return + return [uncubicSuperPath(source), uncubicSuperPath(destination)] +} + + + + + + +// This function converts every segment of a path array into equivalent cubic Bezier curves +// and return the results in a 3 dimensional array that have the following hierarchy: +// Cubic super path: [ ] +// Segments: [ ] ... +// Segment points: [SVG.Point, SVG.Point, SVG.Point] ... +// +// A segment point is a point with the two control points that are attached to it: +// [First control point, Point, Second control point] +// +// If the passed path array cannot be converted in a cubic super path, this function return an empty array. +function cubicSuperPath(pathArray) { + pathArray = new SVG.PathArray(pathArray) + + var cubicSP = [] + , subpath = null + , subpathStartPt = null + , lastPt = null + , lastCtrlPt = null + , i, il, cmd = null, params, lastCmd + , start, control, end + , arcSegPoints, segPt + + for (i = 0, il = pathArray.value.length; i < il; i++) { + lastCmd = cmd + cmd = pathArray.value[i][0] + params = pathArray.value[i].slice(1) + + switch (cmd) { + case 'M': // moveto + // Parameters: x y + if (lastPt) { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + subpath = [] + cubicSP.push(subpath) // Push a reference to the current subpath array in the cubic super path array + subpathStartPt = new SVG.Point(params) + lastPt = subpathStartPt.clone() + lastCtrlPt = subpathStartPt.clone() + break + + case 'L': // lineto + // Parameters: x y + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(params) + lastCtrlPt = lastPt.clone() + break + + case 'H': // horizontal lineto + // Parameters: x + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(params[0], lastPt.y) + lastCtrlPt = lastPt.clone() + break + + case 'V': // vertical lineto + // Parameters: y + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + lastPt = new SVG.Point(lastPt.x, params[0]) + lastCtrlPt = lastPt.clone() + break + + case 'C': // curveto + // Parameters: x1 y1 x2 y2 x y + subpath.push([lastCtrlPt, lastPt, new SVG.Point(params.slice(0,2))]) + lastPt = new SVG.Point(params.slice(4,6)) + lastCtrlPt = new SVG.Point(params.slice(2,4)) + break + + case 'S': // shorthand/smooth curveto + // Parameters: x2 y2 x y + // For this version of curveto, the first control point is the reflection of the second control point on the previous command relative to the current point + // If the previous command is not a curveto command, then the first control point is the same as the current point + if(lastCmd === 'C' || lastCmd === 'S') { + subpath.push([lastCtrlPt, lastPt, lastPt.times(2).minus(lastCtrlPt)]) + } else { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + lastPt = new SVG.Point(params.slice(2,4)) + lastCtrlPt = new SVG.Point(params.slice(0,2)) + break + + case 'Q': // quadratic Bezier curveto + // Parameters: x1 y1 x y + // For an explanation of the method used, see: https://pomax.github.io/bezierinfo/#reordering + start = lastPt + control = new SVG.Point(params.slice(0,2)) + end = new SVG.Point(params.slice(2,4)) + + subpath.push([lastCtrlPt, start, start.times(1/3).plus(control.times(2/3))]) + lastPt = end + lastCtrlPt = control.times(2/3).plus(end.times(1/3)) + break + + case 'T': // shorthand/smooth quadratic Bézier curveto + // Parameters: x y + // For this version of quadratic Bézier curveto, the control point is the reflection of the control point on the previous command relative to the current point + // If the previous command is not a quadratic Bézier curveto command, then the control point is the same as the current point + start = lastPt + if(lastCmd === 'Q' || lastCmd === 'T') { + control = start.times(2).minus(control) + } else { + control = start + } + end = new SVG.Point(params.slice(0,2)) + + subpath.push([lastCtrlPt, start, start.times(1/3).plus(control.times(2/3))]) + lastPt = end + lastCtrlPt = control.times(2/3).plus(end.times(1/3)) + break + + case 'A': // elliptical arc + // Parameters: rx ry x-axis-rotation large-arc-flag sweep-flag x y + arcSegPoints = arcToPath(lastPt, params) + arcSegPoints[0][0] = lastCtrlPt + segPt = arcSegPoints.pop() + lastPt = segPt[1] + lastCtrlPt = segPt[0] + Array.prototype.push.apply(subpath, arcSegPoints) + break + + case 'Z': // closepath + // Parameters: none + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + // Close the path only if it is not already closed + if(lastPt.x != subpathStartPt.x && lastPt.y != subpathStartPt.y) { + lastPt = subpathStartPt + lastCtrlPt = subpathStartPt.clone() + } else { + lastPt = null + lastCtrlPt = null + } + break + } + } + + // Push final segment point if any + if(lastPt) { + subpath.push([lastCtrlPt, lastPt, lastPt.clone()]) + } + + return cubicSP +} + + +// This function convert a cubic super path into a path array +function uncubicSuperPath (cubicSP) { + var i, il, j, jl, array = [], pathArray = new SVG.PathArray, subpath + + for (i = 0, il = cubicSP.length; i < il; i++) { + subpath = cubicSP[i] + + if (subpath.length) { + array.push(['M'].concat(subpath[0][1].toArray())) + + for (j = 1, jl = subpath.length; j < jl; j++) { + array.push(['C'].concat(subpath[j-1][2].toArray(), subpath[j][0].toArray(), subpath[j][1].toArray())) + } + } + } + + // Directly modify the value of a path array, this is done this way for performance + pathArray.value = array + return pathArray +} + +// Convert an arc segment into equivalent cubic Bezier curves +// Depending on the arc, up to 4 curves might be used to represent it since a +// curve gives a good approximation for only a quarter of an ellipse +// The curves are returned as an array of segment points: +// [ [SVG.Point, SVG.Point, SVG.Point] ... ] +function arcToPath(lastPt, params) { + // Parameters extraction, handle out-of-range parameters as specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters + var rx = Math.abs(params[0]), ry = Math.abs(params[1]), xAxisRotation = params[2] % 360 + , largeArcFlag = params[3], sweepFlag = params[4], x2 = params[5], y2 = params[6] + , A = lastPt, B = new SVG.Point(x2, y2) + , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd + , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il + + // Ensure radii are non-zero + if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) { + // treat this arc as a straight line segment + return [[A, A.clone(), A.clone()], [B, B.clone(), B.clone()]] + } + + // Ensure radii are large enough using the algorithm provided in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii + primedCoord = A.minus(B).divide(2).transform(new SVG.Matrix().rotate(xAxisRotation)) + lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry) + if(lambda > 1) { + lambda = Math.sqrt(lambda) + rx = lambda*rx + ry = lambda*ry + } + + // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation) + A = A.transform(mat) + B = B.transform(mat) + + // Calculate the horizontal and vertical distance between the initial and final point of the arc + k = [B.x-A.x, B.y-A.y] + + // Find the length of the chord formed by A and B + cSquare = k[0]*k[0] + k[1]*k[1] + c = Math.sqrt(cSquare) + + // Calculate the ratios of the horizontal and vertical distance on the length of the chord + k[0] /= c + k[1] /= c + + // Calculate the distance between the circle center and the chord midpoint + // using this formula: t = sqrt(r^2 - c^2 / 4) + // where t is the distance between the cirle center and the chord midpoint, + // r is the rayon of the circle and c is the chord length + // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php + // Because of the imprecision of floating point numbers, cSquare might end + // up being slightly above 4 which would result in a negative radicand + // To prevent that, a test is made before computing the square root + t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0 + + // For most situations, there are actually two different ellipses that + // satisfy the constraints imposed by the points A and B, the radii rx and ry, + // and the xAxisRotation + // When the flags largeArcFlag and sweepFlag are equal, it means that the + // second ellipse is used as a solution + // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands + if(largeArcFlag === sweepFlag) { + t *= -1 + } + + // Calculate the coordinates of the center of the circle from the midpoint of the chord + // This is done by multiplying the ratios calculated previously by the distance between + // the circle center and the chord midpoint and using these values to go from the midpoint + // to the center of the circle + // The negative of the vertical distance ratio is used to modify the x coordinate while + // the horizontal distance ratio is used to modify the y coordinate + // That is because the center of the circle is perpendicular to the chord and perpendicular + // lines are negative reciprocals + O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0]) + // Move the center of the circle at the origin + OA = A.minus(O) + OB = B.minus(O) + + // Calculate the start and end angle + tetaStart = Math.acos(OA.x/OA.norm()) + if (OA.y < 0) { + tetaStart *= -1 + } + tetaEnd = Math.acos(OB.x/OB.norm()) + if (OB.y < 0) { + tetaEnd *= -1 + } + + // If sweep-flag is '1', then the arc will be drawn in a "positive-angle" direction, + // make sure that the end angle is above the start angle + if (sweepFlag && tetaStart > tetaEnd) { + tetaEnd += 2*Math.PI + } + // If sweep-flag is '0', then the arc will be drawn in a "negative-angle" direction, + // make sure that the end angle is below the start angle + if (!sweepFlag && tetaStart < tetaEnd) { + tetaEnd -= 2*Math.PI + } + + // Find the number of Bezier curves that are required to represent the arc + // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle + nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI) + + // Calculate the coordinates of the points of all the Bezier curves required to represent the arc + // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic + arcSegPoints = [] + angle = tetaStart + deltaTeta = (tetaEnd-tetaStart)/nbSectors + f = 4*Math.tan(deltaTeta/4)/3 + for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint + cosAngle = Math.cos(angle) + sinAngle = Math.sin(angle) + + pt = O.plus(cosAngle, sinAngle) + arcSegPoints[i] = [pt.plus(+f*sinAngle, -f*cosAngle), pt, pt.plus(-f*sinAngle, +f*cosAngle)] + + angle += deltaTeta + } + + // Remove the first control point of the first segment point and remove the second control point of the last segment point + // These two control points are not used in the approximation of the arc, that is why they are removed + arcSegPoints[0][0] = arcSegPoints[0][1].clone() + arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone() + + // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation) + for (i = 0, il = arcSegPoints.length; i < il; i++) { + arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat) + arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat) + arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat) + } + + return arcSegPoints +} + + +// Use de Casteljau's algorithm to split a cubic Bezier curve +// For a description of the algorithm, see: https://pomax.github.io/bezierinfo/#decasteljau +// Return an array of 3 segment points +function cspSegSplit(segPt1, segPt2, t) { + segPt1 = [segPt1[0].clone(), segPt1[1].clone(), segPt1[2].clone()] + segPt2 = [segPt2[0].clone(), segPt2[1].clone(), segPt2[2].clone()] + + var m1 = segPt1[1].morph(segPt1[2]).at(t) + , m2 = segPt1[2].morph(segPt2[0]).at(t) + , m3 = segPt2[0].morph(segPt2[1]).at(t) + , m4 = m1.morph(m2).at(t) + , m5 = m2.morph(m3).at(t) + , m = m4.morph(m5).at(t) + + return [[segPt1[0], segPt1[1], m1], [m4, m, m5], [m3, segPt2[1], segPt2[2]]] +} + + +// Find the length of a cubic Bezier curve using the built-in method getTotalLength of SVGPathElement +// For more info, see: https://www.w3.org/TR/SVG11/paths.html#InterfaceSVGPathElement +function cspSegLength(segPt1, segPt2) { + var path = document.createElementNS(SVG.ns, "path") + , d = ['M', segPt1[1].toArray(), 'C', segPt1[2].toArray(), segPt2[0].toArray(), segPt2[1].toArray()].join(' ') + + path.setAttribute('d', d) + + return path.getTotalLength() +} + + +// Find the length of all the cubic Bezier curves of a cubic super path and return +// the results in a 2 dimensional array that have the following hierarchy: +// Cubic super path lengths: [ ] +// Segments lengths: [ ] ... +// Cubic Bezier curves length: Number ... +// +// On the returned array, the property total is set to the sum of all the lengths +function cspLengths(cubicSP) { + var total = 0 + , subpath, lengths = [], lengthsSubpath, length + , i, il, j, jl + + for (i = 0, il = cubicSP.length; i < il; i++) { + subpath = cubicSP[i] + lengthsSubpath = [] + lengths[i] = lengthsSubpath // Save a reference to the current subpath lengths array in the cubic super path lengths array + + for (j = 1, jl = subpath.length; j < jl; j++) { + length = cspSegLength(subpath[j-1], subpath[j]) + lengthsSubpath[j-1] = length + total += length + } + } + + lengths.total = total + return lengths +} + + +// Split a cubic Bezier curve at the given length ratio +// Return an array of 3 segment points +function cspSegSplitAtLengthRatio(segPt1, segPt2, lengthRatio) { + var t = 1.0 + , tdiv = t + , currentLength = cspSegLength(segPt1, segPt2) + , targetLength = lengthRatio * currentLength + , diff = currentLength - targetLength + , split = cspSegSplit(segPt1, segPt2, t) + , maxNbLoops = 4096 // For not getting stuck in an infinite loop + + while (Math.abs(diff) > 0.001 && maxNbLoops--) { + tdiv /= 2 + t += (diff < 0) ? tdiv : -tdiv + split = cspSegSplit(segPt1, segPt2, t) + currentLength = cspSegLength(split[0], split[1]) + diff = currentLength - targetLength + } + + return split +} + + + +// Find the position relative to the total length of the endpoint of all the cubic Bezier curves +// of a cubic super path and return the results in a 1 dimensional array +function cspPositions(cubicSP) { + var lengths = cspLengths(cubicSP), total = lengths.total + , pos = 0, positions = [] + , i, il, j, jl + + for (i = 0, il = lengths.length; i < il; i++) { + for (j = 0, jl = lengths[i].length; j < jl; j++) { + pos += lengths[i][j] / total + positions.push(pos) + } + } + + return positions +} + +// Split the passed cubic super path at the specified positions and return the results as a new cubic super path +// For performance reasons, the positions of the passed cubic super path must also be provided +function cspSplitAtPositions(cubicSP, positions, positionsToSplitAt){ + var subpath, newSubpath + , accumNbPositions = 0, segPt, lengthRatio, split, pos, prevPos + , i, il, j, jl // indexes on the cubicSP array + , k = 0 // index on the positions array + , l = 0, ll = positionsToSplitAt.length + + for (i = 0, il = cubicSP.length; i < il && l < ll; i++) { + subpath = cubicSP[i] + // The positions are only for the endpoints of the cubic Bezier curves, so + // a subpath need at least 2 segment points for a position to be on it + if(subpath.length < 2) {continue} + // Test if there are splits to be performed on the current subpath + if(positionsToSplitAt[l] < positions[accumNbPositions + subpath.length-2]) { + k = accumNbPositions + newSubpath = [] + cubicSP[i] = newSubpath // Save a reference to the new current subpath array in the cubic super path array + pos = positions[k-1] || 0 + + // Recopy the content of the current subpath, performing splits where necessary + newSubpath.push(subpath[0]) + for (j = 1, jl = subpath.length; j < jl; j++) { + prevPos = pos + pos = positions[k++] + segPt = subpath[j] + + while(l < ll && positionsToSplitAt[l] < pos) { + lengthRatio = (positionsToSplitAt[l] - prevPos) / (pos - prevPos) + split = cspSegSplitAtLengthRatio(newSubpath[newSubpath.length-1], segPt, lengthRatio) + newSubpath[newSubpath.length-1] = split[0] + newSubpath.push(split[1]) + segPt = split[2] + prevPos = positionsToSplitAt[l++] + } + + newSubpath.push(segPt) + } + } + + // -1 because positions are only for endpoints of Bezier curves + accumNbPositions += subpath.length - 1 + } +} diff --git a/src/patharray.js b/src/patharray.js index 90d0558..c478b9e 100644 --- a/src/patharray.js +++ b/src/patharray.js @@ -100,6 +100,71 @@ SVG.extend(SVG.PathArray, { return this } + // Test if the passed path array use the same commands as this path array +, haveSameCommands: function(pathArray) { + var i, il, haveSameCommands + + pathArray = new SVG.PathArray(pathArray) + + haveSameCommands = this.value.length === pathArray.value.length + for(i = 0, il = this.value.length; haveSameCommands && i < il; i++) { + haveSameCommands = this.value[i][0] === pathArray.value[i][0] + } + + return haveSameCommands + } + // Make path array morphable +, morph: function(pathArray) { + var pathsMorphable + + this.destination = new SVG.PathArray(pathArray) + + if(this.haveSameCommands(this.destination)) { + this.sourceMorphable = this + this.destinationMorphable = this.destination + } else { + pathsMorphable = SVG.utils.makePathsMorphable(this.value, this.destination) + this.sourceMorphable = pathsMorphable[0] + this.destinationMorphable = pathsMorphable[1] + } + + return this + } + // Get morphed path array at given position +, at: function(pos) { + if(pos === 1) { + return this.destination + } else if(pos === 0) { + return this + } else { + var sourceArray = this.sourceMorphable.value + , destinationArray = this.destinationMorphable.value + , array = [], pathArray = new SVG.PathArray() + , i, il, j, jl + + // Animate has specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/paths.html#PathElement + for (i = 0, il = sourceArray.length; i < il; i++) { + array[i] = [sourceArray[i][0]] + for(j=1, jl = sourceArray[i].length; j < jl; j++) { + array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos + } + // For the two flags of the elliptical arc command, the SVG spec say: + // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true + // Elliptical arc command as an array followed by corresponding indexes: + // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] + // 0 1 2 3 4 5 6 7 + if(array[i][0] === 'A') { + array[i][4] = +(array[i][4] != 0) + array[i][5] = +(array[i][5] != 0) + } + } + + // Directly modify the value of a path array, this is done this way for performance + pathArray.value = array + return pathArray + } + } // Absolutize and parse path to array , parse: function(array) { // if it's already a patharray, no need to parse it @@ -131,7 +196,7 @@ SVG.extend(SVG.PathArray, { array.splice.apply(array, [i, 1].concat(first, split.map(function(el){ return '.'+el }))) // add first and all other entries back to array } } - + }else{ array = array.reduce(function(prev, curr){ return [].concat.apply(prev, curr) @@ -240,4 +305,4 @@ SVG.extend(SVG.PathArray, { return SVG.parser.path.getBBox() } -}) \ No newline at end of file +}) diff --git a/src/point.js b/src/point.js index 8d1dae9..226f4e0 100644 --- a/src/point.js +++ b/src/point.js @@ -2,15 +2,15 @@ SVG.Point = SVG.invent({ // Initialize create: function(x,y) { var i, source, base = {x:0, y:0} - + // ensure source as object source = Array.isArray(x) ? {x:x[0], y:x[1]} : typeof x === 'object' ? {x:x.x, y:x.y} : - y != null ? - {x:x, y:y} : base - + x != null ? + {x:x, y:(y != null ? y : x)} : base // If y has no value, then x is used has its value + // This allow element-wise operations to be passed a single number // merge source this.x = source.x this.y = source.y @@ -23,9 +23,9 @@ SVG.Point = SVG.invent({ return new SVG.Point(this) } // Morph one point into another - , morph: function(point) { + , morph: function(x, y) { // store new destination - this.destination = new SVG.Point(point) + this.destination = new SVG.Point(x, y) return this } @@ -57,7 +57,38 @@ SVG.Point = SVG.invent({ , transform: function(matrix) { return new SVG.Point(this.native().matrixTransform(matrix.native())) } - + // return an array of the x and y coordinates + , toArray: function() { + return [this.x, this.y] + } + // perform an element-wise addition with the passed point or number + , plus: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x + point.x, this.y + point.y) + } + // perform an element-wise subtraction with the passed point or number + , minus: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x - point.x, this.y - point.y) + } + // perform an element-wise multiplication with the passed point or number + , times: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x * point.x, this.y * point.y) + } + // perform an element-wise division with the passed point or number + , divide: function(x, y) { + var point = new SVG.Point(x, y) + return new SVG.Point(this.x / point.x, this.y / point.y) + } + // calculate the Euclidean norm + , norm: function() { + return Math.sqrt(this.x*this.x + this.y*this.y) + } + // calculate the distance to the passed point + , distance: function(x, y) { + return this.minus(x, y).norm() + } } }) -- 2.39.5