@emasoft/svg-matrix 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +325 -0
  2. package/bin/svg-matrix.js +994 -378
  3. package/bin/svglinter.cjs +4172 -433
  4. package/bin/svgm.js +744 -184
  5. package/package.json +16 -4
  6. package/src/animation-references.js +71 -52
  7. package/src/arc-length.js +160 -96
  8. package/src/bezier-analysis.js +257 -117
  9. package/src/bezier-intersections.js +411 -148
  10. package/src/browser-verify.js +240 -100
  11. package/src/clip-path-resolver.js +350 -142
  12. package/src/convert-path-data.js +279 -134
  13. package/src/css-specificity.js +78 -70
  14. package/src/flatten-pipeline.js +751 -263
  15. package/src/geometry-to-path.js +511 -182
  16. package/src/index.js +191 -46
  17. package/src/inkscape-support.js +404 -0
  18. package/src/marker-resolver.js +278 -164
  19. package/src/mask-resolver.js +209 -98
  20. package/src/matrix.js +147 -67
  21. package/src/mesh-gradient.js +187 -96
  22. package/src/off-canvas-detection.js +201 -104
  23. package/src/path-analysis.js +187 -107
  24. package/src/path-data-plugins.js +628 -167
  25. package/src/path-simplification.js +0 -1
  26. package/src/pattern-resolver.js +125 -88
  27. package/src/polygon-clip.js +111 -66
  28. package/src/svg-boolean-ops.js +194 -118
  29. package/src/svg-collections.js +48 -19
  30. package/src/svg-flatten.js +282 -164
  31. package/src/svg-parser.js +427 -200
  32. package/src/svg-rendering-context.js +147 -104
  33. package/src/svg-toolbox.js +16411 -3298
  34. package/src/svg2-polyfills.js +114 -245
  35. package/src/transform-decomposition.js +46 -41
  36. package/src/transform-optimization.js +89 -68
  37. package/src/transforms2d.js +49 -16
  38. package/src/transforms3d.js +58 -22
  39. package/src/use-symbol-resolver.js +150 -110
  40. package/src/vector.js +67 -15
  41. package/src/vendor/README.md +110 -0
  42. package/src/vendor/inkscape-hatch-polyfill.js +401 -0
  43. package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
  44. package/src/vendor/inkscape-mesh-polyfill.js +843 -0
  45. package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
  46. package/src/verification.js +288 -124
@@ -0,0 +1,843 @@
1
+
2
+ // Use Canvas to render a mesh gradient, passing the rendering to an image via a data stream.
3
+ // Copyright: Tavmjong Bah 2017
4
+ // Distributed under GNU General Public License version 3 or later. See <http://fsf.org/>.
5
+
6
+ (function() {
7
+ var counter = 0; // Temp, number of calls to Canvas
8
+
9
+ // Name spaces -----------------------------------
10
+ var svgNS = "http://www.w3.org/2000/svg",
11
+ xlinkNS = "http://www.w3.org/1999/xlink",
12
+ xhtmlNS = "http://www.w3.org/1999/xhtml";
13
+
14
+ // Test if mesh gradients are supported.
15
+ var m = document.createElementNS( svgNS, "meshgradient" );
16
+
17
+ if (m.x) {
18
+ return;
19
+ }
20
+
21
+ // Point class -----------------------------------
22
+ function Point(x, y) {
23
+ this.x = x || 0;
24
+ this.y = y || 0;
25
+ }
26
+
27
+ Point.prototype.x = null;
28
+ Point.prototype.y = null;
29
+
30
+ Point.prototype.get_x = function() {
31
+ return this.x;
32
+ };
33
+
34
+ Point.prototype.get_y = function() {
35
+ return this.y;
36
+ };
37
+
38
+ Point.prototype.clone = function() {
39
+ return new Point(this.x, this.y);
40
+ };
41
+
42
+ Point.prototype.add = function(v) {
43
+ return new Point(this.x + v.x, this.y + v.y);
44
+ };
45
+
46
+ Point.prototype.scale = function(v) {
47
+ if(v instanceof Point) {
48
+ return new Point(this.x * v.x, this.y * v.y);
49
+ }
50
+ return new Point(this.x * v, this.y * v);
51
+ };
52
+
53
+ // Transform by affine
54
+ Point.prototype.transform = function(a) {
55
+ var x = this.x * a.a + this.y * a.c + a.e,
56
+ y = this.x * a.b + this.y * a.d + a.f;
57
+ return new Point(x, y);
58
+ };
59
+
60
+ Point.prototype.dist_sq = function(v) {
61
+ var x = this.x - v.x,
62
+ y = this.y - v.y;
63
+ return (x*x + y*y);
64
+ };
65
+
66
+ Point.prototype.toString = function() {
67
+ return "(x=" + this.x + ", y=" + this.y + ")";
68
+ };
69
+
70
+ // Affine class -----------------------------------
71
+
72
+ // As defined in the SVG spec
73
+ // | a c e |
74
+ // | b d f |
75
+ // | 0 0 1 |
76
+ function Affine(a, b, c, d, e, f) {
77
+ if (a === undefined) {
78
+ this.a = 1;
79
+ this.b = 0;
80
+ this.c = 0;
81
+ this.d = 1;
82
+ this.e = 0;
83
+ this.f = 0;
84
+ } else {
85
+ this.a = a;
86
+ this.b = b;
87
+ this.c = c;
88
+ this.d = d;
89
+ this.e = e;
90
+ this.f = f;
91
+ }
92
+ }
93
+
94
+ Affine.prototype.a = null;
95
+ Affine.prototype.b = null;
96
+ Affine.prototype.c = null;
97
+ Affine.prototype.d = null;
98
+ Affine.prototype.e = null;
99
+ Affine.prototype.f = null;
100
+
101
+ Affine.prototype.append = function(v) {
102
+ if (!(v instanceof Affine)) {
103
+ console.log ( "mesh.js: argument to Affine.append is not affine!");
104
+ }
105
+
106
+ var a = this.a * v.a + this.c * v.b,
107
+ b = this.b * v.a + this.d * v.b,
108
+ c = this.a * v.c + this.c * v.d,
109
+ d = this.b * v.c + this.d * v.d,
110
+ e = this.a * v.e + this.c * v.f + this.e,
111
+ f = this.b * v.e + this.d * v.f + this.f;
112
+
113
+ return new Affine(a, b, c, d, e, f);
114
+ };
115
+
116
+ Affine.prototype.toString = function() {
117
+ return ("affine: " + this.a + " " + this.c + " " + this.e +
118
+ "\n " + this.b + " " + this.d + " " + this.f);
119
+ };
120
+
121
+ // Utility functions ---------------------------------
122
+
123
+ // Browsers return a string rather than a transform list for gradientTransform!
124
+ function parseTransform(t) {
125
+ var affine = new Affine(),
126
+ radian,
127
+ tan,
128
+ cos;
129
+
130
+ for (var i in t = t.match(/(\w+\(\s*(\-?\d+\.?\d*e?\-?\d*\s*,?\s*)+\))+/g)) {
131
+ var c = t[i].match(/[\w\.\-]+/g),
132
+ type = c.shift();
133
+
134
+ switch (type) {
135
+ case "translate":
136
+ var trans;
137
+ if (c.length == 2) {
138
+ trans = new Affine( 1, 0, 0, 1, c[0], c[1] );
139
+ } else {
140
+ console.log( "mesh.js: translate does not have 2 arguments!" );
141
+ trans = new Affine( 1, 0, 0, 1, 0, 0 );
142
+ }
143
+ affine = affine.append( trans );
144
+ break;
145
+
146
+ case "scale":
147
+ var scale;
148
+
149
+ if (c.length == 1) {
150
+ scale = new Affine( c[0], 0, 0, c[0], 0, 0 );
151
+ } else if (c.length == 2) {
152
+ scale = new Affine( c[0], 0, 0, c[1], 0, 0 );
153
+ } else {
154
+ console.log( "mesh.js: scale does not have 1 or 2 arguments!" );
155
+ scale = new Affine( 1, 0, 0, 1, 0, 0 );
156
+ }
157
+
158
+ affine = affine.append(scale);
159
+
160
+ break;
161
+
162
+ case "rotate":
163
+ if (c.length == 3 ) {
164
+ trans = new Affine( 1, 0, 0, 1, c[1], c[2]);
165
+ affine = affine.append(trans);
166
+ }
167
+
168
+ if (c[0]) {
169
+ radian = c[0] * Math.PI/180.0;
170
+ cos = Math.cos(radian);
171
+ sin = Math.sin(radian);
172
+
173
+ if (Math.abs(cos) < 1e-16) { // I hate rounding errors...
174
+ cos = 0;
175
+ }
176
+ if (Math.abs(sin) < 1e-16) { // I hate rounding errors...
177
+ sin = 0;
178
+ }
179
+ var rotate = new Affine(cos, sin, -sin, cos, 0, 0);
180
+ affine = affine.append(rotate);
181
+ } else {
182
+ console.log( "math.js: No argument to rotate transform!" );
183
+ }
184
+
185
+ if (c.length == 3) {
186
+ trans = new Affine(1, 0, 0, 1, -c[1], -c[2]);
187
+ affine = affine.append(trans);
188
+ }
189
+
190
+ break;
191
+
192
+ case "skewX":
193
+ if (c[0]) {
194
+ radian = c[0] * Math.PI/180.0;
195
+ tan = Math.tan(radian);
196
+ skewx = new Affine( 1, 0, tan, 1, 0, 0 );
197
+ affine = affine.append(skewx);
198
+ } else {
199
+ console.log("math.js: No argument to skewX transform!");
200
+ }
201
+
202
+ break;
203
+
204
+ case "skewY":
205
+ if (c[0]) {
206
+ radian = c[0] * Math.PI/180.0;
207
+ tan = Math.tan(radian);
208
+ skewy = new Affine( 1, tan, 0, 1, 0, 0 );
209
+ affine = affine.append(skewy);
210
+ } else {
211
+ console.log("math.js: No argument to skewY transform!");
212
+ }
213
+
214
+ break;
215
+
216
+ case "matrix":
217
+ if (c.length == 6) {
218
+ var matrix = new Affine( c[0], c[1], c[2], c[3], c[4], c[5] );
219
+ affine = affine.append(matrix);
220
+ } else {
221
+ console.log("math.js: Incorrect number of arguments for matrix!");
222
+ }
223
+
224
+ break;
225
+
226
+ default:
227
+ console.log("mesh.js: Unhandled transform type: " + type);
228
+
229
+ break;
230
+ }
231
+ }
232
+
233
+ return affine;
234
+ }
235
+
236
+ function colorToString(c) {
237
+ return ("rgb(" + Math.round(c[0]) + "," + Math.round(c[1]) + "," +Math.round(c[2]) + ")");
238
+ }
239
+
240
+ // Split Bezier using de Casteljau's method... faster version
241
+ function split_bezier(p0, p1, p2, p3) {
242
+ var p00 = p0.clone(),
243
+ p13 = p3.clone(),
244
+
245
+ tmp = new Point((p1.x + p2.x)*0.5, (p1.y + p2.y)*0.5),
246
+ p01 = new Point((p0.x + p1.x)*0.5, (p0.y + p1.y)*0.5),
247
+ p12 = new Point((p2.x + p3.x)*0.5, (p2.y + p3.y)*0.5),
248
+
249
+ p02 = new Point((tmp.x + p01.x)*0.5, (tmp.y + p01.y)*0.5),
250
+ p11 = new Point((tmp.x + p12.x)*0.5, (tmp.y + p12.y)*0.5),
251
+
252
+ p03 = new Point((p02.x + p11.x)*0.5, (p02.y + p11.y)*0.5),
253
+ p10 = p03.clone();
254
+
255
+ return ([[p00, p01, p02, p03],
256
+ [p10, p11, p12, p13]]);
257
+ }
258
+
259
+ // See Cairo: cairo-mesh-pattern-rasterizer.c
260
+ function bezier_steps_sq(points) {
261
+ var tmp0 = points[0].dist_sq(points[1]),
262
+ tmp1 = points[2].dist_sq(points[3]),
263
+ tmp2 = points[0].dist_sq(points[2]) * 0.25,
264
+ tmp3 = points[1].dist_sq(points[3]) * 0.25,
265
+ max1 = (tmp0>tmp1?tmp0:tmp1),
266
+ max2 = (tmp2>tmp3?tmp2:tmp3),
267
+ max = (max1>max2?max1:max2);
268
+
269
+ return max*18;
270
+ }
271
+
272
+ // Curve class --------------------------------------
273
+ function Curve(nodes, colors) {
274
+ this.nodes = nodes; // 4 Bezier points
275
+ this.colors = colors; // 2 x 4 colors (two ends x R+G+B+A)
276
+ }
277
+
278
+ // Paint a Bezier curve. w is width of Canvas window.
279
+ Curve.prototype.paint_curve = function(v, w) {
280
+ // If inside, see if we need to split
281
+ var max = bezier_steps_sq(this.nodes);
282
+
283
+ if (max > 2.0) { // Larger values leave holes, smaller take longer to render.
284
+ var beziers = split_bezier(this.nodes[0],this.nodes[1],this.nodes[2],this.nodes[3]),
285
+ colors0 = [[],[]], // ([start][end])
286
+ colors1 = [[],[]];
287
+ for (var i = 0; i < 4; ++ i) {
288
+ colors0[0][i] = this.colors[0][i];
289
+ colors0[1][i] = (this.colors[0][i] + this.colors[1][i])/2;
290
+ colors1[0][i] = (this.colors[0][i] + this.colors[1][i])/2;
291
+ colors1[1][i] = this.colors[1][i];
292
+ }
293
+ var curve0 = new Curve(beziers[0], colors0),
294
+ curve1 = new Curve(beziers[1], colors1);
295
+ curve0.paint_curve(v, w);
296
+ curve1.paint_curve(v, w);
297
+ } else {
298
+ counter++;
299
+
300
+ // Directly write data
301
+ var x = Math.round(this.nodes[0].x),
302
+ y = Math.round(this.nodes[0].y);
303
+ if (x >= 0 && x < w) {
304
+ var index = (y * w + x) * 4;
305
+ v[index ] = Math.round(this.colors[0][0]);
306
+ v[index + 1] = Math.round(this.colors[0][1]);
307
+ v[index + 2] = Math.round(this.colors[0][2]);
308
+ v[index + 3] = Math.round(this.colors[0][3]); // Alpha
309
+ }
310
+ }
311
+ };
312
+
313
+ // Patch class -------------------------------------
314
+ function Patch(nodes, colors) {
315
+ this.nodes = nodes; // 4x4 array of points
316
+ this.colors = colors; // 2x2x4 colors (four corners x R+G+B+A)
317
+ }
318
+
319
+ // Set path for future stroking or filling... useful for debugging.
320
+ Patch.prototype.setOutline = function(v) {
321
+ // Draw patch outline
322
+ v.beginPath();
323
+ v.moveTo( this.nodes[0][0].x, this.nodes[0][0].y );
324
+ v.bezierCurveTo( this.nodes[0][1].x, this.nodes[0][1].y,
325
+ this.nodes[0][2].x, this.nodes[0][2].y,
326
+ this.nodes[0][3].x, this.nodes[0][3].y );
327
+ v.bezierCurveTo( this.nodes[1][3].x, this.nodes[1][3].y,
328
+ this.nodes[2][3].x, this.nodes[2][3].y,
329
+ this.nodes[3][3].x, this.nodes[3][3].y );
330
+ v.bezierCurveTo( this.nodes[3][2].x, this.nodes[3][2].y,
331
+ this.nodes[3][1].x, this.nodes[3][1].y,
332
+ this.nodes[3][0].x, this.nodes[3][0].y );
333
+ v.bezierCurveTo( this.nodes[2][0].x, this.nodes[2][0].y,
334
+ this.nodes[1][0].x, this.nodes[1][0].y,
335
+ this.nodes[0][0].x, this.nodes[0][0].y );
336
+ v.closePath();
337
+ };
338
+
339
+ // Draw stroke patch... useful if debugging.
340
+ Patch.prototype.drawOutline = function(v) {
341
+ this.setOutline(v);
342
+ v.strokeStyle = "black";
343
+ v.stroke();
344
+ };
345
+
346
+ // Fill patch... useful if debugging.
347
+ Patch.prototype.fillOutline = function(v) {
348
+ this.setOutline(v);
349
+ v.fillStyle = colorToString( this.colors[0] );
350
+ v.fill();
351
+ };
352
+
353
+ // Split patch horizontally into two patches.
354
+ Patch.prototype.split = function() {
355
+ var nodes0 = [[],[],[],[]],
356
+ nodes1 = [[],[],[],[]],
357
+ colors0 = [[[],[]],[[],[]]],
358
+ colors1 = [[[],[]],[[],[]]];
359
+
360
+ for (var i = 0; i < 4; ++i) {
361
+ var beziers = split_bezier(this.nodes[0][i], this.nodes[1][i], this.nodes[2][i], this.nodes[3][i]);
362
+
363
+ for (var j = 0; j < 4; ++j) {
364
+ nodes0[0][i] = beziers[0][0];
365
+ nodes0[1][i] = beziers[0][1];
366
+ nodes0[2][i] = beziers[0][2];
367
+ nodes0[3][i] = beziers[0][3];
368
+ nodes1[0][i] = beziers[1][0];
369
+ nodes1[1][i] = beziers[1][1];
370
+ nodes1[2][i] = beziers[1][2];
371
+ nodes1[3][i] = beziers[1][3];
372
+ }
373
+ }
374
+
375
+ for (i = 0; i < 4; ++ i) {
376
+ colors0[0][0][i] = this.colors[0][0][i];
377
+ colors0[0][1][i] = this.colors[0][1][i];
378
+ colors0[1][0][i] = (this.colors[0][0][i] + this.colors[1][0][i])/2;
379
+ colors0[1][1][i] = (this.colors[0][1][i] + this.colors[1][1][i])/2;
380
+ colors1[0][0][i] = (this.colors[0][0][i] + this.colors[1][0][i])/2;
381
+ colors1[0][1][i] = (this.colors[0][1][i] + this.colors[1][1][i])/2;
382
+ colors1[1][0][i] = this.colors[1][0][i];
383
+ colors1[1][1][i] = this.colors[1][1][i];
384
+ }
385
+
386
+ var patch0 = new Patch(nodes0, colors0),
387
+ patch1 = new Patch(nodes1, colors1);
388
+
389
+ return ([patch0, patch1]);
390
+ };
391
+
392
+ Patch.prototype.paint = function(v, w) {
393
+ // Check if patch is inside canvas (need canvas dimensions)
394
+ // To be done.....
395
+
396
+ // If inside, see if we need to split
397
+ var tmp = [];
398
+
399
+ for (var i = 0; i < 4; ++i) {
400
+ tmp[i] = bezier_steps_sq([this.nodes[0][i],this.nodes[1][i],
401
+ this.nodes[2][i],this.nodes[3][i]]);
402
+ }
403
+
404
+ var max = Math.max.apply(null,tmp);
405
+
406
+ if (max > 2.0) { // Larger values leave holes, smaller take longer to render.
407
+ var patches = this.split();
408
+ patches[0].paint(v, w);
409
+ patches[1].paint(v, w);
410
+ } else {
411
+ //this.fillOutline(v);
412
+ this.paint_curve(v, w);
413
+ }
414
+ };
415
+
416
+ Patch.prototype.paint_curve = function(v, w) {
417
+ // Paint a Bezier curve using just the top of the patch. If
418
+ // the patch is thin enough this should work. We leave this
419
+ // function here in case we want to do something more fancy.
420
+ var curve = new Curve(
421
+ [this.nodes[0][0],this.nodes[0][1],this.nodes[0][2],this.nodes[0][3]],
422
+ [this.colors[0][0],this.colors[0][1]]);
423
+
424
+ curve.paint_curve(v, w);
425
+ };
426
+
427
+
428
+ // Mesh class ---------------------------------------
429
+ function Mesh(id) {
430
+ this.id = id;
431
+ var raw = this.readMesh(id);
432
+ this.nodes = raw.nodes; // (m*3+1) x (n*3+1) points
433
+ this.colors = raw.colors; // (m+1) x (n+1) x 4 colors (R+G+B+A)
434
+ }
435
+
436
+ // Weighted average to find Bezier points for linear sides.
437
+ function w_ave(p0, p1) {
438
+ var p = p0.scale(2.0/3.0).add(p1.scale(1.0/3.0));
439
+
440
+ return p;
441
+ }
442
+
443
+ // Function to parse an SVG mesh and return an array of nodes (points) and an array of colors.
444
+ Mesh.prototype.readMesh = function(id) {
445
+ var nodes = [],
446
+ colors = [],
447
+ i,
448
+ j;
449
+
450
+ // First, find the mesh
451
+ var theMesh = document.getElementById(id);
452
+
453
+ if (theMesh === null) {
454
+ console.log( "mesh.js: Could not find mesh: " + id);
455
+ } else {
456
+ nodes[0] = []; // Top row
457
+ colors[0] = []; // Top row
458
+
459
+ var x = Number(theMesh.getAttribute("x")),
460
+ y = Number(theMesh.getAttribute("y"));
461
+
462
+ nodes[0][0] = new Point(x, y);
463
+
464
+ var rows = theMesh.children;
465
+
466
+ for (i = 0; i < rows.length; ++i) {
467
+ // Need to validate if meshrow...
468
+ nodes[3*i+1] = []; // Need three extra rows for each meshrow.
469
+ nodes[3*i+2] = [];
470
+ nodes[3*i+3] = [];
471
+ colors[i+1] = []; // Need one more row than number of meshrows.
472
+
473
+ var patches = rows[i].children;
474
+
475
+ for (j = 0; j < patches.length; ++j) {
476
+ var stops = patches[j].children;
477
+
478
+ for (var k = 0; k < stops.length; ++k) {
479
+ var l = k,
480
+ parts;
481
+
482
+ if (i !== 0) {
483
+ ++l; // There is no top if row isn't first row.
484
+ }
485
+
486
+ var path = stops[k].getAttribute("path"),
487
+ type = "l"; // We need to still find mid-points even if no path.
488
+
489
+ if (path !== null) {
490
+ parts = path.match(/\s*([lLcC])\s*(.*)/);
491
+ type = parts[1];
492
+ }
493
+ var stop_nodes = parse_points( parts[2] );
494
+
495
+ switch (type) {
496
+ case "l":
497
+ if (l === 0) { // Top
498
+ nodes[3*i][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j]);
499
+ nodes[3*i][3*j+1] = w_ave( nodes[3*i][3*j], nodes[3*i][3*j+3] );
500
+ nodes[3*i][3*j+2] = w_ave( nodes[3*i][3*j+3], nodes[3*i][3*j] );
501
+ } else if (l == 1) { // Right
502
+ nodes[3*i+3][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j+3]);
503
+ nodes[3*i+1][3*j+3] = w_ave( nodes[3*i][3*j+3], nodes[3*i+3][3*j+3] );
504
+ nodes[3*i+2][3*j+3] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i][3*j+3] );
505
+ } else if (l == 2) { // Bottom
506
+ if(j===0) {
507
+ nodes[3*i+3][3*j+0] = stop_nodes[0].add(nodes[3*i+3][3*j+3]);
508
+ }
509
+ nodes[3*i+3][3*j+1] = w_ave( nodes[3*i+3][3*j], nodes[3*i+3][3*j+3] );
510
+ nodes[3*i+3][3*j+2] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i+3][3*j] );
511
+ } else { // Left
512
+ nodes[3*i+1][3*j] = w_ave( nodes[3*i][3*j], nodes[3*i+3][3*j] );
513
+ nodes[3*i+2][3*j] = w_ave( nodes[3*i+3][3*j], nodes[3*i][3*j] );
514
+ }
515
+
516
+ break;
517
+
518
+ case "L":
519
+ if (l === 0) { // Top
520
+ nodes[3*i][3*j+3] = stop_nodes[0];
521
+ nodes[3*i][3*j+1] = w_ave( nodes[3*i][3*j], nodes[3*i][3*j+3] );
522
+ nodes[3*i][3*j+2] = w_ave( nodes[3*i][3*j+3], nodes[3*i][3*j] );
523
+ } else if (l === 1) { // Right
524
+ nodes[3*i+3][3*j+3] = stop_nodes[0];
525
+ nodes[3*i+1][3*j+3] = w_ave( nodes[3*i][3*j+3], nodes[3*i+3][3*j+3] );
526
+ nodes[3*i+2][3*j+3] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i][3*j+3] );
527
+ } else if (l === 2) { // Bottom
528
+ if(j === 0) {
529
+ nodes[3*i+3][3*j+0] = stop_nodes[0];
530
+ }
531
+ nodes[3*i+3][3*j+1] = w_ave( nodes[3*i+3][3*j], nodes[3*i+3][3*j+3] );
532
+ nodes[3*i+3][3*j+2] = w_ave( nodes[3*i+3][3*j+3], nodes[3*i+3][3*j] );
533
+ } else { // Left
534
+ nodes[3*i+1][3*j] = w_ave( nodes[3*i][3*j], nodes[3*i+3][3*j] );
535
+ nodes[3*i+2][3*j] = w_ave( nodes[3*i+3][3*j], nodes[3*i][3*j] );
536
+ }
537
+
538
+ break;
539
+
540
+ case "c":
541
+ if (l === 0) { // Top
542
+ nodes[3*i][3*j+1] = stop_nodes[0].add(nodes[3*i][3*j]);
543
+ nodes[3*i][3*j+2] = stop_nodes[1].add(nodes[3*i][3*j]);
544
+ nodes[3*i][3*j+3] = stop_nodes[2].add(nodes[3*i][3*j]);
545
+ } else if (l === 1) { // Right
546
+ nodes[3*i+1][3*j+3] = stop_nodes[0].add(nodes[3*i][3*j+3]);
547
+ nodes[3*i+2][3*j+3] = stop_nodes[1].add(nodes[3*i][3*j+3]);
548
+ nodes[3*i+3][3*j+3] = stop_nodes[2].add(nodes[3*i][3*j+3]);
549
+ } else if (l === 2) { // Bottom
550
+ nodes[3*i+3][3*j+2] = stop_nodes[0].add(nodes[3*i+3][3*j+3]);
551
+ nodes[3*i+3][3*j+1] = stop_nodes[1].add(nodes[3*i+3][3*j+3]);
552
+ if(j === 0) {
553
+ nodes[3*i+3][3*j+0] = stop_nodes[2].add(nodes[3*i+3][3*j+3]);
554
+ }
555
+ } else { // Left
556
+ nodes[3*i+2][3*j] = stop_nodes[0].add(nodes[3*i+3][3*j]);
557
+ nodes[3*i+1][3*j] = stop_nodes[1].add(nodes[3*i+3][3*j]);
558
+ }
559
+
560
+ break;
561
+
562
+ case "C":
563
+ if (l === 0) { // Top
564
+ nodes[3*i][3*j+1] = stop_nodes[0];
565
+ nodes[3*i][3*j+2] = stop_nodes[1];
566
+ nodes[3*i][3*j+3] = stop_nodes[2];
567
+ } else if (l == 1) { // Right
568
+ nodes[3*i+1][3*j+3] = stop_nodes[0];
569
+ nodes[3*i+2][3*j+3] = stop_nodes[1];
570
+ nodes[3*i+3][3*j+3] = stop_nodes[2];
571
+ } else if (l == 2) { // Bottom
572
+ nodes[3*i+3][3*j+2] = stop_nodes[0];
573
+ nodes[3*i+3][3*j+1] = stop_nodes[1];
574
+ if(j === 0) {
575
+ nodes[3*i+3][3*j+0] = stop_nodes[2];
576
+ }
577
+ } else { // Left
578
+ nodes[3*i+2][3*j] = stop_nodes[0];
579
+ nodes[3*i+1][3*j] = stop_nodes[1];
580
+ }
581
+
582
+ break;
583
+
584
+ default:
585
+ console.log("mesh.js: " + type + " invalid path type.");
586
+ }
587
+
588
+ if ((i === 0 && j === 0) || k > 0) {
589
+ var color_raw = getComputedStyle(stops[k]).stopColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i),
590
+ alpha_raw = getComputedStyle(stops[k]).stopOpacity,
591
+ alpha = 255;
592
+
593
+ if (alpha_raw) {
594
+ alpha = parseInt(alpha_raw * 255);
595
+ }
596
+
597
+ if (color_raw) {
598
+ if (l === 0) { // upper left corner
599
+ colors[i][j] = [];
600
+ colors[i][j][0] = parseInt(color_raw[1]);
601
+ colors[i][j][1] = parseInt(color_raw[2]);
602
+ colors[i][j][2] = parseInt(color_raw[3]);
603
+ colors[i][j][3] = alpha; // Alpha
604
+ } else if (l === 1) { // upper right corner
605
+ colors[i][j+1] = [];
606
+ colors[i][j+1][0] = parseInt(color_raw[1]);
607
+ colors[i][j+1][1] = parseInt(color_raw[2]);
608
+ colors[i][j+1][2] = parseInt(color_raw[3]);
609
+ colors[i][j+1][3] = alpha; // Alpha
610
+ } else if (l === 2) { // lower right corner
611
+ colors[i+1][j+1] = [];
612
+ colors[i+1][j+1][0] = parseInt(color_raw[1]);
613
+ colors[i+1][j+1][1] = parseInt(color_raw[2]);
614
+ colors[i+1][j+1][2] = parseInt(color_raw[3]);
615
+ colors[i+1][j+1][3] = alpha; // Alpha
616
+ } else if (l === 3) { // lower left corner
617
+ colors[i+1][j] = [];
618
+ colors[i+1][j][0] = parseInt(color_raw[1]);
619
+ colors[i+1][j][1] = parseInt(color_raw[2]);
620
+ colors[i+1][j][2] = parseInt(color_raw[3]);
621
+ colors[i+1][j][3] = alpha; // Alpha
622
+ }
623
+ }
624
+ }
625
+ }
626
+
627
+ // SVG doesn't use tensor points but we need them for rendering.
628
+ nodes[3*i+1][3*j+1] = new Point();
629
+ nodes[3*i+1][3*j+2] = new Point();
630
+ nodes[3*i+2][3*j+1] = new Point();
631
+ nodes[3*i+2][3*j+2] = new Point();
632
+
633
+ nodes[3*i+1][3*j+1].x =
634
+ ( -4.0 * nodes[3*i ][3*j ].x +
635
+ 6.0 * (nodes[3*i ][3*j+1].x + nodes[3*i+1][3*j ].x) +
636
+ -2.0 * (nodes[3*i ][3*j+3].x + nodes[3*i+3][3*j ].x) +
637
+ 3.0 * (nodes[3*i+3][3*j+1].x + nodes[3*i+1][3*j+3].x) +
638
+ -1.0 * nodes[3*i+3][3*j+3].x ) / 9.0;
639
+ nodes[3*i+1][3*j+2].x =
640
+ ( -4.0 * nodes[3*i ][3*j+3].x +
641
+ 6.0 * (nodes[3*i ][3*j+2].x + nodes[3*i+1][3*j+3].x) +
642
+ -2.0 * (nodes[3*i ][3*j ].x + nodes[3*i+3][3*j+3].x) +
643
+ 3.0 * (nodes[3*i+3][3*j+2].x + nodes[3*i+1][3*j ].x) +
644
+ -1.0 * nodes[3*i+3][3*j ].x ) / 9.0;
645
+ nodes[3*i+2][3*j+1].x =
646
+ ( -4.0 * nodes[3*i+3][3*j ].x +
647
+ 6.0 * (nodes[3*i+3][3*j+1].x + nodes[3*i+2][3*j ].x) +
648
+ -2.0 * (nodes[3*i+3][3*j+3].x + nodes[3*i ][3*j ].x) +
649
+ 3.0 * (nodes[3*i ][3*j+1].x + nodes[3*i+2][3*j+3].x) +
650
+ -1.0 * nodes[3*i ][3*j+3].x ) / 9.0;
651
+ nodes[3*i+2][3*j+2].x =
652
+ ( -4.0 * nodes[3*i+3][3*j+3].x +
653
+ 6.0 * (nodes[3*i+3][3*j+2].x + nodes[3*i+2][3*j+3].x) +
654
+ -2.0 * (nodes[3*i+3][3*j ].x + nodes[3*i ][3*j+3].x) +
655
+ 3.0 * (nodes[3*i ][3*j+2].x + nodes[3*i+2][3*j ].x) +
656
+ -1.0 * nodes[3*i ][3*j ].x ) / 9.0;
657
+
658
+ nodes[3*i+1][3*j+1].y =
659
+ ( -4.0 * nodes[3*i ][3*j ].y +
660
+ 6.0 * (nodes[3*i ][3*j+1].y + nodes[3*i+1][3*j ].y) +
661
+ -2.0 * (nodes[3*i ][3*j+3].y + nodes[3*i+3][3*j ].y) +
662
+ 3.0 * (nodes[3*i+3][3*j+1].y + nodes[3*i+1][3*j+3].y) +
663
+ -1.0 * nodes[3*i+3][3*j+3].y ) / 9.0;
664
+ nodes[3*i+1][3*j+2].y =
665
+ ( -4.0 * nodes[3*i ][3*j+3].y +
666
+ 6.0 * (nodes[3*i ][3*j+2].y + nodes[3*i+1][3*j+3].y) +
667
+ -2.0 * (nodes[3*i ][3*j ].y + nodes[3*i+3][3*j+3].y) +
668
+ 3.0 * (nodes[3*i+3][3*j+2].y + nodes[3*i+1][3*j ].y) +
669
+ -1.0 * nodes[3*i+3][3*j ].y ) / 9.0;
670
+ nodes[3*i+2][3*j+1].y =
671
+ ( -4.0 * nodes[3*i+3][3*j ].y +
672
+ 6.0 * (nodes[3*i+3][3*j+1].y + nodes[3*i+2][3*j ].y) +
673
+ -2.0 * (nodes[3*i+3][3*j+3].y + nodes[3*i ][3*j ].y) +
674
+ 3.0 * (nodes[3*i ][3*j+1].y + nodes[3*i+2][3*j+3].y) +
675
+ -1.0 * nodes[3*i ][3*j+3].y ) / 9.0;
676
+ nodes[3*i+2][3*j+2].y =
677
+ ( -4.0 * nodes[3*i+3][3*j+3].y +
678
+ 6.0 * (nodes[3*i+3][3*j+2].y + nodes[3*i+2][3*j+3].y) +
679
+ -2.0 * (nodes[3*i+3][3*j ].y + nodes[3*i ][3*j+3].y) +
680
+ 3.0 * (nodes[3*i ][3*j+2].y + nodes[3*i+2][3*j ].y) +
681
+ -1.0 * nodes[3*i ][3*j ].y ) / 9.0;
682
+
683
+ }
684
+ }
685
+ }
686
+ return {
687
+ nodes: nodes,
688
+ colors: colors
689
+ };
690
+ };
691
+
692
+ // Extracts out each patch and then paints it
693
+ Mesh.prototype.paint = function(v, w) {
694
+ for (var i = 0; i < (this.nodes.length-1)/3; ++i) {
695
+ for (var j = 0; j < (this.nodes[0].length-1)/3; ++j) {
696
+
697
+ var slice_nodes = [];
698
+ for (var k = i*3; k < (i*3)+4; ++k) {
699
+ slice_nodes.push(this.nodes[k].slice(j*3,(j*3)+4));
700
+ }
701
+
702
+ var slice_colors = [];
703
+ slice_colors.push(this.colors[i ].slice(j,j+2));
704
+ slice_colors.push(this.colors[i+1].slice(j,j+2));
705
+
706
+ var patch = new Patch(slice_nodes, slice_colors);
707
+ patch.paint(v, w);
708
+ }
709
+ }
710
+ };
711
+
712
+ // Transforms mesh into coordinate space of canvas (t is either Point or Affine).
713
+ Mesh.prototype.transform = function(t) {
714
+ var i,
715
+ j;
716
+ if (t instanceof Point) {
717
+ for (i = 0; i < this.nodes.length; ++i) {
718
+ for (j = 0; j < this.nodes[0].length; ++j) {
719
+ this.nodes[i][j] = this.nodes[i][j].add(t);
720
+ }
721
+ }
722
+ }
723
+ if (t instanceof Affine) {
724
+ for (i = 0; i < this.nodes.length; ++i) {
725
+ for (j = 0; j < this.nodes[0].length; ++j) {
726
+ this.nodes[i][j] = this.nodes[i][j].transform(t);
727
+ }
728
+ }
729
+ }
730
+ };
731
+
732
+ // Scale mesh into coordinate space of canvas (t is a Point).
733
+ Mesh.prototype.scale = function(t) {
734
+ var i,
735
+ j;
736
+ for (i = 0; i < this.nodes.length; ++i) {
737
+ for (j = 0; j < this.nodes[0].length; ++j) {
738
+ this.nodes[i][j] = this.nodes[i][j].scale(t);
739
+ }
740
+ }
741
+ };
742
+
743
+ function parse_points(s) {
744
+ var points = [],
745
+ values = s.split(/[ ,]+/),
746
+ i;
747
+ for (i = 0; i < values.length-1; i += 2) {
748
+ points.push( new Point( parseFloat( values[i]), parseFloat( values[i+1] )));
749
+ }
750
+
751
+ return points;
752
+ }
753
+
754
+ // Start of document processing ---------------------
755
+ var shapes = document.querySelectorAll('rect,circle,ellipse,path,text');
756
+
757
+ for (i = 0; i < shapes.length; ++i) {
758
+ var shape = shapes[i];
759
+ // Get id. If no id, create one.
760
+ var shape_id = shape.getAttribute("id");
761
+
762
+ if (!shape_id) {
763
+ shape_id = "patchjs_shape" + i;
764
+ shape.setAttribute("id", shape_id);
765
+ }
766
+
767
+ var fill = shape.style.fill,
768
+ url_value = fill.match(/^url\(\s*\"?\s*#([^\s\"]+)\"?\s*\)/);
769
+
770
+ if (url_value && url_value[1]) {
771
+ var mesh = document.getElementById(url_value[1]);
772
+
773
+ if (mesh.nodeName === "meshgradient" ) {
774
+ var bbox = shape.getBBox();
775
+
776
+ // Create temporary canvas
777
+ var my_canvas = document.createElementNS( xhtmlNS, "canvas" );
778
+ my_canvas.width = bbox.width;
779
+ my_canvas.height = bbox.height;
780
+
781
+ var my_context = my_canvas.getContext("2d"),
782
+ my_canvas_image = my_context.getImageData( 0, 0, my_canvas.width, my_canvas.height),
783
+ my_data = my_canvas_image.data;
784
+
785
+ // Draw a mesh
786
+ var my_mesh = new Mesh( url_value[1] );
787
+
788
+ // Adjust for bounding box if necessary.
789
+ if (mesh.getAttribute( "gradientUnits" ) === "objectBoundingBox") {
790
+ my_mesh.scale( new Point( bbox.width, bbox.height ) );
791
+ }
792
+
793
+ // Apply gradient transform.
794
+ var gradientTransform = mesh.getAttribute("gradientTransform");
795
+
796
+ if (gradientTransform !== null) {
797
+ var affine = parseTransform(gradientTransform);
798
+ my_mesh.transform(affine);
799
+ }
800
+
801
+ // Position to Canvas coordinate.
802
+ var t = new Point(-bbox.x, -bbox.y);
803
+
804
+ if (mesh.getAttribute( "gradientUnits" ) === "userSpaceOnUse") {
805
+ my_mesh.transform(t);
806
+ }
807
+
808
+ // Paint
809
+ my_mesh.paint(my_data, my_canvas.width);
810
+
811
+ my_context.putImageData(my_canvas_image, 0, 0);
812
+
813
+ // Create image element of correct size
814
+ var my_image = document.createElementNS( svgNS, "image" );
815
+ my_image.setAttribute("width", my_canvas.width);
816
+ my_image.setAttribute("height",my_canvas.height);
817
+ my_image.setAttribute("x", bbox.x);
818
+ my_image.setAttribute("y", bbox.y);
819
+
820
+ // Set image to data url
821
+ var my_png = my_canvas.toDataURL();
822
+ my_image.setAttributeNS(xlinkNS, "xlink:href", my_png);
823
+
824
+ // Insert image into document
825
+ shape.parentNode.insertBefore(my_image, shape);
826
+ shape.style.fill = "none";
827
+
828
+ // Create clip referencing shape and insert into document
829
+ var clip = document.createElementNS(svgNS, "clipPath"),
830
+ clip_id = "patchjs_clip" + i;
831
+
832
+ clip.setAttribute("id", clip_id);
833
+
834
+ var use = document.createElementNS(svgNS, "use");
835
+
836
+ use.setAttributeNS(xlinkNS, "xlink:href", "#" + shape_id);
837
+ clip.appendChild(use);
838
+ shape.parentElement.insertBefore(clip, shape);
839
+ my_image.setAttribute("clip-path", "url(#" + clip_id + ")");
840
+ }
841
+ }
842
+ }
843
+ })();