@emasoft/svg-matrix 1.1.0 → 1.2.0

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 (55) hide show
  1. package/bin/svg-matrix.js +7 -6
  2. package/bin/svgm.js +109 -40
  3. package/dist/svg-matrix.min.js +7 -7
  4. package/dist/svg-toolbox.min.js +148 -228
  5. package/dist/svgm.min.js +152 -232
  6. package/dist/version.json +5 -5
  7. package/package.json +1 -1
  8. package/scripts/postinstall.js +72 -41
  9. package/scripts/test-postinstall.js +18 -16
  10. package/scripts/version-sync.js +78 -60
  11. package/src/animation-optimization.js +190 -98
  12. package/src/animation-references.js +11 -3
  13. package/src/arc-length.js +23 -20
  14. package/src/bezier-analysis.js +9 -13
  15. package/src/bezier-intersections.js +18 -4
  16. package/src/browser-verify.js +35 -8
  17. package/src/clip-path-resolver.js +285 -114
  18. package/src/convert-path-data.js +20 -8
  19. package/src/css-specificity.js +33 -9
  20. package/src/douglas-peucker.js +272 -141
  21. package/src/geometry-to-path.js +79 -22
  22. package/src/gjk-collision.js +287 -126
  23. package/src/index.js +56 -21
  24. package/src/inkscape-support.js +122 -101
  25. package/src/logger.js +43 -27
  26. package/src/marker-resolver.js +201 -121
  27. package/src/mask-resolver.js +231 -98
  28. package/src/matrix.js +9 -5
  29. package/src/mesh-gradient.js +22 -14
  30. package/src/off-canvas-detection.js +53 -17
  31. package/src/path-optimization.js +356 -171
  32. package/src/path-simplification.js +671 -256
  33. package/src/pattern-resolver.js +1 -3
  34. package/src/polygon-clip.js +396 -78
  35. package/src/svg-boolean-ops.js +90 -23
  36. package/src/svg-collections.js +1546 -667
  37. package/src/svg-flatten.js +152 -38
  38. package/src/svg-matrix-lib.js +2 -2
  39. package/src/svg-parser.js +5 -1
  40. package/src/svg-rendering-context.js +3 -1
  41. package/src/svg-toolbox-lib.js +2 -2
  42. package/src/svg-toolbox.js +99 -457
  43. package/src/svg-validation-data.js +513 -345
  44. package/src/svg2-polyfills.js +156 -93
  45. package/src/svgm-lib.js +8 -4
  46. package/src/transform-optimization.js +168 -51
  47. package/src/transforms2d.js +73 -40
  48. package/src/transforms3d.js +34 -27
  49. package/src/use-symbol-resolver.js +175 -76
  50. package/src/vector.js +80 -44
  51. package/src/vendor/inkscape-hatch-polyfill.js +143 -108
  52. package/src/vendor/inkscape-hatch-polyfill.min.js +291 -1
  53. package/src/vendor/inkscape-mesh-polyfill.js +953 -766
  54. package/src/vendor/inkscape-mesh-polyfill.min.js +896 -1
  55. package/src/verification.js +3 -4
@@ -9,10 +9,10 @@
9
9
 
10
10
  (function () {
11
11
  // Name spaces -----------------------------------
12
- const svgNS = 'http://www.w3.org/2000/svg';
13
- const xlinkNS = 'http://www.w3.org/1999/xlink';
14
- const unitObjectBoundingBox = 'objectBoundingBox';
15
- const unitUserSpace = 'userSpaceOnUse';
12
+ const svgNS = "http://www.w3.org/2000/svg";
13
+ const xlinkNS = "http://www.w3.org/1999/xlink";
14
+ const unitObjectBoundingBox = "objectBoundingBox";
15
+ const unitUserSpace = "userSpaceOnUse";
16
16
 
17
17
  // Set multiple attributes to an element
18
18
  const setAttributes = (el, attrs) => {
@@ -24,13 +24,18 @@
24
24
  // Copy attributes from the hatch with 'id' to the current element
25
25
  const setReference = (el, id) => {
26
26
  const attr = [
27
- 'x', 'y', 'pitch', 'rotate',
28
- 'hatchUnits', 'hatchContentUnits', 'transform'
27
+ "x",
28
+ "y",
29
+ "pitch",
30
+ "rotate",
31
+ "hatchUnits",
32
+ "hatchContentUnits",
33
+ "transform",
29
34
  ];
30
35
  const template = document.getElementById(id.slice(1));
31
36
 
32
- if (template && template.nodeName === 'hatch') {
33
- attr.forEach(a => {
37
+ if (template && template.nodeName === "hatch") {
38
+ attr.forEach((a) => {
34
39
  let t = template.getAttribute(a);
35
40
  if (el.getAttribute(a) === null && t !== null) {
36
41
  el.setAttribute(a, t);
@@ -38,7 +43,7 @@
38
43
  });
39
44
 
40
45
  if (el.children.length === 0) {
41
- Array.from(template.children).forEach(c => {
46
+ Array.from(template.children).forEach((c) => {
42
47
  el.appendChild(c.cloneNode(true));
43
48
  });
44
49
  }
@@ -48,11 +53,12 @@
48
53
  // Order pain-order of hatchpaths relative to their pitch
49
54
  const orderHatchPaths = (paths) => {
50
55
  const nodeArray = [];
51
- paths.forEach(p => nodeArray.push(p));
56
+ paths.forEach((p) => nodeArray.push(p));
52
57
 
53
- return nodeArray.sort((a, b) =>
54
- // (pitch - a.offset) - (pitch - b.offset)
55
- Number(b.getAttribute('offset')) - Number(a.getAttribute('offset'))
58
+ return nodeArray.sort(
59
+ (a, b) =>
60
+ // (pitch - a.offset) - (pitch - b.offset)
61
+ Number(b.getAttribute("offset")) - Number(a.getAttribute("offset")),
56
62
  );
57
63
  };
58
64
 
@@ -92,55 +98,65 @@
92
98
 
93
99
  while (i < len) {
94
100
  switch (data[i].toUpperCase()) {
95
- case 'Z':
101
+ case "Z":
96
102
  array.push(data[i]);
97
103
  i += 1;
98
104
  last = 0;
99
105
  break;
100
- case 'M':
101
- case 'L':
102
- case 'T':
103
- array.push(data[i], new Point(Number(data[i + 1]), Number(data[i + 2])));
106
+ case "M":
107
+ case "L":
108
+ case "T":
109
+ array.push(
110
+ data[i],
111
+ new Point(Number(data[i + 1]), Number(data[i + 2])),
112
+ );
104
113
  i += 3;
105
114
  last = 1;
106
115
  break;
107
- case 'H':
116
+ case "H":
108
117
  array.push(data[i], new Point(Number(data[i + 1]), null));
109
118
  i += 2;
110
119
  last = 2;
111
120
  break;
112
- case 'V':
121
+ case "V":
113
122
  array.push(data[i], new Point(null, Number(data[i + 1])));
114
123
  i += 2;
115
124
  last = 3;
116
125
  break;
117
- case 'C':
126
+ case "C":
118
127
  array.push(
119
- data[i], new Point(Number(data[i + 1]), Number(data[i + 2])),
128
+ data[i],
129
+ new Point(Number(data[i + 1]), Number(data[i + 2])),
120
130
  new Point(Number(data[i + 3]), Number(data[i + 4])),
121
- new Point(Number(data[i + 5]), Number(data[i + 6]))
131
+ new Point(Number(data[i + 5]), Number(data[i + 6])),
122
132
  );
123
133
  i += 7;
124
134
  last = 4;
125
135
  break;
126
- case 'S':
127
- case 'Q':
136
+ case "S":
137
+ case "Q":
128
138
  array.push(
129
- data[i], new Point(Number(data[i + 1]), Number(data[i + 2])),
130
- new Point(Number(data[i + 3]), Number(data[i + 4]))
139
+ data[i],
140
+ new Point(Number(data[i + 1]), Number(data[i + 2])),
141
+ new Point(Number(data[i + 3]), Number(data[i + 4])),
131
142
  );
132
143
  i += 5;
133
144
  last = 5;
134
145
  break;
135
- case 'A':
146
+ case "A":
136
147
  array.push(
137
- data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
138
- data[i + 5], new Point(Number(data[i + 6]), Number(data[i + 7]))
148
+ data[i],
149
+ data[i + 1],
150
+ data[i + 2],
151
+ data[i + 3],
152
+ data[i + 4],
153
+ data[i + 5],
154
+ new Point(Number(data[i + 6]), Number(data[i + 7])),
139
155
  );
140
156
  i += 8;
141
157
  last = 6;
142
158
  break;
143
- case 'B':
159
+ case "B":
144
160
  array.push(data[i], data[i + 1]);
145
161
  i += 2;
146
162
  last = 7;
@@ -163,21 +179,25 @@
163
179
  array.push(
164
180
  new Point(Number(data[i]), Number(data[i + 1])),
165
181
  new Point(Number(data[i + 2]), Number(data[i + 3])),
166
- new Point(Number(data[i + 4]), Number(data[i + 5]))
182
+ new Point(Number(data[i + 4]), Number(data[i + 5])),
167
183
  );
168
184
  i += 6;
169
185
  break;
170
186
  case 5:
171
187
  array.push(
172
188
  new Point(Number(data[i]), Number(data[i + 1])),
173
- new Point(Number(data[i + 2]), Number(data[i + 3]))
189
+ new Point(Number(data[i + 2]), Number(data[i + 3])),
174
190
  );
175
191
  i += 4;
176
192
  break;
177
193
  case 6:
178
194
  array.push(
179
- data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
180
- new Point(Number(data[i + 5]), Number(data[i + 6]))
195
+ data[i],
196
+ data[i + 1],
197
+ data[i + 2],
198
+ data[i + 3],
199
+ data[i + 4],
200
+ new Point(Number(data[i + 5]), Number(data[i + 6])),
181
201
  );
182
202
  i += 7;
183
203
  break;
@@ -192,70 +212,72 @@
192
212
  };
193
213
 
194
214
  const getYDistance = (hatchpath) => {
195
- const path = document.createElementNS(svgNS, 'path');
196
- let d = hatchpath.getAttribute('d');
215
+ const path = document.createElementNS(svgNS, "path");
216
+ let d = hatchpath.getAttribute("d");
197
217
 
198
- if (d[0].toUpperCase() !== 'M') {
218
+ if (d[0].toUpperCase() !== "M") {
199
219
  d = `M 0,0 ${d}`;
200
220
  }
201
221
 
202
- path.setAttribute('d', d);
222
+ path.setAttribute("d", d);
203
223
 
204
- return path.getPointAtLength(path.getTotalLength()).y -
205
- path.getPointAtLength(0).y;
224
+ return (
225
+ path.getPointAtLength(path.getTotalLength()).y -
226
+ path.getPointAtLength(0).y
227
+ );
206
228
  };
207
229
 
208
230
  // Point class --------------------------------------
209
231
  class Point {
210
- constructor (x, y) {
232
+ constructor(x, y) {
211
233
  this.x = x;
212
234
  this.y = y;
213
235
  }
214
236
 
215
- toString () {
237
+ toString() {
216
238
  return `${this.x} ${this.y}`;
217
239
  }
218
240
 
219
- isPoint () {
241
+ isPoint() {
220
242
  return true;
221
243
  }
222
244
 
223
- clone () {
245
+ clone() {
224
246
  return new Point(this.x, this.y);
225
247
  }
226
248
 
227
- add (v) {
249
+ add(v) {
228
250
  return new Point(this.x + v.x, this.y + v.y);
229
251
  }
230
252
 
231
- distSquared (v) {
253
+ distSquared(v) {
232
254
  let x = this.x - v.x;
233
255
  let y = this.y - v.y;
234
- return (x * x + y * y);
256
+ return x * x + y * y;
235
257
  }
236
258
  }
237
259
 
238
260
  // Start of document processing ---------------------
239
- const shapes = document.querySelectorAll('rect,circle,ellipse,path,text');
261
+ const shapes = document.querySelectorAll("rect,circle,ellipse,path,text");
240
262
 
241
263
  shapes.forEach((shape, i) => {
242
264
  // Get id. If no id, create one.
243
- let shapeId = shape.getAttribute('id');
265
+ let shapeId = shape.getAttribute("id");
244
266
  if (!shapeId) {
245
- shapeId = 'hatch_shape_' + i;
246
- shape.setAttribute('id', shapeId);
267
+ shapeId = "hatch_shape_" + i;
268
+ shape.setAttribute("id", shapeId);
247
269
  }
248
270
 
249
- const fill = shape.getAttribute('fill') || shape.style.fill;
271
+ const fill = shape.getAttribute("fill") || shape.style.fill;
250
272
  const fillURL = fill.match(/^url\(\s*"?\s*#([^\s"]+)"?\s*\)/);
251
273
 
252
274
  if (fillURL && fillURL[1]) {
253
275
  const hatch = document.getElementById(fillURL[1]);
254
276
 
255
- if (hatch && hatch.nodeName === 'hatch') {
256
- const href = hatch.getAttributeNS(xlinkNS, 'href');
277
+ if (hatch && hatch.nodeName === "hatch") {
278
+ const href = hatch.getAttributeNS(xlinkNS, "href");
257
279
 
258
- if (href !== null && href !== '') {
280
+ if (href !== null && href !== "") {
259
281
  setReference(hatch, href);
260
282
  }
261
283
 
@@ -265,26 +287,34 @@
265
287
  }
266
288
 
267
289
  const bbox = shape.getBBox();
268
- const hatchDiag = Math.ceil(Math.sqrt(
269
- bbox.width * bbox.width + bbox.height * bbox.height
270
- ));
290
+ const hatchDiag = Math.ceil(
291
+ Math.sqrt(bbox.width * bbox.width + bbox.height * bbox.height),
292
+ );
271
293
 
272
294
  // Hatch variables
273
- const units = hatch.getAttribute('hatchUnits') || unitObjectBoundingBox;
274
- const contentUnits = hatch.getAttribute('hatchContentUnits') || unitUserSpace;
275
- const rotate = Number(hatch.getAttribute('rotate')) || 0;
276
- const transform = hatch.getAttribute('transform') ||
277
- hatch.getAttribute('hatchTransform') || '';
278
- const hatchpaths = orderHatchPaths(hatch.querySelectorAll('hatchpath,hatchPath'));
279
- const x = units === unitObjectBoundingBox
280
- ? (Number(hatch.getAttribute('x')) * bbox.width) || 0
281
- : Number(hatch.getAttribute('x')) || 0;
282
- const y = units === unitObjectBoundingBox
283
- ? (Number(hatch.getAttribute('y')) * bbox.width) || 0
284
- : Number(hatch.getAttribute('y')) || 0;
285
- let pitch = units === unitObjectBoundingBox
286
- ? (Number(hatch.getAttribute('pitch')) * bbox.width) || 0
287
- : Number(hatch.getAttribute('pitch')) || 0;
295
+ const units = hatch.getAttribute("hatchUnits") || unitObjectBoundingBox;
296
+ const contentUnits =
297
+ hatch.getAttribute("hatchContentUnits") || unitUserSpace;
298
+ const rotate = Number(hatch.getAttribute("rotate")) || 0;
299
+ const transform =
300
+ hatch.getAttribute("transform") ||
301
+ hatch.getAttribute("hatchTransform") ||
302
+ "";
303
+ const hatchpaths = orderHatchPaths(
304
+ hatch.querySelectorAll("hatchpath,hatchPath"),
305
+ );
306
+ const x =
307
+ units === unitObjectBoundingBox
308
+ ? Number(hatch.getAttribute("x")) * bbox.width || 0
309
+ : Number(hatch.getAttribute("x")) || 0;
310
+ const y =
311
+ units === unitObjectBoundingBox
312
+ ? Number(hatch.getAttribute("y")) * bbox.width || 0
313
+ : Number(hatch.getAttribute("y")) || 0;
314
+ let pitch =
315
+ units === unitObjectBoundingBox
316
+ ? Number(hatch.getAttribute("pitch")) * bbox.width || 0
317
+ : Number(hatch.getAttribute("pitch")) || 0;
288
318
 
289
319
  if (contentUnits === unitObjectBoundingBox && bbox.height) {
290
320
  pitch /= bbox.height;
@@ -293,45 +323,46 @@
293
323
  // A negative value is an error.
294
324
  // A value of zero disables rendering of the element
295
325
  if (pitch <= 0) {
296
- console.error('Non-positive pitch');
326
+ console.error("Non-positive pitch");
297
327
  return;
298
328
  }
299
329
 
300
330
  // Pattern variables
301
- const pattern = document.createElementNS(svgNS, 'pattern');
331
+ const pattern = document.createElementNS(svgNS, "pattern");
302
332
  const patternId = `${fillURL[1]}_pattern`;
303
- let patternWidth = bbox.width - bbox.width % pitch;
333
+ let patternWidth = bbox.width - (bbox.width % pitch);
304
334
  let patternHeight = 0;
305
335
 
306
336
  const xPositions = generatePositions(patternWidth, hatchDiag, x, pitch);
307
337
 
308
- hatchpaths.forEach(hatchpath => {
309
- let offset = Number(hatchpath.getAttribute('offset')) || 0;
310
- offset = offset > pitch ? (offset % pitch) : offset;
311
- const currentXPositions = xPositions.map(p => p + offset);
338
+ hatchpaths.forEach((hatchpath) => {
339
+ let offset = Number(hatchpath.getAttribute("offset")) || 0;
340
+ offset = offset > pitch ? offset % pitch : offset;
341
+ const currentXPositions = xPositions.map((p) => p + offset);
312
342
 
313
- const path = document.createElementNS(svgNS, 'path');
314
- let d = '';
343
+ const path = document.createElementNS(svgNS, "path");
344
+ let d = "";
315
345
 
316
346
  for (let j = 0; j < hatchpath.attributes.length; ++j) {
317
347
  const attr = hatchpath.attributes.item(j);
318
- if (attr.name !== 'd') {
348
+ if (attr.name !== "d") {
319
349
  path.setAttribute(attr.name, attr.value);
320
350
  }
321
351
  }
322
352
 
323
- if (hatchpath.getAttribute('d') === null) {
353
+ if (hatchpath.getAttribute("d") === null) {
324
354
  d += currentXPositions.reduce(
325
- (acc, xPos) => `${acc}M ${xPos} ${y} V ${hatchDiag} `, ''
355
+ (acc, xPos) => `${acc}M ${xPos} ${y} V ${hatchDiag} `,
356
+ "",
326
357
  );
327
358
  patternHeight = hatchDiag;
328
359
  } else {
329
- const hatchData = hatchpath.getAttribute('d');
360
+ const hatchData = hatchpath.getAttribute("d");
330
361
  const data = parsePath(
331
- hatchData.match(/([+-]?(\d+(\.\d+)?))|[MmZzLlHhVvCcSsQqTtAaBb]/g)
362
+ hatchData.match(/([+-]?(\d+(\.\d+)?))|[MmZzLlHhVvCcSsQqTtAaBb]/g),
332
363
  );
333
364
  const len = data.length;
334
- const startsWithM = data[0] === 'M';
365
+ const startsWithM = data[0] === "M";
335
366
  const relative = data[0].toLowerCase() === data[0];
336
367
  const point = new Point(0, 0);
337
368
  let yOffset = getYDistance(hatchpath);
@@ -342,23 +373,26 @@
342
373
 
343
374
  // The offset must be positive
344
375
  if (yOffset <= 0) {
345
- console.error('y offset is non-positive');
376
+ console.error("y offset is non-positive");
346
377
  return;
347
378
  }
348
- patternHeight = bbox.height - bbox.height % yOffset;
379
+ patternHeight = bbox.height - (bbox.height % yOffset);
349
380
 
350
381
  const currentYPositions = generatePositions(
351
- patternHeight, hatchDiag, y, yOffset
382
+ patternHeight,
383
+ hatchDiag,
384
+ y,
385
+ yOffset,
352
386
  );
353
387
 
354
- currentXPositions.forEach(xPos => {
388
+ currentXPositions.forEach((xPos) => {
355
389
  point.x = xPos;
356
390
 
357
391
  if (!startsWithM && !relative) {
358
392
  d += `M ${xPos} 0`;
359
393
  }
360
394
 
361
- currentYPositions.forEach(yPos => {
395
+ currentYPositions.forEach((yPos) => {
362
396
  point.y = yPos;
363
397
 
364
398
  if (relative) {
@@ -366,35 +400,36 @@
366
400
  d += `M ${xPos} ${yPos} ${hatchData}`;
367
401
  } else {
368
402
  // Path is absolute, translate every point
369
- d += data.map(e => e.isPoint && e.isPoint() ? e.add(point) : e)
370
- .map(e => e.isPoint && e.isPoint() ? e.toString() : e)
371
- .reduce((acc, e) => `${acc} ${e}`, '');
403
+ d += data
404
+ .map((e) => (e.isPoint && e.isPoint() ? e.add(point) : e))
405
+ .map((e) => (e.isPoint && e.isPoint() ? e.toString() : e))
406
+ .reduce((acc, e) => `${acc} ${e}`, "");
372
407
  }
373
408
  });
374
409
  });
375
410
 
376
411
  // The hatchpaths are infinite, so they have no fill
377
- path.style.fill = 'none';
412
+ path.style.fill = "none";
378
413
  }
379
414
 
380
- path.setAttribute('d', d);
415
+ path.setAttribute("d", d);
381
416
  pattern.appendChild(path);
382
417
  });
383
418
 
384
419
  setAttributes(pattern, {
385
- 'id': patternId,
386
- 'patternUnits': unitUserSpace,
387
- 'patternContentUnits': contentUnits,
388
- 'width': patternWidth,
389
- 'height': patternHeight,
390
- 'x': bbox.x,
391
- 'y': bbox.y,
392
- 'patternTransform': `rotate(${rotate} ${0} ${0}) ${transform}`
420
+ id: patternId,
421
+ patternUnits: unitUserSpace,
422
+ patternContentUnits: contentUnits,
423
+ width: patternWidth,
424
+ height: patternHeight,
425
+ x: bbox.x,
426
+ y: bbox.y,
427
+ patternTransform: `rotate(${rotate} ${0} ${0}) ${transform}`,
393
428
  });
394
429
  hatch.parentElement.insertBefore(pattern, hatch);
395
430
 
396
431
  shape.style.fill = `url(#${patternId})`;
397
- shape.setAttribute('fill', `url(#${patternId})`);
432
+ shape.setAttribute("fill", `url(#${patternId})`);
398
433
  }
399
434
  }
400
435
  });