diff options
| author | Valentin Ionita <valentin.ionita1201@gmail.com> | 2019-06-26 19:35:47 +0000 |
|---|---|---|
| committer | Tavmjong Bah <tavmjong@free.fr> | 2019-06-26 19:35:47 +0000 |
| commit | a9c687c3868780fe00d251d6ad1579674c1ead7f (patch) | |
| tree | 9051db73cdcfbf42e7bde56b16435f10e6944786 /src | |
| parent | Update WIX CPack attributes (diff) | |
| download | inkscape-a9c687c3868780fe00d251d6ad1579674c1ead7f.tar.gz inkscape-a9c687c3868780fe00d251d6ad1579674c1ead7f.zip | |
mesh.js update
Updated formatting and migrated to ES6 syntax for classes, variable declarations,
arrow functions, iterators. Fixed variable redeclarations and undefined
getComputedStyle, removed unnecessary escapes from RegEx.
Used standardjs for linting, but kept the semicolons.
Diffstat (limited to 'src')
| -rw-r--r-- | src/extension/internal/polyfill/README | 5 | ||||
| -rw-r--r-- | src/extension/internal/polyfill/README.md | 16 | ||||
| -rw-r--r-- | src/extension/internal/polyfill/mesh.js | 1961 | ||||
| -rw-r--r-- | src/extension/internal/polyfill/mesh_compressed.include | 2 |
4 files changed, 1163 insertions, 821 deletions
diff --git a/src/extension/internal/polyfill/README b/src/extension/internal/polyfill/README deleted file mode 100644 index b08613df4..000000000 --- a/src/extension/internal/polyfill/README +++ /dev/null @@ -1,5 +0,0 @@ - -This directory contains JavaScript "Polyfills" to support rendering of SVG 2 features that are not well supported by browsers. - - mesh.js Renders mesh gradients. Currently does not support bi-cubic meshes and meshes on strokes. - mesh_compressed.include mesh.js minified and wrapped as a C++11 raw string literal. diff --git a/src/extension/internal/polyfill/README.md b/src/extension/internal/polyfill/README.md new file mode 100644 index 000000000..adfc31555 --- /dev/null +++ b/src/extension/internal/polyfill/README.md @@ -0,0 +1,16 @@ +# Gradient Mesh JavaScript polyfill + +This directory contains JavaScript "Polyfills" to support rendering of SVG 2 +features that are not well supported by browsers, but appeared in the 2016 +[specification](https://www.w3.org/TR/2016/CR-SVG2-20160915/pservers.html#MeshGradients) + +The included files are: + - `mesh.js` mesh gradients supporting bicubic meshes and mesh on strokes. + - `mesh_compressed.include` mesh.js minified and wrapped as a C++11 raw string literal. + +## Details +The coding standard used is [semistandard](https://github.com/Flet/semistandard), +a more permissive (allows endrow semicolons) over the famous, open-source +[standardjs](https://standardjs.com/). + +The minifier used for the compressed version is [JavaScript minifier](https://javascript-minifier.com/). diff --git a/src/extension/internal/polyfill/mesh.js b/src/extension/internal/polyfill/mesh.js index 7268bebae..d3ba65fa6 100644 --- a/src/extension/internal/polyfill/mesh.js +++ b/src/extension/internal/polyfill/mesh.js @@ -1,857 +1,1188 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Use Canvas to render a mesh gradient, passing the rendering to an image via a data stream. // Copyright: Tavmjong Bah 2018 +// Contributor: Valentin Ionita 2019 // Distributed under GNU General Public License version 2 or later. See <http://fsf.org/>. -(function() { +(function () { + // Name spaces ----------------------------------- + const svgNS = 'http://www.w3.org/2000/svg'; + const xlinkNS = 'http://www.w3.org/1999/xlink'; + const xhtmlNS = 'http://www.w3.org/1999/xhtml'; + /* + * Maximum threshold for Bezier step size + * Larger values leave holes, smaller take longer to render. + */ + const maxBezierStep = 2.0; + + // Test if mesh gradients are supported. + if (document.createElementNS(svgNS, 'meshgradient').x) { + return; + } + + /* + * Utility functions ----------------------------- + */ + // Split Bezier using de Casteljau's method. + const splitBezier = (p0, p1, p2, p3) => { + let tmp = new Point((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5); + let p01 = new Point((p0.x + p1.x) * 0.5, (p0.y + p1.y) * 0.5); + let p12 = new Point((p2.x + p3.x) * 0.5, (p2.y + p3.y) * 0.5); + let p02 = new Point((tmp.x + p01.x) * 0.5, (tmp.y + p01.y) * 0.5); + let p11 = new Point((tmp.x + p12.x) * 0.5, (tmp.y + p12.y) * 0.5); + let p03 = new Point((p02.x + p11.x) * 0.5, (p02.y + p11.y) * 0.5); + + return ([ + [p0, p01, p02, p03], + [p03, p11, p12, p3] + ]); + }; + + // See Cairo: cairo-mesh-pattern-rasterizer.c + const bezierStepsSquared = (points) => { + let tmp0 = points[0].distSquared(points[1]); + let tmp1 = points[2].distSquared(points[3]); + let tmp2 = points[0].distSquared(points[2]) * 0.25; + let tmp3 = points[1].distSquared(points[3]) * 0.25; + + let max1 = tmp0 > tmp1 ? tmp0 : tmp1; + + let max2 = tmp2 > tmp3 ? tmp2 : tmp3; + + let max = max1 > max2 ? max1 : max2; + + return max * 18; + }; + + // Euclidean distance + const distance = (p0, p1) => Math.sqrt(p0.distSquared(p1)); + + // Weighted average to find Bezier points for linear sides. + const wAvg = (p0, p1) => p0.scale(2.0 / 3.0).add(p1.scale(1.0 / 3.0)); + + // Browsers return a string rather than a transform list for gradientTransform! + const parseTransform = (t) => { + let affine = new Affine(); + let trans, scale, radian, tan, skewx, skewy, rotate; + let transforms = t.match(/(\w+\(\s*[^)]+\))+/g); + + transforms.forEach((i) => { + let c = i.match(/[\w.-]+/g); + let type = c.shift(); + + switch (type) { + case 'translate': + if (c.length === 2) { + trans = new Affine(1, 0, 0, 1, c[0], c[1]); + } else { + console.error('mesh.js: translate does not have 2 arguments!'); + trans = new Affine(1, 0, 0, 1, 0, 0); + } + affine = affine.append(trans); + break; + + case 'scale': + if (c.length === 1) { + scale = new Affine(c[0], 0, 0, c[0], 0, 0); + } else if (c.length === 2) { + scale = new Affine(c[0], 0, 0, c[1], 0, 0); + } else { + console.error('mesh.js: scale does not have 1 or 2 arguments!'); + scale = new Affine(1, 0, 0, 1, 0, 0); + } + affine = affine.append(scale); + break; + + case 'rotate': + if (c.length === 3) { + trans = new Affine(1, 0, 0, 1, c[1], c[2]); + affine = affine.append(trans); + } + if (c[0]) { + radian = c[0] * Math.PI / 180.0; + let cos = Math.cos(radian); + let sin = Math.sin(radian); + if (Math.abs(cos) < 1e-16) { // I hate rounding errors... + cos = 0; + } + if (Math.abs(sin) < 1e-16) { // I hate rounding errors... + sin = 0; + } + rotate = new Affine(cos, sin, -sin, cos, 0, 0); + affine = affine.append(rotate); + } else { + console.error('math.js: No argument to rotate transform!'); + } + if (c.length === 3) { + trans = new Affine(1, 0, 0, 1, -c[1], -c[2]); + affine = affine.append(trans); + } + break; + + case 'skewX': + if (c[0]) { + radian = c[0] * Math.PI / 180.0; + tan = Math.tan(radian); + skewx = new Affine(1, 0, tan, 1, 0, 0); + affine = affine.append(skewx); + } else { + console.error('math.js: No argument to skewX transform!'); + } + break; + + case 'skewY': + if (c[0]) { + radian = c[0] * Math.PI / 180.0; + tan = Math.tan(radian); + skewy = new Affine(1, tan, 0, 1, 0, 0); + affine = affine.append(skewy); + } else { + console.error('math.js: No argument to skewY transform!'); + } + break; + + case 'matrix': + if (c.length === 6) { + affine = affine.append(new Affine(...c)); + } else { + console.error('math.js: Incorrect number of arguments for matrix!'); + } + break; + + default: + console.error('mesh.js: Unhandled transform type: ' + type); + break; + } + }); + + return affine; + }; + + const parsePoints = (s) => { + let points = []; + let values = s.split(/[ ,]+/); + for (let i = 0, imax = values.length - 1; i < imax; i += 2) { + points.push(new Point(parseFloat(values[i]), parseFloat(values[i + 1]))); + } + return points; + }; - var counter = 0; // Temp, number of calls to Canvas + // Set multiple attributes to an element + const setAttributes = (el, attrs) => { + for (let key in attrs) { + el.setAttribute(key, attrs[key]); + } + }; + + // Find the slope of point p_k by the values in p_k-1 and p_k+1 + const finiteDifferences = (c0, c1, c2, d01, d12) => { + let slope = [0, 0, 0, 0]; + let slow, shigh; + + for (let k = 0; k < 3; ++k) { + if ((c1[k] < c0[k] && c1[k] < c2[k]) || (c0[k] < c1[k] && c2[k] < c1[k])) { + slope[k] = 0; + } else { + slope[k] = 0.5 * ((c1[k] - c0[k]) / d01 + (c2[k] - c1[k]) / d12); + slow = Math.abs(3.0 * (c1[k] - c0[k]) / d01); + shigh = Math.abs(3.0 * (c2[k] - c1[k]) / d12); + + if (slope[k] > slow) { + slope[k] = slow; + } else if (slope[k] > shigh) { + slope[k] = shigh; + } + } + } - // Name spaces ----------------------------------- - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink" - var xhtmlNS = "http://www.w3.org/1999/xhtml"; + return slope; + }; + + // Coefficient matrix used for solving linear system + const A = [ + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [-3, 3, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [2, -2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0], + [-3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, -2, 0, -1, 0], + [9, -9, -9, 9, 6, 3, -6, -3, 6, -6, 3, -3, 4, 2, 2, 1], + [-6, 6, 6, -6, -3, -3, 3, 3, -4, 4, -2, 2, -2, -2, -1, -1], + [2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 1, 0, 1, 0], + [-6, 6, 6, -6, -4, -2, 4, 2, -3, 3, -3, 3, -2, -1, -2, -1], + [4, -4, -4, 4, 2, 2, -2, -2, 2, -2, 2, -2, 1, 1, 1, 1] + ]; + + // Solve the linear system for bicubic interpolation + const solveLinearSystem = (v) => { + let alpha = []; + + for (let i = 0; i < 16; ++i) { + alpha[i] = 0; + for (let j = 0; j < 16; ++j) { + alpha[i] += A[i][j] * v[j]; + } + } - // Test if mesh gradients are supported. - var m = document.createElementNS( svgNS, "meshgradient" ); - if (m.x) { - return; + return alpha; + }; + + // Evaluate the interpolation parameters at (y, x) + const evaluateSolution = (alpha, x, y) => { + const xx = x * x; + const yy = y * y; + const xxx = x * x * x; + const yyy = y * y * y; + + let result = + alpha[0] + + alpha[1] * x + + alpha[2] * xx + + alpha[3] * xxx + + alpha[4] * y + + alpha[5] * y * x + + alpha[6] * y * xx + + alpha[7] * y * xxx + + alpha[8] * yy + + alpha[9] * yy * x + + alpha[10] * yy * xx + + alpha[11] * yy * xxx + + alpha[12] * yyy + + alpha[13] * yyy * x + + alpha[14] * yyy * xx + + alpha[15] * yyy * xxx; + + return result; + }; + + // Split a patch into 8x8 smaller patches + const splitPatch = (patch) => { + let yPatches = []; + let xPatches = []; + let patches = []; + + // Horizontal splitting + for (let i = 0; i < 4; ++i) { + yPatches[i] = []; + yPatches[i][0] = splitBezier( + patch[0][i], patch[1][i], + patch[2][i], patch[3][i] + ); + + yPatches[i][1] = []; + yPatches[i][1].push(...splitBezier(...yPatches[i][0][0])); + yPatches[i][1].push(...splitBezier(...yPatches[i][0][1])); + + yPatches[i][2] = []; + yPatches[i][2].push(...splitBezier(...yPatches[i][1][0])); + yPatches[i][2].push(...splitBezier(...yPatches[i][1][1])); + yPatches[i][2].push(...splitBezier(...yPatches[i][1][2])); + yPatches[i][2].push(...splitBezier(...yPatches[i][1][3])); } - // Test above test using known good SVG element - // var l = document.createElementNS( svgNS, "linearGradient" ); - // if (l.x1) { - // console.log( "linearGradient has x1" ); - // return; - // } else { - // console.log( "linearGradient does not have x1" ); - // } - - // Point class ----------------------------------- - function Point(x, y) { - this.x = x || 0; - this.y = y || 0; - }; - - Point.prototype.x = null; - Point.prototype.y = null; - - Point.prototype.get_x = function() { - return this.x; + // Vertical splitting + for (let i = 0; i < 8; ++i) { + xPatches[i] = []; + + for (let j = 0; j < 4; ++j) { + xPatches[i][j] = []; + xPatches[i][j][0] = splitBezier( + yPatches[0][2][i][j], yPatches[1][2][i][j], + yPatches[2][2][i][j], yPatches[3][2][i][j] + ); + + xPatches[i][j][1] = []; + xPatches[i][j][1].push(...splitBezier(...xPatches[i][j][0][0])); + xPatches[i][j][1].push(...splitBezier(...xPatches[i][j][0][1])); + + xPatches[i][j][2] = []; + xPatches[i][j][2].push(...splitBezier(...xPatches[i][j][1][0])); + xPatches[i][j][2].push(...splitBezier(...xPatches[i][j][1][1])); + xPatches[i][j][2].push(...splitBezier(...xPatches[i][j][1][2])); + xPatches[i][j][2].push(...splitBezier(...xPatches[i][j][1][3])); + } } - Point.prototype.get_y = function() { - return this.y; + for (let i = 0; i < 8; ++i) { + patches[i] = []; + + for (let j = 0; j < 8; ++j) { + patches[i][j] = []; + + patches[i][j][0] = xPatches[i][0][2][j]; + patches[i][j][1] = xPatches[i][1][2][j]; + patches[i][j][2] = xPatches[i][2][2][j]; + patches[i][j][3] = xPatches[i][3][2][j]; + } } - Point.prototype.clone = function() { - return new Point(this.x, this.y); + return patches; + }; + + // Point class ----------------------------------- + class Point { + constructor (x, y) { + this.x = x || 0; + this.y = y || 0; } - Point.prototype.add = function(v) { - return new Point(this.x + v.x, this.y + v.y); - }; + toString () { + return `(x=${this.x}, y=${this.y})`; + } - Point.prototype.scale = function(v) { - if( v instanceof Point ) { - return new Point(this.x * v.x, this.y * v.y); - } - return new Point(this.x * v, this.y * v); - }; + clone () { + return new Point(this.x, this.y); + } - // Transform by affine - Point.prototype.transform = function(a) { - var x = this.x * a.a + this.y * a.c + a.e; - var y = this.x * a.b + this.y * a.d + a.f; - return new Point(x, y); - }; - - Point.prototype.dist_sq = function(v) { - var x = this.x - v.x; - var y = this.y - v.y; - return (x*x + y*y); - }; - - Point.prototype.toString = function() { - return "(x=" + this.x + ", y=" + this.y + ")"; - }; - - // Affine class ----------------------------------- - - // As defined in the SVG spec - // | a c e | - // | b d f | - // | 0 0 1 | - function Affine(a, b, c, d, e, f) { - if (a === undefined) { - this.a = 1; - this.b = 0; - this.c = 0; - this.d = 1; - this.e = 0; - this.f = 0; - } else { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.e = e; - this.f = f; - } - }; - - Affine.prototype.a = null; - Affine.prototype.b = null; - Affine.prototype.c = null; - Affine.prototype.d = null; - Affine.prototype.e = null; - Affine.prototype.f = null; - - Affine.prototype.append = function(v) { - if ( !(v instanceof Affine) ) { - console.log ( "mesh.js: argument to Affine.append is not affine!"); - } - var a = this.a * v.a + this.c * v.b; - var b = this.b * v.a + this.d * v.b; - var c = this.a * v.c + this.c * v.d; - var d = this.b * v.c + this.d * v.d; - var e = this.a * v.e + this.c * v.f + this.e; - var f = this.b * v.e + this.d * v.f + this.f; - return new Affine( a, b, c, d, e, f ); - }; - - Affine.prototype.toString = function() { - return ("affine: " + this.a + " " + this.c + " " + this.e + - "\n " + this.b + " " + this.d + " " + this.f); - }; - - // Utility functions --------------------------------- - - // Browsers return a string rather than a transform list for gradientTransform! - function parseTransform(t) { - // console.log( "parseTransform: " + t ); - var affine = new Affine(); - for (var i in t = t.match(/(\w+\(\s*(\-?\d+\.?\d*e?\-?\d*\s*,?\s*)+\))+/g)) { - var c = t[i].match(/[\w\.\-]+/g); - var type = c.shift(); - switch ( type ) { - - case "translate": - var trans; - if (c.length == 2) { - trans = new Affine( 1, 0, 0, 1, c[0], c[1] ); - } else { - console.log( "mesh.js: translate does not have 2 arguments!" ); - trans = new Affine( 1, 0, 0, 1, 0, 0 ); - } - console.log( trans.toString() ); - affine = affine.append( trans ); - break; - - case "scale": - var scale; - if (c.length == 1 ) { - scale = new Affine( c[0], 0, 0, c[0], 0, 0 ); - } else if (c.length == 2) { - scale = new Affine( c[0], 0, 0, c[1], 0, 0 ); - } else { - console.log( "mesh.js: scale does not have 1 or 2 arguments!" ); - scale = new Affine( 1, 0, 0, 1, 0, 0 ); - } - affine = affine.append( scale ); - break; - - case "rotate": - if (c.length == 3 ) { - var trans = new Affine( 1, 0, 0, 1, c[1], c[2]); - affine = affine.append( trans ); - } - if (c[0]) { - var radian = c[0] * Math.PI/180.0; - var cos = Math.cos(radian); - var sin = Math.sin(radian); - if (Math.abs( cos ) < 1e-16) { // I hate rounding errors... - cos = 0; - } - if (Math.abs( sin ) < 1e-16) { // I hate rounding errors... - sin = 0; - } - var rotate = new Affine( cos, sin, -sin, cos, 0, 0 ); - affine = affine.append( rotate ); - } else { - console.log( "math.js: No argument to rotate transform!" ); - } - if (c.length == 3 ) { - var trans = new Affine( 1, 0, 0, 1, -c[1], -c[2]); - affine = affine.append( trans ); - } - break; - - case "skewX": - if (c[0]) { - var radian = c[0] * Math.PI/180.0; - var tan = Math.tan(radian); - var skewx = new Affine( 1, 0, tan, 1, 0, 0 ); - affine = affine.append( skewx ); - } else { - console.log( "math.js: No argument to skewX transform!" ); - } - break; - - case "skewY": - if (c[0]) { - var radian = c[0] * Math.PI/180.0; - var tan = Math.tan(radian); - var skewy = new Affine( 1, tan, 0, 1, 0, 0 ); - affine = affine.append( skewy ); - } else { - console.log( "math.js: No argument to skewY transform!" ); - } - break; - - case "matrix": - if (c.length == 6) { - var matrix = new Affine( c[0], c[1], c[2], c[3], c[4], c[5] ); - affine = affine.append( matrix ); - } else { - console.log( "math.js: Incorrect number of arguments for matrix!" ); - } - break; - - default: - console.log( "mesh.js: Unhandled transform type: " + type); - break; - } - } - // console.log( " affine:\n" + affine.toString() ); - return affine; - }; - - - function colorToString(c) { - return ("rgb(" + Math.round(c[0]) + "," + Math.round(c[1]) + "," +Math.round(c[2]) + ")"); - }; - - // Split Bezier using de Casteljau's method. - function split_bezier(p0, p1, p2, p3) { - - // console.log( "split_bezier" ); - var p00 = p0.clone(); - var p13 = p3.clone(); - - var tmp = p1.add(p2).scale(0.5); - var p01 = p0.add(p1).scale(0.5); - var p12 = p2.add(p3).scale(0.5); - - var p02 = p01.add(tmp).scale(0.5); - var p11 = tmp.add(p12).scale(0.5); - - var p03 = p02.add(p11).scale(0.5); - var p10 = p03.clone(); - - return ([[p00, p01, p02, p03], - [p10, p11, p12, p13]]); + add (v) { + return new Point(this.x + v.x, this.y + v.y); } - // See Cairo: cairo-mesh-pattern-rasterizer.c - function bezier_steps_sq(points) { - var tmp = []; - tmp[0] = points[0].dist_sq(points[1]); - tmp[1] = points[2].dist_sq(points[3]); - tmp[2] = points[0].dist_sq(points[2]) * 0.25; - tmp[3] = points[1].dist_sq(points[3]) * 0.25; - return Math.max.apply(null,tmp) * 18; - }; - - - // Curve class -------------------------------------- - function Curve(nodes, colors) { - this.nodes = nodes; // 4 Bezier points - this.colors = colors; // 2 x 4 colors (two ends x R+G+B+A) - }; - - // Paint a Bezier curve. w is width of Canvas window. - Curve.prototype.paint_curve = function(v, w) { - - // console.log( "Curve.paint_curve" ); - // If inside, see if we need to split - var max = bezier_steps_sq(this.nodes); - - if (max > 2.0) { // Larger values leave holes, smaller take longer to render. - var beziers = split_bezier(this.nodes[0],this.nodes[1],this.nodes[2],this.nodes[3]); - var colors0 = [[],[]]; // ([start][end]) - var colors1 = [[],[]]; - for (var i = 0; i < 4; ++ i) { - colors0[0][i] = this.colors[0][i]; - colors0[1][i] = (this.colors[0][i] + this.colors[1][i])/2; - colors1[0][i] = (this.colors[0][i] + this.colors[1][i])/2; - colors1[1][i] = this.colors[1][i]; - } - var curve0 = new Curve( beziers[0], colors0 ); - var curve1 = new Curve( beziers[1], colors1 ); - curve0.paint_curve(v, w); - curve1.paint_curve(v, w); - } else { - counter++; - - // Directly write data - var x = Math.round(this.nodes[0].x); - var y = Math.round(this.nodes[0].y); - if (x >= 0 && x < w ) { - var index = (y * w + x) * 4; - v[index ] = Math.round(this.colors[0][0]); - v[index + 1] = Math.round(this.colors[0][1]); - v[index + 2] = Math.round(this.colors[0][2]); - v[index + 3] = Math.round(this.colors[0][3]); // Alpha - } - - // Draw curve, quick and dirty (via canvas context) - // v.beginPath(); - // v.moveTo( this.nodes[0].x, this.nodes[0].y ); - // v.bezierCurveTo( this.nodes[1].x, this.nodes[1].y, - // this.nodes[2].x, this.nodes[2].y, - // this.nodes[3].x, this.nodes[3].y ); - // v.strokeStyle = colorToString( this.colors[0] ); - // v.stroke(); - } + scale (v) { + if (v.x === undefined) { + return new Point(this.x * v, this.y * v); + } + return new Point(this.x * v.x, this.y * v.y); } - // Patch class ------------------------------------- - function Patch(nodes, colors) { - this.nodes = nodes; // 4x4 array of points - this.colors = colors; // 2x2x4 colors (four corners x R+G+B+A) - }; - - // Set path for future stroking or filling... useful for debugging. - Patch.prototype.setOutline = function(v) { - - // Draw patch outline - v.beginPath(); - v.moveTo( this.nodes[0][0].x, this.nodes[0][0].y ); - v.bezierCurveTo( this.nodes[0][1].x, this.nodes[0][1].y, - this.nodes[0][2].x, this.nodes[0][2].y, - this.nodes[0][3].x, this.nodes[0][3].y ); - v.bezierCurveTo( this.nodes[1][3].x, this.nodes[1][3].y, - this.nodes[2][3].x, this.nodes[2][3].y, - this.nodes[3][3].x, this.nodes[3][3].y ); - v.bezierCurveTo( this.nodes[3][2].x, this.nodes[3][2].y, - this.nodes[3][1].x, this.nodes[3][1].y, - this.nodes[3][0].x, this.nodes[3][0].y ); - v.bezierCurveTo( this.nodes[2][0].x, this.nodes[2][0].y, - this.nodes[1][0].x, this.nodes[1][0].y, - this.nodes[0][0].x, this.nodes[0][0].y ); - v.closePath(); - }; - - // Draw stroke patch... useful if debugging. - Patch.prototype.drawOutline = function(v) { - - this.setOutline(v); - v.strokeStyle = "black"; - v.stroke(); - }; - - // Fill patch... useful if debugging. - Patch.prototype.fillOutline = function(v) { - - this.setOutline(v); - v.fillStyle = colorToString( this.colors[0] ); - v.fill(); - }; + distSquared (v) { + let x = this.x - v.x; + let y = this.y - v.y; + return (x * x + y * y); + } - // Split patch horizontally into two patches. - Patch.prototype.split = function() { - - // console.log( "Patch.split" ); - - var nodes0 = [[],[],[],[]]; - var nodes1 = [[],[],[],[]]; - var colors0 = [[[],[]],[[],[]]]; - var colors1 = [[[],[]],[[],[]]]; - - for (var i = 0; i < 4; ++i) { - var beziers = split_bezier( this.nodes[0][i], this.nodes[1][i], this.nodes[2][i], this.nodes[3][i] ); - for (var j = 0; j < 4; ++j) { - nodes0[0][i] = beziers[0][0]; - nodes0[1][i] = beziers[0][1]; - nodes0[2][i] = beziers[0][2]; - nodes0[3][i] = beziers[0][3]; - nodes1[0][i] = beziers[1][0]; - nodes1[1][i] = beziers[1][1]; - nodes1[2][i] = beziers[1][2]; - nodes1[3][i] = beziers[1][3]; - } - } - - for (var i = 0; i < 4; ++ i) { - colors0[0][0][i] = this.colors[0][0][i]; - colors0[0][1][i] = this.colors[0][1][i]; - colors0[1][0][i] = (this.colors[0][0][i] + this.colors[1][0][i])/2; - colors0[1][1][i] = (this.colors[0][1][i] + this.colors[1][1][i])/2; - colors1[0][0][i] = (this.colors[0][0][i] + this.colors[1][0][i])/2; - colors1[0][1][i] = (this.colors[0][1][i] + this.colors[1][1][i])/2; - colors1[1][0][i] = this.colors[1][0][i]; - colors1[1][1][i] = this.colors[1][1][i]; - } - - var patch0 = new Patch( nodes0, colors0 ); - var patch1 = new Patch( nodes1, colors1 ); - - return ([patch0, patch1]); - }; - - Patch.prototype.paint = function(v, w) { - - // console.log( "Patch.paint" ); - // console.log( this.nodes ); - - // Check if patch is inside canvas (need canvas dimensions) - // To be done..... - - // If inside, see if we need to split - var tmp = []; - for (var i = 0; i < 4; ++i ) { - tmp[i] = bezier_steps_sq([this.nodes[0][i],this.nodes[1][i], - this.nodes[2][i],this.nodes[3][i]]); - } - - var max = Math.max.apply(null,tmp); - // console.log( "Max: " + max ); - - if (max > 2.0) { // Larger values leave holes, smaller take longer to render. - // console.log( "Paint: Splitting" ); - var patches = this.split(); - // console.log( patches ); - patches[0].paint(v, w); - patches[1].paint(v, w) - } else { - // console.log( "Paint: Filling" ); - //this.fillOutline(v); - this.paint_curve(v, w); - } - }; - - Patch.prototype.paint_curve = function(v, w) { - - // console.log( "Patch.paint_curve" ); - - // Paint a Bezier curve using just the top of the patch. If - // the patch is thin enough this should work. We leave this - // function here in case we want to do something more fancy. - var curve = new Curve( - [this.nodes[0][0],this.nodes[0][1],this.nodes[0][2],this.nodes[0][3]], - [this.colors[0][0],this.colors[0][1]]); - curve.paint_curve(v, w); + // Transform by affine + transform (affine) { + let x = this.x * affine.a + this.y * affine.c + affine.e; + let y = this.x * affine.b + this.y * affine.d + affine.f; + return new Point(x, y); + } + } + + /* + * Affine class ------------------------------------- + * + * As defined in the SVG spec + * | a c e | + * | b d f | + * | 0 0 1 | + * + */ + + class Affine { + constructor (a, b, c, d, e, f) { + if (a === undefined) { + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.e = 0; + this.f = 0; + } else { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + } } + toString () { + return `affine: ${this.a} ${this.c} ${this.e} \n\ + ${this.b} ${this.d} ${this.f}`; + } - // Mesh class --------------------------------------- - function Mesh(id) { - // console.log( "Mesh: " + id ); - this.id = id; - var raw = this.readMesh( id ); - this.nodes = raw.nodes; // (m*3+1) x (n*3+1) points - this.colors = raw.colors; // (m+1) x (n+1) x 4 colors (R+G+B+A) - // console.log( this.nodes ); - // console.log( this.colors ); - }; - - // Weighted average to find Bezier points for linear sides. - function w_ave(p0, p1) { - var p = p0.scale(2.0/3.0).add(p1.scale(1.0/3.0)); - return p; + append (v) { + if (!(v instanceof Affine)) { + console.error(`mesh.js: argument to Affine.append is not affine!`); + } + let a = this.a * v.a + this.c * v.b; + let b = this.b * v.a + this.d * v.b; + let c = this.a * v.c + this.c * v.d; + let d = this.b * v.c + this.d * v.d; + let e = this.a * v.e + this.c * v.f + this.e; + let f = this.b * v.e + this.d * v.f + this.f; + return new Affine(a, b, c, d, e, f); } + } - // Function to parse an SVG mesh and return an array of nodes (points) and an array of colors. - Mesh.prototype.readMesh = function(id) { - - var nodes = []; - var colors = []; - - // First, find the mesh - var theMesh = document.getElementById(id); - if (theMesh == null) { - console.log( "mesh.js: Could not find mesh: " + id); - } else { - // console.log( "Reading mesh: " + id); - - nodes[0] = []; // Top row - colors[0] = []; // Top row - - var x = Number(theMesh.getAttribute("x")); - var y = Number(theMesh.getAttribute("y")); - // console.log( " x: " + x + " y: " + y ); - nodes[0][0] = new Point(x, y); - - var rows = theMesh.children - for (var i = 0; i < rows.length; ++i ) { - // Need to validate if meshrow... - nodes[ 3*i+1] = []; // Need three extra rows for each meshrow. - nodes[ 3*i+2] = []; - nodes[ 3*i+3] = []; - colors[i+1] = []; // Need one more row than number of meshrows. - // console.log( " row: " + i); - var patches = rows[i].children; - for (var j = 0; j < patches.length; ++j) { - // console.log( " patch: " + j); - var stops = patches[j].children; - for (var k = 0; k < stops.length; ++k) { - var l = k; - if (i != 0) { - ++l; // There is no top if row isn't first row. - } - // console.log( " stop: " + k); - var path = stops[k].getAttribute("path"); - - var type = "l"; // We need to still find mid-points even if no path. - if (path != null) { - var parts = path.match(/\s*([lLcC])\s*(.*)/); - type = parts[1]; - } - var stop_nodes = parse_points( parts[2] ); - - switch (type) { - case "l": - if (l == 0) { // Top - nodes[3*i][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j]); - nodes[3*i][3*j+1] = w_ave( nodes[3*i][3*j], nodes[3*i][3*j+3] ); - nodes[3*i][3*j+2] = w_ave( nodes[3*i][3*j+3], nodes[3*i][3*j] ); - } else if (l == 1) { // Right - nodes[3*i+3][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j+3]); - nodes[3*i+1][3*j+3] = w_ave( nodes[3*i][3*j+3], nodes[3*i+3][3*j+3] ); - nodes[3*i+2][3*j+3] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i][3*j+3] ); - } else if (l == 2) { // Bottom - if(j==0) { - nodes[3*i+3][3*j+0] = stop_nodes[0].add(nodes[3*i+3][3*j+3]); - } - nodes[3*i+3][3*j+1] = w_ave( nodes[3*i+3][3*j], nodes[3*i+3][3*j+3] ); - nodes[3*i+3][3*j+2] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i+3][3*j] ); - } else { // Left - nodes[3*i+1][3*j] = w_ave( nodes[3*i][3*j], nodes[3*i+3][3*j] ); - nodes[3*i+2][3*j] = w_ave( nodes[3*i+3][3*j], nodes[3*i][3*j] ); - } - break; - case "L": - if (l == 0) { // Top - nodes[3*i][3*j+3] = stop_nodes[0]; - nodes[3*i][3*j+1] = w_ave( nodes[3*i][3*j], nodes[3*i][3*j+3] ); - nodes[3*i][3*j+2] = w_ave( nodes[3*i][3*j+3], nodes[3*i][3*j] ); - } else if (l == 1) { // Right - nodes[3*i+3][3*j+3] = stop_nodes[0]; - nodes[3*i+1][3*j+3] = w_ave( nodes[3*i][3*j+3], nodes[3*i+3][3*j+3] ); - nodes[3*i+2][3*j+3] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i][3*j+3] ); - } else if (l == 2) { // Bottom - if(j==0) { - nodes[3*i+3][3*j+0] = stop_nodes[0]; - } - nodes[3*i+3][3*j+1] = w_ave( nodes[3*i+3][3*j], nodes[3*i+3][3*j+3] ); - nodes[3*i+3][3*j+2] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i+3][3*j] ); - } else { // Left - nodes[3*i+1][3*j] = w_ave( nodes[3*i][3*j], nodes[3*i+3][3*j] ); - nodes[3*i+2][3*j] = w_ave( nodes[3*i+3][3*j], nodes[3*i][3*j] ); - } - break; - case "c": - if (l == 0) { // Top - nodes[3*i][3*j+1] = stop_nodes[0].add(nodes[3*i][3*j]); - nodes[3*i][3*j+2] = stop_nodes[1].add(nodes[3*i][3*j]); - nodes[3*i][3*j+3] = stop_nodes[2].add(nodes[3*i][3*j]); - } else if (l == 1) { // Right - nodes[3*i+1][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j+3]); - nodes[3*i+2][3*j+3] = stop_nodes[1].add(nodes[3*i][3*j+3]); - nodes[3*i+3][3*j+3] = stop_nodes[2].add(nodes[3*i][3*j+3]); - } else if (l == 2) { // Bottom - nodes[3*i+3][3*j+2] = stop_nodes[0].add(nodes[3*i+3][3*j+3]); - nodes[3*i+3][3*j+1] = stop_nodes[1].add(nodes[3*i+3][3*j+3]); - if(j==0) { - nodes[3*i+3][3*j+0] = stop_nodes[2].add(nodes[3*i+3][3*j+3]); - } - } else { // Left - nodes[3*i+2][3*j] = stop_nodes[0].add(nodes[3*i+3][3*j]); - nodes[3*i+1][3*j] = stop_nodes[1].add(nodes[3*i+3][3*j]); - } - break; - case "C": - if (l == 0) { // Top - nodes[3*i][3*j+1] = stop_nodes[0]; - nodes[3*i][3*j+2] = stop_nodes[1]; - nodes[3*i][3*j+3] = stop_nodes[2]; - } else if (l == 1) { // Right - nodes[3*i+1][3*j+3] = stop_nodes[0]; - nodes[3*i+2][3*j+3] = stop_nodes[1]; - nodes[3*i+3][3*j+3] = stop_nodes[2]; - } else if (l == 2) { // Bottom - nodes[3*i+3][3*j+2] = stop_nodes[0]; - nodes[3*i+3][3*j+1] = stop_nodes[1]; - if(j==0) { - nodes[3*i+3][3*j+0] = stop_nodes[2]; - } - } else { // Left - nodes[3*i+2][3*j] = stop_nodes[0]; - nodes[3*i+1][3*j] = stop_nodes[1]; - } - break - default: - console.log("mesh.js: " + type + " invalid path type."); - } - - if ( (i == 0 && j == 0) || k > 0 ) { - var color_raw = getComputedStyle(stops[k]).stopColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i); - var alpha_raw = getComputedStyle(stops[k]).stopOpacity; - // console.log( " color_raw: " + color_raw + " alpha_raw: " + alpha_raw); - var alpha = 255; - if (alpha_raw) { - alpha = parseInt(alpha_raw * 255); - } - // console.log( " alpha: " + alpha ); - if (color_raw) { - if (l == 0) { // upper left corner - colors[i][j] = []; - colors[i][j][0] = parseInt(color_raw[1]); - colors[i][j][1] = parseInt(color_raw[2]); - colors[i][j][2] = parseInt(color_raw[3]); - colors[i][j][3] = alpha; // Alpha - } else if (l == 1) { // upper right corner - colors[i][j+1] = []; - colors[i][j+1][0] = parseInt(color_raw[1]); - colors[i][j+1][1] = parseInt(color_raw[2]); - colors[i][j+1][2] = parseInt(color_raw[3]); - colors[i][j+1][3] = alpha; // Alpha - } else if (l == 2) { // lower right corner - colors[i+1][j+1] = []; - colors[i+1][j+1][0] = parseInt(color_raw[1]); - colors[i+1][j+1][1] = parseInt(color_raw[2]); - colors[i+1][j+1][2] = parseInt(color_raw[3]); - colors[i+1][j+1][3] = alpha; // Alpha - } else if (l == 3) { // lower left corner - colors[i+1][j] = []; - colors[i+1][j][0] = parseInt(color_raw[1]); - colors[i+1][j][1] = parseInt(color_raw[2]); - colors[i+1][j][2] = parseInt(color_raw[3]); - colors[i+1][j][3] = alpha; // Alpha - } - } - } - } - - // SVG doesn't use tensor points but we need them for rendering. - nodes[3*i+1][3*j+1] = new Point; - nodes[3*i+1][3*j+2] = new Point; - nodes[3*i+2][3*j+1] = new Point; - nodes[3*i+2][3*j+2] = new Point; - - nodes[3*i+1][3*j+1].x = - ( -4.0 * nodes[3*i ][3*j ].x + - 6.0 * ( nodes[3*i ][3*j+1].x + nodes[3*i+1][3*j ].x ) + - -2.0 * ( nodes[3*i ][3*j+3].x + nodes[3*i+3][3*j ].x ) + - 3.0 * ( nodes[3*i+3][3*j+1].x + nodes[3*i+1][3*j+3].x ) + - -1.0 * nodes[3*i+3][3*j+3].x ) / 9.0; - nodes[3*i+1][3*j+2].x = - ( -4.0 * nodes[3*i ][3*j+3].x + - 6.0 * ( nodes[3*i ][3*j+2].x + nodes[3*i+1][3*j+3].x ) + - -2.0 * ( nodes[3*i ][3*j ].x + nodes[3*i+3][3*j+3].x ) + - 3.0 * ( nodes[3*i+3][3*j+2].x + nodes[3*i+1][3*j ].x ) + - -1.0 * nodes[3*i+3][3*j ].x ) / 9.0; - nodes[3*i+2][3*j+1].x = - ( -4.0 * nodes[3*i+3][3*j ].x + - 6.0 * ( nodes[3*i+3][3*j+1].x + nodes[3*i+2][3*j ].x ) + - -2.0 * ( nodes[3*i+3][3*j+3].x + nodes[3*i ][3*j ].x ) + - 3.0 * ( nodes[3*i ][3*j+1].x + nodes[3*i+2][3*j+3].x ) + - -1.0 * nodes[3*i ][3*j+3].x ) / 9.0; - nodes[3*i+2][3*j+2].x = - ( -4.0 * nodes[3*i+3][3*j+3].x + - 6.0 * ( nodes[3*i+3][3*j+2].x + nodes[3*i+2][3*j+3].x ) + - -2.0 * ( nodes[3*i+3][3*j ].x + nodes[3*i ][3*j+3].x ) + - 3.0 * ( nodes[3*i ][3*j+2].x + nodes[3*i+2][3*j ].x ) + - -1.0 * nodes[3*i ][3*j ].x ) / 9.0; - - nodes[3*i+1][3*j+1].y = - ( -4.0 * nodes[3*i ][3*j ].y + - 6.0 * ( nodes[3*i ][3*j+1].y + nodes[3*i+1][3*j ].y ) + - -2.0 * ( nodes[3*i ][3*j+3].y + nodes[3*i+3][3*j ].y ) + - 3.0 * ( nodes[3*i+3][3*j+1].y + nodes[3*i+1][3*j+3].y ) + - -1.0 * nodes[3*i+3][3*j+3].y ) / 9.0; - nodes[3*i+1][3*j+2].y = - ( -4.0 * nodes[3*i ][3*j+3].y + - 6.0 * ( nodes[3*i ][3*j+2].y + nodes[3*i+1][3*j+3].y ) + - -2.0 * ( nodes[3*i ][3*j ].y + nodes[3*i+3][3*j+3].y ) + - 3.0 * ( nodes[3*i+3][3*j+2].y + nodes[3*i+1][3*j ].y ) + - -1.0 * nodes[3*i+3][3*j ].y ) / 9.0; - nodes[3*i+2][3*j+1].y = - ( -4.0 * nodes[3*i+3][3*j ].y + - 6.0 * ( nodes[3*i+3][3*j+1].y + nodes[3*i+2][3*j ].y ) + - -2.0 * ( nodes[3*i+3][3*j+3].y + nodes[3*i ][3*j ].y ) + - 3.0 * ( nodes[3*i ][3*j+1].y + nodes[3*i+2][3*j+3].y ) + - -1.0 * nodes[3*i ][3*j+3].y ) / 9.0; - nodes[3*i+2][3*j+2].y = - ( -4.0 * nodes[3*i+3][3*j+3].y + - 6.0 * ( nodes[3*i+3][3*j+2].y + nodes[3*i+2][3*j+3].y ) + - -2.0 * ( nodes[3*i+3][3*j ].y + nodes[3*i ][3*j+3].y ) + - 3.0 * ( nodes[3*i ][3*j+2].y + nodes[3*i+2][3*j ].y ) + - -1.0 * nodes[3*i ][3*j ].y ) / 9.0; - - } - } - // console.log( nodes ); - } - return { - nodes: nodes, - colors: colors - }; + // Curve class -------------------------------------- + class Curve { + constructor (nodes, colors) { + this.nodes = nodes; // 4 Bezier points + this.colors = colors; // 2 x 4 colors (two ends x R+G+B+A) } - // Extracts out each patch and then paints it - Mesh.prototype.paint = function(v, w) { + /* + * Paint a Bezier curve + * w is canvas.width + * h is canvas.height + */ + paintCurve (v, w) { + // If inside, see if we need to split + if (bezierStepsSquared(this.nodes) > maxBezierStep) { + const beziers = splitBezier(...this.nodes); + // ([start][end]) + let colors0 = [[], []]; + let colors1 = [[], []]; + + /* + * Linear horizontal interpolation of the middle value for every + * patch exceeding thereshold + */ + for (let i = 0; i < 4; ++i) { + colors0[0][i] = this.colors[0][i]; + colors0[1][i] = (this.colors[0][i] + this.colors[1][i]) / 2; + colors1[0][i] = colors0[1][i]; + colors1[1][i] = this.colors[1][i]; + } + let curve0 = new Curve(beziers[0], colors0); + let curve1 = new Curve(beziers[1], colors1); + curve0.paintCurve(v, w); + curve1.paintCurve(v, w); + } else { + // Directly write data + let x = Math.round(this.nodes[0].x); + if (x >= 0 && x < w) { + let index = (~~this.nodes[0].y * w + x) * 4; + v[index] = Math.round(this.colors[0][0]); + v[index + 1] = Math.round(this.colors[0][1]); + v[index + 2] = Math.round(this.colors[0][2]); + v[index + 3] = Math.round(this.colors[0][3]); // Alpha + } + } + } + } - for (var i = 0; i < (this.nodes.length-1)/3; ++i) { - for (var j = 0; j < (this.nodes[0].length-1)/3; ++j) { + // Patch class ------------------------------------- + class Patch { + constructor (nodes, colors) { + this.nodes = nodes; // 4x4 array of points + this.colors = colors; // 2x2x4 colors (four corners x R+G+B+A) + } - var slice_nodes = []; - for ( var k = i*3; k < (i*3)+4; ++k ) { - slice_nodes.push(this.nodes[k].slice(j*3,(j*3)+4)); - } + // Split patch horizontally into two patches. + split () { + let nodes0 = [[], [], [], []]; + let nodes1 = [[], [], [], []]; + let colors0 = [ + [[], []], + [[], []] + ]; + let colors1 = [ + [[], []], + [[], []] + ]; + + for (let i = 0; i < 4; ++i) { + const beziers = splitBezier( + this.nodes[0][i], this.nodes[1][i], + this.nodes[2][i], this.nodes[3][i] + ); + + nodes0[0][i] = beziers[0][0]; + nodes0[1][i] = beziers[0][1]; + nodes0[2][i] = beziers[0][2]; + nodes0[3][i] = beziers[0][3]; + nodes1[0][i] = beziers[1][0]; + nodes1[1][i] = beziers[1][1]; + nodes1[2][i] = beziers[1][2]; + nodes1[3][i] = beziers[1][3]; + } + + /* + * Linear vertical interpolation of the middle value for every + * patch exceeding thereshold + */ + for (let i = 0; i < 4; ++i) { + colors0[0][0][i] = this.colors[0][0][i]; + colors0[0][1][i] = this.colors[0][1][i]; + colors0[1][0][i] = (this.colors[0][0][i] + this.colors[1][0][i]) / 2; + colors0[1][1][i] = (this.colors[0][1][i] + this.colors[1][1][i]) / 2; + colors1[0][0][i] = colors0[1][0][i]; + colors1[0][1][i] = colors0[1][1][i]; + colors1[1][0][i] = this.colors[1][0][i]; + colors1[1][1][i] = this.colors[1][1][i]; + } + + return ([new Patch(nodes0, colors0), new Patch(nodes1, colors1)]); + } - var slice_colors = []; - slice_colors.push(this.colors[i ].slice(j,j+2)); - slice_colors.push(this.colors[i+1].slice(j,j+2)); + paint (v, w) { + // Check if we need to split + let larger = false; + let step; + for (let i = 0; i < 4; ++i) { + step = bezierStepsSquared([ + this.nodes[0][i], this.nodes[1][i], + this.nodes[2][i], this.nodes[3][i] + ]); + + if (step > maxBezierStep) { + larger = true; + break; + } + } + + if (larger) { + let patches = this.split(); + patches[0].paint(v, w); + patches[1].paint(v, w); + } else { + /* + * Paint a Bezier curve using just the top of the patch. If + * the patch is thin enough this should work. We leave this + * function here in case we want to do something more fancy. + */ + let curve = new Curve([...this.nodes[0]], [...this.colors[0]]); + curve.paintCurve(v, w); + } + } + } - var patch = new Patch(slice_nodes, slice_colors); - patch.paint(v, w); - } - } - }; + // Mesh class --------------------------------------- + class Mesh { + constructor (mesh) { + this.readMesh(mesh); + this.type = mesh.getAttribute('type') || 'bilinear'; + } + + // Function to parse an SVG mesh and set the nodes (points) and colors + readMesh (mesh) { + let nodes = [[]]; + let colors = [[]]; + + let x = Number(mesh.getAttribute('x')); + let y = Number(mesh.getAttribute('y')); + nodes[0][0] = new Point(x, y); + + let rows = mesh.children; + for (let i = 0, imax = rows.length; i < imax; ++i) { + // Need to validate if meshrow... + nodes[3 * i + 1] = []; // Need three extra rows for each meshrow. + nodes[3 * i + 2] = []; + nodes[3 * i + 3] = []; + colors[i + 1] = []; // Need one more row than number of meshrows. + + let patches = rows[i].children; + for (let j = 0, jmax = patches.length; j < jmax; ++j) { + let stops = patches[j].children; + for (let k = 0, kmax = stops.length; k < kmax; ++k) { + let l = k; + if (i !== 0) { + ++l; // There is no top if row isn't first row. + } + let path = stops[k].getAttribute('path'); + let parts; + + let type = 'l'; // We need to still find mid-points even if no path. + if (path != null) { + parts = path.match(/\s*([lLcC])\s*(.*)/); + type = parts[1]; + } + let stopNodes = parsePoints(parts[2]); + + switch (type) { + case 'l': + if (l === 0) { // Top + nodes[3 * i][3 * j + 3] = stopNodes[0].add(nodes[3 * i][3 * j]); + nodes[3 * i][3 * j + 1] = wAvg(nodes[3 * i][3 * j], nodes[3 * i][3 * j + 3]); + nodes[3 * i][3 * j + 2] = wAvg(nodes[3 * i][3 * j + 3], nodes[3 * i][3 * j]); + } else if (l === 1) { // Right + nodes[3 * i + 3][3 * j + 3] = stopNodes[0].add(nodes[3 * i][3 * j + 3]); + nodes[3 * i + 1][3 * j + 3] = wAvg(nodes[3 * i][3 * j + 3], nodes[3 * i + 3][3 * j + 3]); + nodes[3 * i + 2][3 * j + 3] = wAvg(nodes[3 * i + 3][3 * j + 3], nodes[3 * i][3 * j + 3]); + } else if (l === 2) { // Bottom + if (j === 0) { + nodes[3 * i + 3][3 * j + 0] = stopNodes[0].add(nodes[3 * i + 3][3 * j + 3]); + } + nodes[3 * i + 3][3 * j + 1] = wAvg(nodes[3 * i + 3][3 * j], nodes[3 * i + 3][3 * j + 3]); + nodes[3 * i + 3][3 * j + 2] = wAvg(nodes[3 * i + 3][3 * j + 3], nodes[3 * i + 3][3 * j]); + } else { // Left + nodes[3 * i + 1][3 * j] = wAvg(nodes[3 * i][3 * j], nodes[3 * i + 3][3 * j]); + nodes[3 * i + 2][3 * j] = wAvg(nodes[3 * i + 3][3 * j], nodes[3 * i][3 * j]); + } + break; + case 'L': + if (l === 0) { // Top + nodes[3 * i][3 * j + 3] = stopNodes[0]; + nodes[3 * i][3 * j + 1] = wAvg(nodes[3 * i][3 * j], nodes[3 * i][3 * j + 3]); + nodes[3 * i][3 * j + 2] = wAvg(nodes[3 * i][3 * j + 3], nodes[3 * i][3 * j]); + } else if (l === 1) { // Right + nodes[3 * i + 3][3 * j + 3] = stopNodes[0]; + nodes[3 * i + 1][3 * j + 3] = wAvg(nodes[3 * i][3 * j + 3], nodes[3 * i + 3][3 * j + 3]); + nodes[3 * i + 2][3 * j + 3] = wAvg(nodes[3 * i + 3][3 * j + 3], nodes[3 * i][3 * j + 3]); + } else if (l === 2) { // Bottom + if (j === 0) { + nodes[3 * i + 3][3 * j + 0] = stopNodes[0]; + } + nodes[3 * i + 3][3 * j + 1] = wAvg(nodes[3 * i + 3][3 * j], nodes[3 * i + 3][3 * j + 3]); + nodes[3 * i + 3][3 * j + 2] = wAvg(nodes[3 * i + 3][3 * j + 3], nodes[3 * i + 3][3 * j]); + } else { // Left + nodes[3 * i + 1][3 * j] = wAvg(nodes[3 * i][3 * j], nodes[3 * i + 3][3 * j]); + nodes[3 * i + 2][3 * j] = wAvg(nodes[3 * i + 3][3 * j], nodes[3 * i][3 * j]); + } + break; + case 'c': + if (l === 0) { // Top + nodes[3 * i][3 * j + 1] = stopNodes[0].add(nodes[3 * i][3 * j]); + nodes[3 * i][3 * j + 2] = stopNodes[1].add(nodes[3 * i][3 * j]); + nodes[3 * i][3 * j + 3] = stopNodes[2].add(nodes[3 * i][3 * j]); + } else if (l === 1) { // Right + nodes[3 * i + 1][3 * j + 3] = stopNodes[0].add(nodes[3 * i][3 * j + 3]); + nodes[3 * i + 2][3 * j + 3] = stopNodes[1].add(nodes[3 * i][3 * j + 3]); + nodes[3 * i + 3][3 * j + 3] = stopNodes[2].add(nodes[3 * i][3 * j + 3]); + } else if (l === 2) { // Bottom + nodes[3 * i + 3][3 * j + 2] = stopNodes[0].add(nodes[3 * i + 3][3 * j + 3]); + nodes[3 * i + 3][3 * j + 1] = stopNodes[1].add(nodes[3 * i + 3][3 * j + 3]); + if (j === 0) { + nodes[3 * i + 3][3 * j + 0] = stopNodes[2].add(nodes[3 * i + 3][3 * j + 3]); + } + } else { // Left + nodes[3 * i + 2][3 * j] = stopNodes[0].add(nodes[3 * i + 3][3 * j]); + nodes[3 * i + 1][3 * j] = stopNodes[1].add(nodes[3 * i + 3][3 * j]); + } + break; + case 'C': + if (l === 0) { // Top + nodes[3 * i][3 * j + 1] = stopNodes[0]; + nodes[3 * i][3 * j + 2] = stopNodes[1]; + nodes[3 * i][3 * j + 3] = stopNodes[2]; + } else if (l === 1) { // Right + nodes[3 * i + 1][3 * j + 3] = stopNodes[0]; + nodes[3 * i + 2][3 * j + 3] = stopNodes[1]; + nodes[3 * i + 3][3 * j + 3] = stopNodes[2]; + } else if (l === 2) { // Bottom + nodes[3 * i + 3][3 * j + 2] = stopNodes[0]; + nodes[3 * i + 3][3 * j + 1] = stopNodes[1]; + if (j === 0) { + nodes[3 * i + 3][3 * j + 0] = stopNodes[2]; + } + } else { // Left + nodes[3 * i + 2][3 * j] = stopNodes[0]; + nodes[3 * i + 1][3 * j] = stopNodes[1]; + } + break; + default: + console.error('mesh.js: ' + type + ' invalid path type.'); + } + + if ((i === 0 && j === 0) || k > 0) { + let colorRaw = window.getComputedStyle(stops[k]).stopColor + .match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i); + let alphaRaw = window.getComputedStyle(stops[k]).stopOpacity; + let alpha = 255; + if (alphaRaw) { + alpha = Math.floor(alphaRaw * 255); + } + + if (colorRaw) { + if (l === 0) { // upper left corner + colors[i][j] = []; + colors[i][j][0] = Math.floor(colorRaw[1]); + colors[i][j][1] = Math.floor(colorRaw[2]); + colors[i][j][2] = Math.floor(colorRaw[3]); + colors[i][j][3] = alpha; // Alpha + } else if (l === 1) { // upper right corner + colors[i][j + 1] = []; + colors[i][j + 1][0] = Math.floor(colorRaw[1]); + colors[i][j + 1][1] = Math.floor(colorRaw[2]); + colors[i][j + 1][2] = Math.floor(colorRaw[3]); + colors[i][j + 1][3] = alpha; // Alpha + } else if (l === 2) { // lower right corner + colors[i + 1][j + 1] = []; + colors[i + 1][j + 1][0] = Math.floor(colorRaw[1]); + colors[i + 1][j + 1][1] = Math.floor(colorRaw[2]); + colors[i + 1][j + 1][2] = Math.floor(colorRaw[3]); + colors[i + 1][j + 1][3] = alpha; // Alpha + } else if (l === 3) { // lower left corner + colors[i + 1][j] = []; + colors[i + 1][j][0] = Math.floor(colorRaw[1]); + colors[i + 1][j][1] = Math.floor(colorRaw[2]); + colors[i + 1][j][2] = Math.floor(colorRaw[3]); + colors[i + 1][j][3] = alpha; // Alpha + } + } + } + } + + // SVG doesn't use tensor points but we need them for rendering. + nodes[3 * i + 1][3 * j + 1] = new Point(); + nodes[3 * i + 1][3 * j + 2] = new Point(); + nodes[3 * i + 2][3 * j + 1] = new Point(); + nodes[3 * i + 2][3 * j + 2] = new Point(); + + nodes[3 * i + 1][3 * j + 1].x = + (-4.0 * nodes[3 * i][3 * j].x + + 6.0 * (nodes[3 * i][3 * j + 1].x + nodes[3 * i + 1][3 * j].x) + + -2.0 * (nodes[3 * i][3 * j + 3].x + nodes[3 * i + 3][3 * j].x) + + 3.0 * (nodes[3 * i + 3][3 * j + 1].x + nodes[3 * i + 1][3 * j + 3].x) + + -1.0 * nodes[3 * i + 3][3 * j + 3].x) / 9.0; + nodes[3 * i + 1][3 * j + 2].x = + (-4.0 * nodes[3 * i][3 * j + 3].x + + 6.0 * (nodes[3 * i][3 * j + 2].x + nodes[3 * i + 1][3 * j + 3].x) + + -2.0 * (nodes[3 * i][3 * j].x + nodes[3 * i + 3][3 * j + 3].x) + + 3.0 * (nodes[3 * i + 3][3 * j + 2].x + nodes[3 * i + 1][3 * j].x) + + -1.0 * nodes[3 * i + 3][3 * j].x) / 9.0; + nodes[3 * i + 2][3 * j + 1].x = + (-4.0 * nodes[3 * i + 3][3 * j].x + + 6.0 * (nodes[3 * i + 3][3 * j + 1].x + nodes[3 * i + 2][3 * j].x) + + -2.0 * (nodes[3 * i + 3][3 * j + 3].x + nodes[3 * i][3 * j].x) + + 3.0 * (nodes[3 * i][3 * j + 1].x + nodes[3 * i + 2][3 * j + 3].x) + + -1.0 * nodes[3 * i][3 * j + 3].x) / 9.0; + nodes[3 * i + 2][3 * j + 2].x = + (-4.0 * nodes[3 * i + 3][3 * j + 3].x + + 6.0 * (nodes[3 * i + 3][3 * j + 2].x + nodes[3 * i + 2][3 * j + 3].x) + + -2.0 * (nodes[3 * i + 3][3 * j].x + nodes[3 * i][3 * j + 3].x) + + 3.0 * (nodes[3 * i][3 * j + 2].x + nodes[3 * i + 2][3 * j].x) + + -1.0 * nodes[3 * i][3 * j].x) / 9.0; + + nodes[3 * i + 1][3 * j + 1].y = + (-4.0 * nodes[3 * i][3 * j].y + + 6.0 * (nodes[3 * i][3 * j + 1].y + nodes[3 * i + 1][3 * j].y) + + -2.0 * (nodes[3 * i][3 * j + 3].y + nodes[3 * i + 3][3 * j].y) + + 3.0 * (nodes[3 * i + 3][3 * j + 1].y + nodes[3 * i + 1][3 * j + 3].y) + + -1.0 * nodes[3 * i + 3][3 * j + 3].y) / 9.0; + nodes[3 * i + 1][3 * j + 2].y = + (-4.0 * nodes[3 * i][3 * j + 3].y + + 6.0 * (nodes[3 * i][3 * j + 2].y + nodes[3 * i + 1][3 * j + 3].y) + + -2.0 * (nodes[3 * i][3 * j].y + nodes[3 * i + 3][3 * j + 3].y) + + 3.0 * (nodes[3 * i + 3][3 * j + 2].y + nodes[3 * i + 1][3 * j].y) + + -1.0 * nodes[3 * i + 3][3 * j].y) / 9.0; + nodes[3 * i + 2][3 * j + 1].y = + (-4.0 * nodes[3 * i + 3][3 * j].y + + 6.0 * (nodes[3 * i + 3][3 * j + 1].y + nodes[3 * i + 2][3 * j].y) + + -2.0 * (nodes[3 * i + 3][3 * j + 3].y + nodes[3 * i][3 * j].y) + + 3.0 * (nodes[3 * i][3 * j + 1].y + nodes[3 * i + 2][3 * j + 3].y) + + -1.0 * nodes[3 * i][3 * j + 3].y) / 9.0; + nodes[3 * i + 2][3 * j + 2].y = + (-4.0 * nodes[3 * i + 3][3 * j + 3].y + + 6.0 * (nodes[3 * i + 3][3 * j + 2].y + nodes[3 * i + 2][3 * j + 3].y) + + -2.0 * (nodes[3 * i + 3][3 * j].y + nodes[3 * i][3 * j + 3].y) + + 3.0 * (nodes[3 * i][3 * j + 2].y + nodes[3 * i + 2][3 * j].y) + + -1.0 * nodes[3 * i][3 * j].y) / 9.0; + } + } + + this.nodes = nodes; // (m*3+1) x (n*3+1) points + this.colors = colors; // (m+1) x (n+1) x 4 colors (R+G+B+A) + } + + // Extracts out each patch and then paints it + paintMesh (v, w) { + let imax = (this.nodes.length - 1) / 3; + let jmax = (this.nodes[0].length - 1) / 3; + + if (this.type === 'bilinear' || imax < 2 || jmax < 2) { + let patch; + + for (let i = 0; i < imax; ++i) { + for (let j = 0; j < jmax; ++j) { + let sliceNodes = []; + for (let k = i * 3, kmax = (i * 3) + 4; k < kmax; ++k) { + sliceNodes.push(this.nodes[k].slice(j * 3, (j * 3) + 4)); + } + + let sliceColors = []; + sliceColors.push(this.colors[i].slice(j, j + 2)); + sliceColors.push(this.colors[i + 1].slice(j, j + 2)); + + patch = new Patch(sliceNodes, sliceColors); + patch.paint(v, w); + } + } + } else { + // Reference: + // https://en.wikipedia.org/wiki/Bicubic_interpolation#Computation + let d01, d12, patch, sliceNodes, nodes, f, alpha; + const ilast = imax; + const jlast = jmax; + imax++; + jmax++; + + /* + * d = the interpolation data + * d[i][j] = a node record (Point, color_array, color_dx, color_dy) + * d[i][j][0] : Point + * d[i][j][1] : [RGBA] + * d[i][j][2] = dx [RGBA] + * d[i][j][3] = dy [RGBA] + * d[i][j][][k] : color channel k + */ + let d = new Array(imax); + + // Setting the node and the colors + for (let i = 0; i < imax; ++i) { + d[i] = new Array(jmax); + for (let j = 0; j < jmax; ++j) { + d[i][j] = []; + d[i][j][0] = this.nodes[3 * i][3 * j]; + d[i][j][1] = this.colors[i][j]; + } + } + + // Calculate the inner derivatives + for (let i = 0; i < imax; ++i) { + for (let j = 0; j < jmax; ++j) { + // dx + if (i !== 0 && i !== ilast) { + d01 = distance(d[i - 1][j][0], d[i][j][0]); + d12 = distance(d[i + 1][j][0], d[i][j][0]); + d[i][j][2] = finiteDifferences(d[i - 1][j][1], d[i][j][1], + d[i + 1][j][1], d01, d12); + } + + // dy + if (j !== 0 && j !== jlast) { + d01 = distance(d[i][j - 1][0], d[i][j][0]); + d12 = distance(d[i][j + 1][0], d[i][j][0]); + d[i][j][3] = finiteDifferences(d[i][j - 1][1], d[i][j][1], + d[i][j + 1][1], d01, d12); + } + + // dxy is, by standard, set to 0 + } + } + + /* + * Calculate the exterior derivatives + * We fit the exterior derivatives onto parabolas generated by + * the point and the interior derivatives. + */ + for (let j = 0; j < jmax; ++j) { + d[0][j][2] = []; + d[ilast][j][2] = []; + + for (let k = 0; k < 4; ++k) { + d01 = distance(d[1][j][0], d[0][j][0]); + d12 = distance(d[ilast][j][0], d[ilast - 1][j][0]); + + if (d01 > 0) { + d[0][j][2][k] = 2.0 * (d[1][j][1][k] - d[0][j][1][k]) / d01 - + d[1][j][2][k]; + } else { + console.log(`0 was 0! (j: ${j}, k: ${k})`); + d[0][j][2][k] = 0; + } + + if (d12 > 0) { + d[ilast][j][2][k] = 2.0 * (d[ilast][j][1][k] - d[ilast - 1][j][1][k]) / + d12 - d[ilast - 1][j][2][k]; + } else { + console.log(`last was 0! (j: ${j}, k: ${k})`); + d[ilast][j][2][k] = 0; + } + } + } + + for (let i = 0; i < imax; ++i) { + d[i][0][3] = []; + d[i][jlast][3] = []; + + for (let k = 0; k < 4; ++k) { + d01 = distance(d[i][1][0], d[i][0][0]); + d12 = distance(d[i][jlast][0], d[i][jlast - 1][0]); + + if (d01 > 0) { + d[i][0][3][k] = 2.0 * (d[i][1][1][k] - d[i][0][1][k]) / d01 - + d[i][1][3][k]; + } else { + console.log(`0 was 0! (i: ${i}, k: ${k})`); + d[i][0][3][k] = 0; + } + + if (d12 > 0) { + d[i][jlast][3][k] = 2.0 * (d[i][jlast][1][k] - d[i][jlast - 1][1][k]) / + d12 - d[i][jlast - 1][3][k]; + } else { + console.log(`last was 0! (i: ${i}, k: ${k})`); + d[i][jlast][3][k] = 0; + } + } + } + + // Fill patches + for (let i = 0; i < ilast; ++i) { + for (let j = 0; j < jlast; ++j) { + let dLeft = distance(d[i][j][0], d[i + 1][j][0]); + let dRight = distance(d[i][j + 1][0], d[i + 1][j + 1][0]); + let dTop = distance(d[i][j][0], d[i][j + 1][0]); + let dBottom = distance(d[i + 1][j][0], d[i + 1][j + 1][0]); + let r = [[], [], [], []]; + + for (let k = 0; k < 4; ++k) { + f = []; + + f[0] = d[i][j][1][k]; + f[1] = d[i + 1][j][1][k]; + f[2] = d[i][j + 1][1][k]; + f[3] = d[i + 1][j + 1][1][k]; + f[4] = d[i][j][2][k] * dLeft; + f[5] = d[i + 1][j][2][k] * dLeft; + f[6] = d[i][j + 1][2][k] * dRight; + f[7] = d[i + 1][j + 1][2][k] * dRight; + f[8] = d[i][j][3][k] * dTop; + f[9] = d[i + 1][j][3][k] * dBottom; + f[10] = d[i][j + 1][3][k] * dTop; + f[11] = d[i + 1][j + 1][3][k] * dBottom; + f[12] = 0; // dxy + f[13] = 0; // dxy + f[14] = 0; // dxy + f[15] = 0; // dxy + + // get alpha values + alpha = solveLinearSystem(f); + + for (let l = 0; l < 9; ++l) { + r[k][l] = []; + + for (let m = 0; m < 9; ++m) { + // evaluation + r[k][l][m] = evaluateSolution(alpha, l / 8, m / 8); + + if (r[k][l][m] > 255) { + r[k][l][m] = 255; + } else if (r[k][l][m] < 0.0) { + r[k][l][m] = 0.0; + } + } + } + } + + // split the bezier patch into 8x8 patches + sliceNodes = []; + for (let k = i * 3, kmax = (i * 3) + 4; k < kmax; ++k) { + sliceNodes.push(this.nodes[k].slice(j * 3, (j * 3) + 4)); + } + + nodes = splitPatch(sliceNodes); + + // Create patches and paint the bilinearliy + for (let l = 0; l < 8; ++l) { + for (let m = 0; m < 8; ++m) { + patch = new Patch( + nodes[l][m], + [[ + [r[0][l][m], r[1][l][m], r[2][l][m], r[3][l][m]], + [r[0][l][m + 1], r[1][l][m + 1], r[2][l][m + 1], r[3][l][m + 1]] + ], [ + [r[0][l + 1][m], r[1][l + 1][m], r[2][l + 1][m], r[3][l + 1][m]], + [r[0][l + 1][m + 1], r[1][l + 1][m + 1], r[2][l + 1][m + 1], r[3][l + 1][m + 1]] + ]] + ); + + patch.paint(v, w); + } + } + } + } + } + } // Transforms mesh into coordinate space of canvas (t is either Point or Affine). - Mesh.prototype.transform = function(t) { - // console.log( "t: " + t ); - if (t instanceof Point) { - for (var i = 0; i < this.nodes.length; ++i) { - for (var j = 0; j < this.nodes[0].length; ++j) { - this.nodes[i][j] = this.nodes[i][j].add(t); - } - } - } - if (t instanceof Affine) { - for (var i = 0; i < this.nodes.length; ++i) { - for (var j = 0; j < this.nodes[0].length; ++j) { - this.nodes[i][j] = this.nodes[i][j].transform(t); - } - } - } - }; + transform (t) { + if (t instanceof Point) { + for (let i = 0, imax = this.nodes.length; i < imax; ++i) { + for (let j = 0, jmax = this.nodes[0].length; j < jmax; ++j) { + this.nodes[i][j] = this.nodes[i][j].add(t); + } + } + } else if (t instanceof Affine) { + for (let i = 0, imax = this.nodes.length; i < imax; ++i) { + for (let j = 0, jmax = this.nodes[0].length; j < jmax; ++j) { + this.nodes[i][j] = this.nodes[i][j].transform(t); + } + } + } + } // Scale mesh into coordinate space of canvas (t is a Point). - Mesh.prototype.scale = function(t) { - for (var i = 0; i < this.nodes.length; ++i) { - for (var j = 0; j < this.nodes[0].length; ++j) { - this.nodes[i][j] = this.nodes[i][j].scale(t); - } - } - }; - - function parse_points(s) { - - var points = []; - var values = s.split(/[ ,]+/); - for (var i = 0; i < values.length-1; i += 2) { - points.push( new Point( parseFloat( values[i]), parseFloat( values[i+1] ))); - } - return points; + scale (t) { + for (let i = 0, imax = this.nodes.length; i < imax; ++i) { + for (let j = 0, jmax = this.nodes[0].length; j < jmax; ++j) { + this.nodes[i][j] = this.nodes[i][j].scale(t); + } + } } + } - // Start of document processing --------------------- - - var shapes = document.querySelectorAll('rect,circle,ellipse,path,text'); - // console.log("Shapes: " + shapes.length); - - for (var i = 0; i < shapes.length; ++i) { - var shape = shapes[i]; - // console.log( shape.nodeName ); - // Get id. If no id, create one. - var shape_id = shape.getAttribute("id"); - if (!shape_id) { - shape_id = "patchjs_shape" + i; - shape.setAttribute("id", shape_id); - } - // console.log( "id: " + shape_id ); - - var fill = shape.style.fill; - var url_value = fill.match(/^url\(\s*\"?\s*#([^\s\"]+)\"?\s*\)/); - if (url_value && url_value[1]) { - // console.log( "Got url! " + url_value[1]); - var mesh = document.getElementById(url_value[1]); - // console.log( mesh ); - // console.log( mesh.nodeName ); - if (mesh.nodeName === "meshgradient" ) { - // console.log( "Got mesh" ); - var bbox = shape.getBBox(); - // console.log( bbox ); - - // Create temporary canvas - var my_canvas = document.createElementNS( xhtmlNS, "canvas" ); - //var my_canvas = document.createElement( "canvas" ); // Both work for HTML... - my_canvas.width = bbox.width; - my_canvas.height = bbox.height; - - // console.log ( "Canvas: " + my_canvas.width + "x" + my_canvas.height ); - var my_context = my_canvas.getContext("2d"); - - var my_canvas_image = my_context.getImageData( 0, 0, my_canvas.width, my_canvas.height); - var my_data = my_canvas_image.data; - - // Draw a mesh - var my_mesh = new Mesh( url_value[1] ); - - // Adjust for bounding box if necessary. - if (mesh.getAttribute( "gradientUnits" ) === "objectBoundingBox") { - my_mesh.scale( new Point( bbox.width, bbox.height ) ); - } - - // Apply gradient transform. - var gradientTransform = mesh.getAttribute("gradientTransform"); - // console.log( typeof gradientTransform ); - if ( gradientTransform != null ) { - var affine = parseTransform( gradientTransform ); - my_mesh.transform( affine ); - } - - // Position to Canvas coordinate. - var t = new Point( -bbox.x, -bbox.y ); - if (mesh.getAttribute( "gradientUnits" ) === "userSpaceOnUse") { - my_mesh.transform(t); - } - - // Paint - my_mesh.paint(my_data, my_canvas.width); - - my_context.putImageData(my_canvas_image, 0, 0); - - // Create image element of correct size - var my_image = document.createElementNS( svgNS, "image" ); - my_image.setAttribute("width", my_canvas.width); - my_image.setAttribute("height",my_canvas.height); - my_image.setAttribute("x", bbox.x); - my_image.setAttribute("y", bbox.y); - - // Set image to data url - var my_png = my_canvas.toDataURL(); - my_image.setAttributeNS(xlinkNS, "xlink:href", my_png); - - // Insert image into document - shape.parentNode.insertBefore( my_image, shape ); - shape.style.fill = "none"; - - // Create clip referencing shape and insert into document - var clip = document.createElementNS( svgNS, "clipPath"); - var clip_id = "patchjs_clip" + i; - clip.setAttribute("id", clip_id); - var use = document.createElementNS( svgNS, "use"); - use.setAttributeNS( xlinkNS, "xlink:href", "#" + shape_id); - clip.appendChild(use); - shape.parentElement.insertBefore(clip, shape); - my_image.setAttribute("clip-path", "url(#" + clip_id + ")"); - } - } + // Start of document processing --------------------- + const shapes = document.querySelectorAll('rect,circle,ellipse,path,text'); + + shapes.forEach((shape, i) => { + // Get id. If no id, create one. + let shapeId = shape.getAttribute('id'); + if (!shapeId) { + shapeId = 'patchjs_shape' + i; + shape.setAttribute('id', shapeId); } -})(); + const fillURL = shape.style.fill.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/); + const strokeURL = shape.style.stroke.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/); + + if (fillURL && fillURL[1]) { + const mesh = document.getElementById(fillURL[1]); + + if (mesh && mesh.nodeName === 'meshgradient') { + const bbox = shape.getBBox(); + + // Create temporary canvas + let myCanvas = document.createElementNS(xhtmlNS, 'canvas'); + setAttributes(myCanvas, { + 'width': bbox.width, + 'height': bbox.height + }); + + const myContext = myCanvas.getContext('2d'); + let myCanvasImage = myContext.createImageData(bbox.width, bbox.height); + + // Draw a mesh + const myMesh = new Mesh(mesh); + + // Adjust for bounding box if necessary. + if (mesh.getAttribute('gradientUnits') === 'objectBoundingBox') { + myMesh.scale(new Point(bbox.width, bbox.height)); + } + + // Apply gradient transform. + const gradientTransform = mesh.getAttribute('gradientTransform'); + if (gradientTransform != null) { + myMesh.transform(parseTransform(gradientTransform)); + } + + // Position to Canvas coordinate. + if (mesh.getAttribute('gradientUnits') === 'userSpaceOnUse') { + myMesh.transform(new Point(-bbox.x, -bbox.y)); + } + + // Paint + myMesh.paintMesh(myCanvasImage.data, myCanvas.width); + myContext.putImageData(myCanvasImage, 0, 0); + + // Create image element of correct size + const myImage = document.createElementNS(svgNS, 'image'); + setAttributes(myImage, { + 'width': bbox.width, + 'height': bbox.height, + 'x': bbox.x, + 'y': bbox.y + }); + + // Set image to data url + let myPNG = myCanvas.toDataURL(); + myImage.setAttributeNS(xlinkNS, 'xlink:href', myPNG); + + // Insert image into document + shape.parentNode.insertBefore(myImage, shape); + shape.style.fill = 'none'; + + // Create clip referencing shape and insert into document + const use = document.createElementNS(svgNS, 'use'); + use.setAttributeNS(xlinkNS, 'xlink:href', '#' + shapeId); + + const clipId = 'patchjs_clip' + i; + const clip = document.createElementNS(svgNS, 'clipPath'); + clip.setAttribute('id', clipId); + clip.appendChild(use); + shape.parentElement.insertBefore(clip, shape); + myImage.setAttribute('clip-path', 'url(#' + clipId + ')'); + + // Force the Garbage Collector to free the space + myCanvasImage = null; + myCanvas = null; + myPNG = null; + } + } + + if (strokeURL && strokeURL[1]) { + const mesh = document.getElementById(strokeURL[1]); + + if (mesh && mesh.nodeName === 'meshgradient') { + const strokeWidth = parseFloat(shape.style.strokeWidth.slice(0, -2)); + const strokeMiterlimit = parseFloat(shape.style.strokeMiterlimit) || + parseFloat(shape.getAttribute('stroke-miterlimit')) || 1; + const phase = strokeWidth * strokeMiterlimit; + + const bbox = shape.getBBox(); + const boxWidth = Math.trunc(bbox.width + phase); + const boxHeight = Math.trunc(bbox.height + phase); + const boxX = Math.trunc(bbox.x - phase / 2); + const boxY = Math.trunc(bbox.y - phase / 2); + + // Create temporary canvas + let myCanvas = document.createElementNS(xhtmlNS, 'canvas'); + setAttributes(myCanvas, { + 'width': boxWidth, + 'height': boxHeight + }); + + const myContext = myCanvas.getContext('2d'); + let myCanvasImage = myContext.createImageData(boxWidth, boxHeight); + + // Draw a mesh + const myMesh = new Mesh(mesh); + + // Adjust for bounding box if necessary. + if (mesh.getAttribute('gradientUnits') === 'objectBoundingBox') { + myMesh.scale(new Point(boxWidth, boxHeight)); + } + + // Apply gradient transform. + const gradientTransform = mesh.getAttribute('gradientTransform'); + if (gradientTransform != null) { + myMesh.transform(parseTransform(gradientTransform)); + } + + // Position to Canvas coordinate. + if (mesh.getAttribute('gradientUnits') === 'userSpaceOnUse') { + myMesh.transform(new Point(-boxX, -boxY)); + } + + // Paint + myMesh.paintMesh(myCanvasImage.data, myCanvas.width); + myContext.putImageData(myCanvasImage, 0, 0); + + // Create image element of correct size + const myImage = document.createElementNS(svgNS, 'image'); + setAttributes(myImage, { + 'width': boxWidth, + 'height': boxHeight, + 'x': 0, + 'y': 0 + }); + + // Set image to data url + let myPNG = myCanvas.toDataURL(); + myImage.setAttributeNS(xlinkNS, 'xlink:href', myPNG); + + // Create pattern to hold the stroke image + const patternId = 'pattern_clip' + i; + const myPattern = document.createElementNS(svgNS, 'pattern'); + setAttributes(myPattern, { + 'id': patternId, + 'patternUnits': 'userSpaceOnUse', + 'width': boxWidth, + 'height': boxHeight, + 'x': boxX, + 'y': boxY + }); + myPattern.appendChild(myImage); + + // Insert image into document + mesh.parentNode.appendChild(myPattern); + shape.style.stroke = 'url(#' + patternId + ')'; + + // Force the Garbage Collector to free the space + myCanvasImage = null; + myCanvas = null; + myPNG = null; + } + } + }); +})(); diff --git a/src/extension/internal/polyfill/mesh_compressed.include b/src/extension/internal/polyfill/mesh_compressed.include index 464f912a8..c6eeca0bb 100644 --- a/src/extension/internal/polyfill/mesh_compressed.include +++ b/src/extension/internal/polyfill/mesh_compressed.include @@ -1,4 +1,4 @@ //SPDX-License-Identifier: GPL-2.0-or-later R"=====( -!function(){var t="http://www.w3.org/2000/svg",e="http://www.w3.org/1999/xlink";if(!document.createElementNS(t,"meshgradient").x){M.prototype.x=null,M.prototype.y=null,M.prototype.get_x=function(){return this.x},M.prototype.get_y=function(){return this.y},M.prototype.clone=function(){return new M(this.x,this.y)},M.prototype.add=function(t){return new M(this.x+t.x,this.y+t.y)},M.prototype.scale=function(t){return t instanceof M?new M(this.x*t.x,this.y*t.y):new M(this.x*t,this.y*t)},M.prototype.transform=function(t){return new M(this.x*t.a+this.y*t.c+t.e,this.x*t.b+this.y*t.d+t.f)},M.prototype.dist_sq=function(t){var e=this.x-t.x,s=this.y-t.y;return e*e+s*s},M.prototype.toString=function(){return"(x="+this.x+", y="+this.y+")"},I.prototype.a=null,I.prototype.b=null,I.prototype.c=null,I.prototype.d=null,I.prototype.e=null,I.prototype.f=null,I.prototype.append=function(t){return t instanceof I||console.log("mesh.js: argument to Affine.append is not affine!"),new I(this.a*t.a+this.c*t.b,this.b*t.a+this.d*t.b,this.a*t.c+this.c*t.d,this.b*t.c+this.d*t.d,this.a*t.e+this.c*t.f+this.e,this.b*t.e+this.d*t.f+this.f)},I.prototype.toString=function(){return"affine: "+this.a+" "+this.c+" "+this.e+"\n "+this.b+" "+this.d+" "+this.f},_.prototype.paint_curve=function(t,e){if(2<S(this.nodes)){for(var s=A(this.nodes[0],this.nodes[1],this.nodes[2],this.nodes[3]),n=[[],[]],o=[[],[]],i=0;i<4;++i)n[0][i]=this.colors[0][i],n[1][i]=(this.colors[0][i]+this.colors[1][i])/2,o[0][i]=(this.colors[0][i]+this.colors[1][i])/2,o[1][i]=this.colors[1][i];var r=new _(s[0],n),a=new _(s[1],o);r.paint_curve(t,e),a.paint_curve(t,e)}else{0;var h=Math.round(this.nodes[0].x),d=Math.round(this.nodes[0].y);if(0<=h&&h<e){var l=4*(d*e+h);t[l]=Math.round(this.colors[0][0]),t[l+1]=Math.round(this.colors[0][1]),t[l+2]=Math.round(this.colors[0][2]),t[l+3]=Math.round(this.colors[0][3])}}},N.prototype.setOutline=function(t){t.beginPath(),t.moveTo(this.nodes[0][0].x,this.nodes[0][0].y),t.bezierCurveTo(this.nodes[0][1].x,this.nodes[0][1].y,this.nodes[0][2].x,this.nodes[0][2].y,this.nodes[0][3].x,this.nodes[0][3].y),t.bezierCurveTo(this.nodes[1][3].x,this.nodes[1][3].y,this.nodes[2][3].x,this.nodes[2][3].y,this.nodes[3][3].x,this.nodes[3][3].y),t.bezierCurveTo(this.nodes[3][2].x,this.nodes[3][2].y,this.nodes[3][1].x,this.nodes[3][1].y,this.nodes[3][0].x,this.nodes[3][0].y),t.bezierCurveTo(this.nodes[2][0].x,this.nodes[2][0].y,this.nodes[1][0].x,this.nodes[1][0].y,this.nodes[0][0].x,this.nodes[0][0].y),t.closePath()},N.prototype.drawOutline=function(t){this.setOutline(t),t.strokeStyle="black",t.stroke()},N.prototype.fillOutline=function(t){var e;this.setOutline(t),t.fillStyle=(e=this.colors[0],"rgb("+Math.round(e[0])+","+Math.round(e[1])+","+Math.round(e[2])+")"),t.fill()},N.prototype.split=function(){for(var t=[[],[],[],[]],e=[[],[],[],[]],s=[[[],[]],[[],[]]],n=[[[],[]],[[],[]]],o=0;o<4;++o)for(var i=A(this.nodes[0][o],this.nodes[1][o],this.nodes[2][o],this.nodes[3][o]),r=0;r<4;++r)t[0][o]=i[0][0],t[1][o]=i[0][1],t[2][o]=i[0][2],t[3][o]=i[0][3],e[0][o]=i[1][0],e[1][o]=i[1][1],e[2][o]=i[1][2],e[3][o]=i[1][3];for(o=0;o<4;++o)s[0][0][o]=this.colors[0][0][o],s[0][1][o]=this.colors[0][1][o],s[1][0][o]=(this.colors[0][0][o]+this.colors[1][0][o])/2,s[1][1][o]=(this.colors[0][1][o]+this.colors[1][1][o])/2,n[0][0][o]=(this.colors[0][0][o]+this.colors[1][0][o])/2,n[0][1][o]=(this.colors[0][1][o]+this.colors[1][1][o])/2,n[1][0][o]=this.colors[1][0][o],n[1][1][o]=this.colors[1][1][o];return[new N(t,s),new N(e,n)]},N.prototype.paint=function(t,e){for(var s=[],n=0;n<4;++n)s[n]=S([this.nodes[0][n],this.nodes[1][n],this.nodes[2][n],this.nodes[3][n]]);if(2<Math.max.apply(null,s)){var o=this.split();o[0].paint(t,e),o[1].paint(t,e)}else this.paint_curve(t,e)},N.prototype.paint_curve=function(t,e){new _([this.nodes[0][0],this.nodes[0][1],this.nodes[0][2],this.nodes[0][3]],[this.colors[0][0],this.colors[0][1]]).paint_curve(t,e)},j.prototype.readMesh=function(t){var e=[],s=[],n=document.getElementById(t);if(null==n)console.log("mesh.js: Could not find mesh: "+t);else{e[0]=[],s[0]=[];var o=Number(n.getAttribute("x")),i=Number(n.getAttribute("y"));e[0][0]=new M(o,i);for(var r=n.children,a=0;a<r.length;++a){e[3*a+1]=[],e[3*a+2]=[],e[3*a+3]=[],s[a+1]=[];for(var h=r[a].children,d=0;d<h.length;++d){for(var l=h[d].children,c=0;c<l.length;++c){var p=c;0!=a&&++p;var u=l[c].getAttribute("path"),f="l";if(null!=u){var y=u.match(/\s*([lLcC])\s*(.*)/);f=y[1]}var x=B(y[2]);switch(f){case"l":0==p?(e[3*a][3*d+3]=x[0].add(e[3*a][3*d]),e[3*a][3*d+1]=C(e[3*a][3*d],e[3*a][3*d+3]),e[3*a][3*d+2]=C(e[3*a][3*d+3],e[3*a][3*d])):1==p?(e[3*a+3][3*d+3]=x[0].add(e[3*a][3*d+3]),e[3*a+1][3*d+3]=C(e[3*a][3*d+3],e[3*a+3][3*d+3]),e[3*a+2][3*d+3]=C(e[3*a+3][3*d+3],e[3*a][3*d+3])):2==p?(0==d&&(e[3*a+3][3*d+0]=x[0].add(e[3*a+3][3*d+3])),e[3*a+3][3*d+1]=C(e[3*a+3][3*d],e[3*a+3][3*d+3]),e[3*a+3][3*d+2]=C(e[3*a+3][3*d+3],e[3*a+3][3*d])):(e[3*a+1][3*d]=C(e[3*a][3*d],e[3*a+3][3*d]),e[3*a+2][3*d]=C(e[3*a+3][3*d],e[3*a][3*d]));break;case"L":0==p?(e[3*a][3*d+3]=x[0],e[3*a][3*d+1]=C(e[3*a][3*d],e[3*a][3*d+3]),e[3*a][3*d+2]=C(e[3*a][3*d+3],e[3*a][3*d])):1==p?(e[3*a+3][3*d+3]=x[0],e[3*a+1][3*d+3]=C(e[3*a][3*d+3],e[3*a+3][3*d+3]),e[3*a+2][3*d+3]=C(e[3*a+3][3*d+3],e[3*a][3*d+3])):2==p?(0==d&&(e[3*a+3][3*d+0]=x[0]),e[3*a+3][3*d+1]=C(e[3*a+3][3*d],e[3*a+3][3*d+3]),e[3*a+3][3*d+2]=C(e[3*a+3][3*d+3],e[3*a+3][3*d])):(e[3*a+1][3*d]=C(e[3*a][3*d],e[3*a+3][3*d]),e[3*a+2][3*d]=C(e[3*a+3][3*d],e[3*a][3*d]));break;case"c":0==p?(e[3*a][3*d+1]=x[0].add(e[3*a][3*d]),e[3*a][3*d+2]=x[1].add(e[3*a][3*d]),e[3*a][3*d+3]=x[2].add(e[3*a][3*d])):1==p?(e[3*a+1][3*d+3]=x[0].add(e[3*a][3*d+3]),e[3*a+2][3*d+3]=x[1].add(e[3*a][3*d+3]),e[3*a+3][3*d+3]=x[2].add(e[3*a][3*d+3])):2==p?(e[3*a+3][3*d+2]=x[0].add(e[3*a+3][3*d+3]),e[3*a+3][3*d+1]=x[1].add(e[3*a+3][3*d+3]),0==d&&(e[3*a+3][3*d+0]=x[2].add(e[3*a+3][3*d+3]))):(e[3*a+2][3*d]=x[0].add(e[3*a+3][3*d]),e[3*a+1][3*d]=x[1].add(e[3*a+3][3*d]));break;case"C":0==p?(e[3*a][3*d+1]=x[0],e[3*a][3*d+2]=x[1],e[3*a][3*d+3]=x[2]):1==p?(e[3*a+1][3*d+3]=x[0],e[3*a+2][3*d+3]=x[1],e[3*a+3][3*d+3]=x[2]):2==p?(e[3*a+3][3*d+2]=x[0],e[3*a+3][3*d+1]=x[1],0==d&&(e[3*a+3][3*d+0]=x[2])):(e[3*a+2][3*d]=x[0],e[3*a+1][3*d]=x[1]);break;default:console.log("mesh.js: "+f+" invalid path type.")}if(0==a&&0==d||0<c){var g=getComputedStyle(l[c]).stopColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i),v=getComputedStyle(l[c]).stopOpacity,m=255;v&&(m=parseInt(255*v)),g&&(0==p?(s[a][d]=[],s[a][d][0]=parseInt(g[1]),s[a][d][1]=parseInt(g[2]),s[a][d][2]=parseInt(g[3]),s[a][d][3]=m):1==p?(s[a][d+1]=[],s[a][d+1][0]=parseInt(g[1]),s[a][d+1][1]=parseInt(g[2]),s[a][d+1][2]=parseInt(g[3]),s[a][d+1][3]=m):2==p?(s[a+1][d+1]=[],s[a+1][d+1][0]=parseInt(g[1]),s[a+1][d+1][1]=parseInt(g[2]),s[a+1][d+1][2]=parseInt(g[3]),s[a+1][d+1][3]=m):3==p&&(s[a+1][d]=[],s[a+1][d][0]=parseInt(g[1]),s[a+1][d][1]=parseInt(g[2]),s[a+1][d][2]=parseInt(g[3]),s[a+1][d][3]=m))}}e[3*a+1][3*d+1]=new M,e[3*a+1][3*d+2]=new M,e[3*a+2][3*d+1]=new M,e[3*a+2][3*d+2]=new M,e[3*a+1][3*d+1].x=(-4*e[3*a][3*d].x+6*(e[3*a][3*d+1].x+e[3*a+1][3*d].x)+-2*(e[3*a][3*d+3].x+e[3*a+3][3*d].x)+3*(e[3*a+3][3*d+1].x+e[3*a+1][3*d+3].x)+-1*e[3*a+3][3*d+3].x)/9,e[3*a+1][3*d+2].x=(-4*e[3*a][3*d+3].x+6*(e[3*a][3*d+2].x+e[3*a+1][3*d+3].x)+-2*(e[3*a][3*d].x+e[3*a+3][3*d+3].x)+3*(e[3*a+3][3*d+2].x+e[3*a+1][3*d].x)+-1*e[3*a+3][3*d].x)/9,e[3*a+2][3*d+1].x=(-4*e[3*a+3][3*d].x+6*(e[3*a+3][3*d+1].x+e[3*a+2][3*d].x)+-2*(e[3*a+3][3*d+3].x+e[3*a][3*d].x)+3*(e[3*a][3*d+1].x+e[3*a+2][3*d+3].x)+-1*e[3*a][3*d+3].x)/9,e[3*a+2][3*d+2].x=(-4*e[3*a+3][3*d+3].x+6*(e[3*a+3][3*d+2].x+e[3*a+2][3*d+3].x)+-2*(e[3*a+3][3*d].x+e[3*a][3*d+3].x)+3*(e[3*a][3*d+2].x+e[3*a+2][3*d].x)+-1*e[3*a][3*d].x)/9,e[3*a+1][3*d+1].y=(-4*e[3*a][3*d].y+6*(e[3*a][3*d+1].y+e[3*a+1][3*d].y)+-2*(e[3*a][3*d+3].y+e[3*a+3][3*d].y)+3*(e[3*a+3][3*d+1].y+e[3*a+1][3*d+3].y)+-1*e[3*a+3][3*d+3].y)/9,e[3*a+1][3*d+2].y=(-4*e[3*a][3*d+3].y+6*(e[3*a][3*d+2].y+e[3*a+1][3*d+3].y)+-2*(e[3*a][3*d].y+e[3*a+3][3*d+3].y)+3*(e[3*a+3][3*d+2].y+e[3*a+1][3*d].y)+-1*e[3*a+3][3*d].y)/9,e[3*a+2][3*d+1].y=(-4*e[3*a+3][3*d].y+6*(e[3*a+3][3*d+1].y+e[3*a+2][3*d].y)+-2*(e[3*a+3][3*d+3].y+e[3*a][3*d].y)+3*(e[3*a][3*d+1].y+e[3*a+2][3*d+3].y)+-1*e[3*a][3*d+3].y)/9,e[3*a+2][3*d+2].y=(-4*e[3*a+3][3*d+3].y+6*(e[3*a+3][3*d+2].y+e[3*a+2][3*d+3].y)+-2*(e[3*a+3][3*d].y+e[3*a][3*d+3].y)+3*(e[3*a][3*d+2].y+e[3*a+2][3*d].y)+-1*e[3*a][3*d].y)/9}}}return{nodes:e,colors:s}},j.prototype.paint=function(t,e){for(var s=0;s<(this.nodes.length-1)/3;++s)for(var n=0;n<(this.nodes[0].length-1)/3;++n){for(var o=[],i=3*s;i<3*s+4;++i)o.push(this.nodes[i].slice(3*n,3*n+4));var r=[];r.push(this.colors[s].slice(n,n+2)),r.push(this.colors[s+1].slice(n,n+2)),new N(o,r).paint(t,e)}},j.prototype.transform=function(t){if(t instanceof M)for(var e=0;e<this.nodes.length;++e)for(var s=0;s<this.nodes[0].length;++s)this.nodes[e][s]=this.nodes[e][s].add(t);if(t instanceof I)for(e=0;e<this.nodes.length;++e)for(s=0;s<this.nodes[0].length;++s)this.nodes[e][s]=this.nodes[e][s].transform(t)},j.prototype.scale=function(t){for(var e=0;e<this.nodes.length;++e)for(var s=0;s<this.nodes[0].length;++s)this.nodes[e][s]=this.nodes[e][s].scale(t)};for(var s=document.querySelectorAll("rect,circle,ellipse,path,text"),n=0;n<s.length;++n){var o=s[n],i=o.getAttribute("id");i||(i="patchjs_shape"+n,o.setAttribute("id",i));var r=o.style.fill.match(/^url\(\s*\"?\s*#([^\s\"]+)\"?\s*\)/);if(r&&r[1]){var a=document.getElementById(r[1]);if("meshgradient"===a.nodeName){var h=o.getBBox(),d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=h.width,d.height=h.height;var l=d.getContext("2d"),c=l.getImageData(0,0,d.width,d.height),p=c.data,u=new j(r[1]);"objectBoundingBox"===a.getAttribute("gradientUnits")&&u.scale(new M(h.width,h.height));var f=a.getAttribute("gradientTransform");if(null!=f){var y=k(f);u.transform(y)}var x=new M(-h.x,-h.y);"userSpaceOnUse"===a.getAttribute("gradientUnits")&&u.transform(x),u.paint(p,d.width),l.putImageData(c,0,0);var g=document.createElementNS(t,"image");g.setAttribute("width",d.width),g.setAttribute("height",d.height),g.setAttribute("x",h.x),g.setAttribute("y",h.y);var v=d.toDataURL();g.setAttributeNS(e,"xlink:href",v),o.parentNode.insertBefore(g,o),o.style.fill="none";var m=document.createElementNS(t,"clipPath"),w="patchjs_clip"+n;m.setAttribute("id",w);var b=document.createElementNS(t,"use");b.setAttributeNS(e,"xlink:href","#"+i),m.appendChild(b),o.parentElement.insertBefore(m,o),g.setAttribute("clip-path","url(#"+w+")")}}}}function M(t,e){this.x=t||0,this.y=e||0}function I(t,e,s,n,o,i){void 0===t?(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0):(this.a=t,this.b=e,this.c=s,this.d=n,this.e=o,this.f=i)}function k(t){var e=new I;for(var s in t=t.match(/(\w+\(\s*(\-?\d+\.?\d*e?\-?\d*\s*,?\s*)+\))+/g)){var n=t[s].match(/[\w\.\-]+/g),o=n.shift();switch(o){case"translate":2==n.length?r=new I(1,0,0,1,n[0],n[1]):(console.log("mesh.js: translate does not have 2 arguments!"),r=new I(1,0,0,1,0,0)),console.log(r.toString()),e=e.append(r);break;case"scale":var i;1==n.length?i=new I(n[0],0,0,n[0],0,0):2==n.length?i=new I(n[0],0,0,n[1],0,0):(console.log("mesh.js: scale does not have 1 or 2 arguments!"),i=new I(1,0,0,1,0,0)),e=e.append(i);break;case"rotate":if(3==n.length){var r=new I(1,0,0,1,n[1],n[2]);e=e.append(r)}if(n[0]){var a=n[0]*Math.PI/180,h=Math.cos(a),d=Math.sin(a);Math.abs(h)<1e-16&&(h=0),Math.abs(d)<1e-16&&(d=0);var l=new I(h,d,-d,h,0,0);e=e.append(l)}else console.log("math.js: No argument to rotate transform!");if(3==n.length){r=new I(1,0,0,1,-n[1],-n[2]);e=e.append(r)}break;case"skewX":if(n[0]){a=n[0]*Math.PI/180;var c=new I(1,0,Math.tan(a),1,0,0);e=e.append(c)}else console.log("math.js: No argument to skewX transform!");break;case"skewY":if(n[0]){a=n[0]*Math.PI/180;var p=new I(1,Math.tan(a),0,1,0,0);e=e.append(p)}else console.log("math.js: No argument to skewY transform!");break;case"matrix":if(6==n.length){var u=new I(n[0],n[1],n[2],n[3],n[4],n[5]);e=e.append(u)}else console.log("math.js: Incorrect number of arguments for matrix!");break;default:console.log("mesh.js: Unhandled transform type: "+o)}}return e}function A(t,e,s,n){var o=t.clone(),i=n.clone(),r=e.add(s).scale(.5),a=t.add(e).scale(.5),h=s.add(n).scale(.5),d=a.add(r).scale(.5),l=r.add(h).scale(.5),c=d.add(l).scale(.5),p=c.clone();return[[o,a,d,c],[p,l,h,i]]}function S(t){var e=[];return e[0]=t[0].dist_sq(t[1]),e[1]=t[2].dist_sq(t[3]),e[2]=.25*t[0].dist_sq(t[2]),e[3]=.25*t[1].dist_sq(t[3]),18*Math.max.apply(null,e)}function _(t,e){this.nodes=t,this.colors=e}function N(t,e){this.nodes=t,this.colors=e}function j(t){this.id=t;var e=this.readMesh(t);this.nodes=e.nodes,this.colors=e.colors}function C(t,e){return t.scale(2/3).add(e.scale(1/3))}function B(t){for(var e=[],s=t.split(/[ ,]+/),n=0;n<s.length-1;n+=2)e.push(new M(parseFloat(s[n]),parseFloat(s[n+1])));return e}}(); +!function(){const t="http://www.w3.org/2000/svg",e="http://www.w3.org/1999/xlink",s="http://www.w3.org/1999/xhtml",r=2;if(document.createElementNS(t,"meshgradient").x)return;const n=(t,e,s,r)=>{let n=new x(.5*(e.x+s.x),.5*(e.y+s.y)),o=new x(.5*(t.x+e.x),.5*(t.y+e.y)),i=new x(.5*(s.x+r.x),.5*(s.y+r.y)),a=new x(.5*(n.x+o.x),.5*(n.y+o.y)),h=new x(.5*(n.x+i.x),.5*(n.y+i.y)),l=new x(.5*(a.x+h.x),.5*(a.y+h.y));return[[t,o,a,l],[l,h,i,r]]},o=t=>{let e=t[0].distSquared(t[1]),s=t[2].distSquared(t[3]),r=.25*t[0].distSquared(t[2]),n=.25*t[1].distSquared(t[3]),o=e>s?e:s,i=r>n?r:n;return 18*(o>i?o:i)},i=(t,e)=>Math.sqrt(t.distSquared(e)),a=(t,e)=>t.scale(2/3).add(e.scale(1/3)),h=t=>{let e,s,r,n,o,i,a,h=new g;return t.match(/(\w+\(\s*[^)]+\))+/g).forEach(t=>{let l=t.match(/[\w.-]+/g),d=l.shift();switch(d){case"translate":2===l.length?e=new g(1,0,0,1,l[0],l[1]):(console.error("mesh.js: translate does not have 2 arguments!"),e=new g(1,0,0,1,0,0)),h=h.append(e);break;case"scale":1===l.length?s=new g(l[0],0,0,l[0],0,0):2===l.length?s=new g(l[0],0,0,l[1],0,0):(console.error("mesh.js: scale does not have 1 or 2 arguments!"),s=new g(1,0,0,1,0,0)),h=h.append(s);break;case"rotate":if(3===l.length&&(e=new g(1,0,0,1,l[1],l[2]),h=h.append(e)),l[0]){r=l[0]*Math.PI/180;let t=Math.cos(r),e=Math.sin(r);Math.abs(t)<1e-16&&(t=0),Math.abs(e)<1e-16&&(e=0),a=new g(t,e,-e,t,0,0),h=h.append(a)}else console.error("math.js: No argument to rotate transform!");3===l.length&&(e=new g(1,0,0,1,-l[1],-l[2]),h=h.append(e));break;case"skewX":l[0]?(r=l[0]*Math.PI/180,n=Math.tan(r),o=new g(1,0,n,1,0,0),h=h.append(o)):console.error("math.js: No argument to skewX transform!");break;case"skewY":l[0]?(r=l[0]*Math.PI/180,n=Math.tan(r),i=new g(1,n,0,1,0,0),h=h.append(i)):console.error("math.js: No argument to skewY transform!");break;case"matrix":6===l.length?h=h.append(new g(...l)):console.error("math.js: Incorrect number of arguments for matrix!");break;default:console.error("mesh.js: Unhandled transform type: "+d)}}),h},l=t=>{let e=[],s=t.split(/[ ,]+/);for(let t=0,r=s.length-1;t<r;t+=2)e.push(new x(parseFloat(s[t]),parseFloat(s[t+1])));return e},d=(t,e)=>{for(let s in e)t.setAttribute(s,e[s])},c=(t,e,s,r,n)=>{let o,i,a=[0,0,0,0];for(let h=0;h<3;++h)e[h]<t[h]&&e[h]<s[h]||t[h]<e[h]&&s[h]<e[h]?a[h]=0:(a[h]=.5*((e[h]-t[h])/r+(s[h]-e[h])/n),o=Math.abs(3*(e[h]-t[h])/r),i=Math.abs(3*(s[h]-e[h])/n),a[h]>o?a[h]=o:a[h]>i&&(a[h]=i));return a},u=[[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],[-3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0],[2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0],[0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0],[-3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0],[0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0],[9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1],[-6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1],[2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0],[0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0],[-6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1],[4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1]],f=t=>{let e=[];for(let s=0;s<16;++s){e[s]=0;for(let r=0;r<16;++r)e[s]+=u[s][r]*t[r]}return e},p=(t,e,s)=>{const r=e*e,n=s*s,o=e*e*e,i=s*s*s;return t[0]+t[1]*e+t[2]*r+t[3]*o+t[4]*s+t[5]*s*e+t[6]*s*r+t[7]*s*o+t[8]*n+t[9]*n*e+t[10]*n*r+t[11]*n*o+t[12]*i+t[13]*i*e+t[14]*i*r+t[15]*i*o},y=t=>{let e=[],s=[],r=[];for(let s=0;s<4;++s)e[s]=[],e[s][0]=n(t[0][s],t[1][s],t[2][s],t[3][s]),e[s][1]=[],e[s][1].push(...n(...e[s][0][0])),e[s][1].push(...n(...e[s][0][1])),e[s][2]=[],e[s][2].push(...n(...e[s][1][0])),e[s][2].push(...n(...e[s][1][1])),e[s][2].push(...n(...e[s][1][2])),e[s][2].push(...n(...e[s][1][3]));for(let t=0;t<8;++t){s[t]=[];for(let r=0;r<4;++r)s[t][r]=[],s[t][r][0]=n(e[0][2][t][r],e[1][2][t][r],e[2][2][t][r],e[3][2][t][r]),s[t][r][1]=[],s[t][r][1].push(...n(...s[t][r][0][0])),s[t][r][1].push(...n(...s[t][r][0][1])),s[t][r][2]=[],s[t][r][2].push(...n(...s[t][r][1][0])),s[t][r][2].push(...n(...s[t][r][1][1])),s[t][r][2].push(...n(...s[t][r][1][2])),s[t][r][2].push(...n(...s[t][r][1][3]))}for(let t=0;t<8;++t){r[t]=[];for(let e=0;e<8;++e)r[t][e]=[],r[t][e][0]=s[t][0][2][e],r[t][e][1]=s[t][1][2][e],r[t][e][2]=s[t][2][2][e],r[t][e][3]=s[t][3][2][e]}return r};class x{constructor(t,e){this.x=t||0,this.y=e||0}toString(){return`(x=${this.x}, y=${this.y})`}clone(){return new x(this.x,this.y)}add(t){return new x(this.x+t.x,this.y+t.y)}scale(t){return void 0===t.x?new x(this.x*t,this.y*t):new x(this.x*t.x,this.y*t.y)}distSquared(t){let e=this.x-t.x,s=this.y-t.y;return e*e+s*s}transform(t){let e=this.x*t.a+this.y*t.c+t.e,s=this.x*t.b+this.y*t.d+t.f;return new x(e,s)}}class g{constructor(t,e,s,r,n,o){void 0===t?(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0):(this.a=t,this.b=e,this.c=s,this.d=r,this.e=n,this.f=o)}toString(){return`affine: ${this.a} ${this.c} ${this.e} \n ${this.b} ${this.d} ${this.f}`}append(t){t instanceof g||console.error("mesh.js: argument to Affine.append is not affine!");let e=this.a*t.a+this.c*t.b,s=this.b*t.a+this.d*t.b,r=this.a*t.c+this.c*t.d,n=this.b*t.c+this.d*t.d,o=this.a*t.e+this.c*t.f+this.e,i=this.b*t.e+this.d*t.f+this.f;return new g(e,s,r,n,o,i)}}class w{constructor(t,e){this.nodes=t,this.colors=e}paintCurve(t,e){if(o(this.nodes)>r){const s=n(...this.nodes);let r=[[],[]],o=[[],[]];for(let t=0;t<4;++t)r[0][t]=this.colors[0][t],r[1][t]=(this.colors[0][t]+this.colors[1][t])/2,o[0][t]=r[1][t],o[1][t]=this.colors[1][t];let i=new w(s[0],r),a=new w(s[1],o);i.paintCurve(t,e),a.paintCurve(t,e)}else{let s=Math.round(this.nodes[0].x);if(s>=0&&s<e){let r=4*(~~this.nodes[0].y*e+s);t[r]=Math.round(this.colors[0][0]),t[r+1]=Math.round(this.colors[0][1]),t[r+2]=Math.round(this.colors[0][2]),t[r+3]=Math.round(this.colors[0][3])}}}}class m{constructor(t,e){this.nodes=t,this.colors=e}split(){let t=[[],[],[],[]],e=[[],[],[],[]],s=[[[],[]],[[],[]]],r=[[[],[]],[[],[]]];for(let s=0;s<4;++s){const r=n(this.nodes[0][s],this.nodes[1][s],this.nodes[2][s],this.nodes[3][s]);t[0][s]=r[0][0],t[1][s]=r[0][1],t[2][s]=r[0][2],t[3][s]=r[0][3],e[0][s]=r[1][0],e[1][s]=r[1][1],e[2][s]=r[1][2],e[3][s]=r[1][3]}for(let t=0;t<4;++t)s[0][0][t]=this.colors[0][0][t],s[0][1][t]=this.colors[0][1][t],s[1][0][t]=(this.colors[0][0][t]+this.colors[1][0][t])/2,s[1][1][t]=(this.colors[0][1][t]+this.colors[1][1][t])/2,r[0][0][t]=s[1][0][t],r[0][1][t]=s[1][1][t],r[1][0][t]=this.colors[1][0][t],r[1][1][t]=this.colors[1][1][t];return[new m(t,s),new m(e,r)]}paint(t,e){let s,n=!1;for(let t=0;t<4;++t)if((s=o([this.nodes[0][t],this.nodes[1][t],this.nodes[2][t],this.nodes[3][t]]))>r){n=!0;break}if(n){let s=this.split();s[0].paint(t,e),s[1].paint(t,e)}else{new w([...this.nodes[0]],[...this.colors[0]]).paintCurve(t,e)}}}class b{constructor(t){this.readMesh(t),this.type=t.getAttribute("type")||"bilinear"}readMesh(t){let e=[[]],s=[[]],r=Number(t.getAttribute("x")),n=Number(t.getAttribute("y"));e[0][0]=new x(r,n);let o=t.children;for(let t=0,r=o.length;t<r;++t){e[3*t+1]=[],e[3*t+2]=[],e[3*t+3]=[],s[t+1]=[];let r=o[t].children;for(let n=0,o=r.length;n<o;++n){let o=r[n].children;for(let r=0,i=o.length;r<i;++r){let i=r;0!==t&&++i;let h,d=o[r].getAttribute("path"),c="l";null!=d&&(c=(h=d.match(/\s*([lLcC])\s*(.*)/))[1]);let u=l(h[2]);switch(c){case"l":0===i?(e[3*t][3*n+3]=u[0].add(e[3*t][3*n]),e[3*t][3*n+1]=a(e[3*t][3*n],e[3*t][3*n+3]),e[3*t][3*n+2]=a(e[3*t][3*n+3],e[3*t][3*n])):1===i?(e[3*t+3][3*n+3]=u[0].add(e[3*t][3*n+3]),e[3*t+1][3*n+3]=a(e[3*t][3*n+3],e[3*t+3][3*n+3]),e[3*t+2][3*n+3]=a(e[3*t+3][3*n+3],e[3*t][3*n+3])):2===i?(0===n&&(e[3*t+3][3*n+0]=u[0].add(e[3*t+3][3*n+3])),e[3*t+3][3*n+1]=a(e[3*t+3][3*n],e[3*t+3][3*n+3]),e[3*t+3][3*n+2]=a(e[3*t+3][3*n+3],e[3*t+3][3*n])):(e[3*t+1][3*n]=a(e[3*t][3*n],e[3*t+3][3*n]),e[3*t+2][3*n]=a(e[3*t+3][3*n],e[3*t][3*n]));break;case"L":0===i?(e[3*t][3*n+3]=u[0],e[3*t][3*n+1]=a(e[3*t][3*n],e[3*t][3*n+3]),e[3*t][3*n+2]=a(e[3*t][3*n+3],e[3*t][3*n])):1===i?(e[3*t+3][3*n+3]=u[0],e[3*t+1][3*n+3]=a(e[3*t][3*n+3],e[3*t+3][3*n+3]),e[3*t+2][3*n+3]=a(e[3*t+3][3*n+3],e[3*t][3*n+3])):2===i?(0===n&&(e[3*t+3][3*n+0]=u[0]),e[3*t+3][3*n+1]=a(e[3*t+3][3*n],e[3*t+3][3*n+3]),e[3*t+3][3*n+2]=a(e[3*t+3][3*n+3],e[3*t+3][3*n])):(e[3*t+1][3*n]=a(e[3*t][3*n],e[3*t+3][3*n]),e[3*t+2][3*n]=a(e[3*t+3][3*n],e[3*t][3*n]));break;case"c":0===i?(e[3*t][3*n+1]=u[0].add(e[3*t][3*n]),e[3*t][3*n+2]=u[1].add(e[3*t][3*n]),e[3*t][3*n+3]=u[2].add(e[3*t][3*n])):1===i?(e[3*t+1][3*n+3]=u[0].add(e[3*t][3*n+3]),e[3*t+2][3*n+3]=u[1].add(e[3*t][3*n+3]),e[3*t+3][3*n+3]=u[2].add(e[3*t][3*n+3])):2===i?(e[3*t+3][3*n+2]=u[0].add(e[3*t+3][3*n+3]),e[3*t+3][3*n+1]=u[1].add(e[3*t+3][3*n+3]),0===n&&(e[3*t+3][3*n+0]=u[2].add(e[3*t+3][3*n+3]))):(e[3*t+2][3*n]=u[0].add(e[3*t+3][3*n]),e[3*t+1][3*n]=u[1].add(e[3*t+3][3*n]));break;case"C":0===i?(e[3*t][3*n+1]=u[0],e[3*t][3*n+2]=u[1],e[3*t][3*n+3]=u[2]):1===i?(e[3*t+1][3*n+3]=u[0],e[3*t+2][3*n+3]=u[1],e[3*t+3][3*n+3]=u[2]):2===i?(e[3*t+3][3*n+2]=u[0],e[3*t+3][3*n+1]=u[1],0===n&&(e[3*t+3][3*n+0]=u[2])):(e[3*t+2][3*n]=u[0],e[3*t+1][3*n]=u[1]);break;default:console.error("mesh.js: "+c+" invalid path type.")}if(0===t&&0===n||r>0){let e=window.getComputedStyle(o[r]).stopColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i),a=window.getComputedStyle(o[r]).stopOpacity,h=255;a&&(h=Math.floor(255*a)),e&&(0===i?(s[t][n]=[],s[t][n][0]=Math.floor(e[1]),s[t][n][1]=Math.floor(e[2]),s[t][n][2]=Math.floor(e[3]),s[t][n][3]=h):1===i?(s[t][n+1]=[],s[t][n+1][0]=Math.floor(e[1]),s[t][n+1][1]=Math.floor(e[2]),s[t][n+1][2]=Math.floor(e[3]),s[t][n+1][3]=h):2===i?(s[t+1][n+1]=[],s[t+1][n+1][0]=Math.floor(e[1]),s[t+1][n+1][1]=Math.floor(e[2]),s[t+1][n+1][2]=Math.floor(e[3]),s[t+1][n+1][3]=h):3===i&&(s[t+1][n]=[],s[t+1][n][0]=Math.floor(e[1]),s[t+1][n][1]=Math.floor(e[2]),s[t+1][n][2]=Math.floor(e[3]),s[t+1][n][3]=h))}}e[3*t+1][3*n+1]=new x,e[3*t+1][3*n+2]=new x,e[3*t+2][3*n+1]=new x,e[3*t+2][3*n+2]=new x,e[3*t+1][3*n+1].x=(-4*e[3*t][3*n].x+6*(e[3*t][3*n+1].x+e[3*t+1][3*n].x)+-2*(e[3*t][3*n+3].x+e[3*t+3][3*n].x)+3*(e[3*t+3][3*n+1].x+e[3*t+1][3*n+3].x)+-1*e[3*t+3][3*n+3].x)/9,e[3*t+1][3*n+2].x=(-4*e[3*t][3*n+3].x+6*(e[3*t][3*n+2].x+e[3*t+1][3*n+3].x)+-2*(e[3*t][3*n].x+e[3*t+3][3*n+3].x)+3*(e[3*t+3][3*n+2].x+e[3*t+1][3*n].x)+-1*e[3*t+3][3*n].x)/9,e[3*t+2][3*n+1].x=(-4*e[3*t+3][3*n].x+6*(e[3*t+3][3*n+1].x+e[3*t+2][3*n].x)+-2*(e[3*t+3][3*n+3].x+e[3*t][3*n].x)+3*(e[3*t][3*n+1].x+e[3*t+2][3*n+3].x)+-1*e[3*t][3*n+3].x)/9,e[3*t+2][3*n+2].x=(-4*e[3*t+3][3*n+3].x+6*(e[3*t+3][3*n+2].x+e[3*t+2][3*n+3].x)+-2*(e[3*t+3][3*n].x+e[3*t][3*n+3].x)+3*(e[3*t][3*n+2].x+e[3*t+2][3*n].x)+-1*e[3*t][3*n].x)/9,e[3*t+1][3*n+1].y=(-4*e[3*t][3*n].y+6*(e[3*t][3*n+1].y+e[3*t+1][3*n].y)+-2*(e[3*t][3*n+3].y+e[3*t+3][3*n].y)+3*(e[3*t+3][3*n+1].y+e[3*t+1][3*n+3].y)+-1*e[3*t+3][3*n+3].y)/9,e[3*t+1][3*n+2].y=(-4*e[3*t][3*n+3].y+6*(e[3*t][3*n+2].y+e[3*t+1][3*n+3].y)+-2*(e[3*t][3*n].y+e[3*t+3][3*n+3].y)+3*(e[3*t+3][3*n+2].y+e[3*t+1][3*n].y)+-1*e[3*t+3][3*n].y)/9,e[3*t+2][3*n+1].y=(-4*e[3*t+3][3*n].y+6*(e[3*t+3][3*n+1].y+e[3*t+2][3*n].y)+-2*(e[3*t+3][3*n+3].y+e[3*t][3*n].y)+3*(e[3*t][3*n+1].y+e[3*t+2][3*n+3].y)+-1*e[3*t][3*n+3].y)/9,e[3*t+2][3*n+2].y=(-4*e[3*t+3][3*n+3].y+6*(e[3*t+3][3*n+2].y+e[3*t+2][3*n+3].y)+-2*(e[3*t+3][3*n].y+e[3*t][3*n+3].y)+3*(e[3*t][3*n+2].y+e[3*t+2][3*n].y)+-1*e[3*t][3*n].y)/9}}this.nodes=e,this.colors=s}paintMesh(t,e){let s=(this.nodes.length-1)/3,r=(this.nodes[0].length-1)/3;if("bilinear"===this.type||s<2||r<2){let n;for(let o=0;o<s;++o)for(let s=0;s<r;++s){let r=[];for(let t=3*o,e=3*o+4;t<e;++t)r.push(this.nodes[t].slice(3*s,3*s+4));let i=[];i.push(this.colors[o].slice(s,s+2)),i.push(this.colors[o+1].slice(s,s+2)),(n=new m(r,i)).paint(t,e)}}else{let n,o,a,h,l,d,u;const x=s,g=r;s++,r++;let w=new Array(s);for(let t=0;t<s;++t){w[t]=new Array(r);for(let e=0;e<r;++e)w[t][e]=[],w[t][e][0]=this.nodes[3*t][3*e],w[t][e][1]=this.colors[t][e]}for(let t=0;t<s;++t)for(let e=0;e<r;++e)0!==t&&t!==x&&(n=i(w[t-1][e][0],w[t][e][0]),o=i(w[t+1][e][0],w[t][e][0]),w[t][e][2]=c(w[t-1][e][1],w[t][e][1],w[t+1][e][1],n,o)),0!==e&&e!==g&&(n=i(w[t][e-1][0],w[t][e][0]),o=i(w[t][e+1][0],w[t][e][0]),w[t][e][3]=c(w[t][e-1][1],w[t][e][1],w[t][e+1][1],n,o));for(let t=0;t<r;++t){w[0][t][2]=[],w[x][t][2]=[];for(let e=0;e<4;++e)n=i(w[1][t][0],w[0][t][0]),o=i(w[x][t][0],w[x-1][t][0]),w[0][t][2][e]=n>0?2*(w[1][t][1][e]-w[0][t][1][e])/n-w[1][t][2][e]:0,w[x][t][2][e]=o>0?2*(w[x][t][1][e]-w[x-1][t][1][e])/o-w[x-1][t][2][e]:0}for(let t=0;t<s;++t){w[t][0][3]=[],w[t][g][3]=[];for(let e=0;e<4;++e)n=i(w[t][1][0],w[t][0][0]),o=i(w[t][g][0],w[t][g-1][0]),w[t][0][3][e]=n>0?2*(w[t][1][1][e]-w[t][0][1][e])/n-w[t][1][3][e]:0,w[t][g][3][e]=o>0?2*(w[t][g][1][e]-w[t][g-1][1][e])/o-w[t][g-1][3][e]:0}for(let s=0;s<x;++s)for(let r=0;r<g;++r){let n=i(w[s][r][0],w[s+1][r][0]),o=i(w[s][r+1][0],w[s+1][r+1][0]),c=i(w[s][r][0],w[s][r+1][0]),x=i(w[s+1][r][0],w[s+1][r+1][0]),g=[[],[],[],[]];for(let t=0;t<4;++t){(d=[])[0]=w[s][r][1][t],d[1]=w[s+1][r][1][t],d[2]=w[s][r+1][1][t],d[3]=w[s+1][r+1][1][t],d[4]=w[s][r][2][t]*n,d[5]=w[s+1][r][2][t]*n,d[6]=w[s][r+1][2][t]*o,d[7]=w[s+1][r+1][2][t]*o,d[8]=w[s][r][3][t]*c,d[9]=w[s+1][r][3][t]*x,d[10]=w[s][r+1][3][t]*c,d[11]=w[s+1][r+1][3][t]*x,d[12]=0,d[13]=0,d[14]=0,d[15]=0,u=f(d);for(let e=0;e<9;++e){g[t][e]=[];for(let s=0;s<9;++s)g[t][e][s]=p(u,e/8,s/8),g[t][e][s]>255?g[t][e][s]=255:g[t][e][s]<0&&(g[t][e][s]=0)}}h=[];for(let t=3*s,e=3*s+4;t<e;++t)h.push(this.nodes[t].slice(3*r,3*r+4));l=y(h);for(let s=0;s<8;++s)for(let r=0;r<8;++r)(a=new m(l[s][r],[[[g[0][s][r],g[1][s][r],g[2][s][r],g[3][s][r]],[g[0][s][r+1],g[1][s][r+1],g[2][s][r+1],g[3][s][r+1]]],[[g[0][s+1][r],g[1][s+1][r],g[2][s+1][r],g[3][s+1][r]],[g[0][s+1][r+1],g[1][s+1][r+1],g[2][s+1][r+1],g[3][s+1][r+1]]]])).paint(t,e)}}}transform(t){if(t instanceof x)for(let e=0,s=this.nodes.length;e<s;++e)for(let s=0,r=this.nodes[0].length;s<r;++s)this.nodes[e][s]=this.nodes[e][s].add(t);else if(t instanceof g)for(let e=0,s=this.nodes.length;e<s;++e)for(let s=0,r=this.nodes[0].length;s<r;++s)this.nodes[e][s]=this.nodes[e][s].transform(t)}scale(t){for(let e=0,s=this.nodes.length;e<s;++e)for(let s=0,r=this.nodes[0].length;s<r;++s)this.nodes[e][s]=this.nodes[e][s].scale(t)}}document.querySelectorAll("rect,circle,ellipse,path,text").forEach((r,n)=>{let o=r.getAttribute("id");o||(o="patchjs_shape"+n,r.setAttribute("id",o));const i=r.style.fill.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/),a=r.style.stroke.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/);if(i&&i[1]){const a=document.getElementById(i[1]);if(a&&"meshgradient"===a.nodeName){const i=r.getBBox();let l=document.createElementNS(s,"canvas");d(l,{width:i.width,height:i.height});const c=l.getContext("2d");let u=c.createImageData(i.width,i.height);const f=new b(a);"objectBoundingBox"===a.getAttribute("gradientUnits")&&f.scale(new x(i.width,i.height));const p=a.getAttribute("gradientTransform");null!=p&&f.transform(h(p)),"userSpaceOnUse"===a.getAttribute("gradientUnits")&&f.transform(new x(-i.x,-i.y)),f.paintMesh(u.data,l.width),c.putImageData(u,0,0);const y=document.createElementNS(t,"image");d(y,{width:i.width,height:i.height,x:i.x,y:i.y});let g=l.toDataURL();y.setAttributeNS(e,"xlink:href",g),r.parentNode.insertBefore(y,r),r.style.fill="none";const w=document.createElementNS(t,"use");w.setAttributeNS(e,"xlink:href","#"+o);const m="patchjs_clip"+n,M=document.createElementNS(t,"clipPath");M.setAttribute("id",m),M.appendChild(w),r.parentElement.insertBefore(M,r),y.setAttribute("clip-path","url(#"+m+")"),u=null,l=null,g=null}}if(a&&a[1]){const o=document.getElementById(a[1]);if(o&&"meshgradient"===o.nodeName){const i=parseFloat(r.style.strokeWidth.slice(0,-2))*(parseFloat(r.style.strokeMiterlimit)||parseFloat(r.getAttribute("stroke-miterlimit"))||1),a=r.getBBox(),l=Math.trunc(a.width+i),c=Math.trunc(a.height+i),u=Math.trunc(a.x-i/2),f=Math.trunc(a.y-i/2);let p=document.createElementNS(s,"canvas");d(p,{width:l,height:c});const y=p.getContext("2d");let g=y.createImageData(l,c);const w=new b(o);"objectBoundingBox"===o.getAttribute("gradientUnits")&&w.scale(new x(l,c));const m=o.getAttribute("gradientTransform");null!=m&&w.transform(h(m)),"userSpaceOnUse"===o.getAttribute("gradientUnits")&&w.transform(new x(-u,-f)),w.paintMesh(g.data,p.width),y.putImageData(g,0,0);const M=document.createElementNS(t,"image");d(M,{width:l,height:c,x:0,y:0});let S=p.toDataURL();M.setAttributeNS(e,"xlink:href",S);const k="pattern_clip"+n,A=document.createElementNS(t,"pattern");d(A,{id:k,patternUnits:"userSpaceOnUse",width:l,height:c,x:u,y:f}),A.appendChild(M),o.parentNode.appendChild(A),r.style.stroke="url(#"+k+")",g=null,p=null,S=null}}})}(); )=====" |
