@emasoft/svg-matrix 1.0.28 → 1.0.30
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.
- package/README.md +325 -0
- package/bin/svg-matrix.js +985 -378
- package/bin/svglinter.cjs +4172 -433
- package/bin/svgm.js +723 -180
- package/package.json +16 -4
- package/src/animation-references.js +71 -52
- package/src/arc-length.js +160 -96
- package/src/bezier-analysis.js +257 -117
- package/src/bezier-intersections.js +411 -148
- package/src/browser-verify.js +240 -100
- package/src/clip-path-resolver.js +350 -142
- package/src/convert-path-data.js +279 -134
- package/src/css-specificity.js +78 -70
- package/src/flatten-pipeline.js +751 -263
- package/src/geometry-to-path.js +511 -182
- package/src/index.js +191 -46
- package/src/inkscape-support.js +18 -7
- package/src/marker-resolver.js +278 -164
- package/src/mask-resolver.js +209 -98
- package/src/matrix.js +147 -67
- package/src/mesh-gradient.js +187 -96
- package/src/off-canvas-detection.js +201 -104
- package/src/path-analysis.js +187 -107
- package/src/path-data-plugins.js +628 -167
- package/src/path-simplification.js +0 -1
- package/src/pattern-resolver.js +125 -88
- package/src/polygon-clip.js +111 -66
- package/src/svg-boolean-ops.js +194 -118
- package/src/svg-collections.js +22 -18
- package/src/svg-flatten.js +282 -164
- package/src/svg-parser.js +427 -200
- package/src/svg-rendering-context.js +147 -104
- package/src/svg-toolbox.js +16381 -3370
- package/src/svg2-polyfills.js +93 -224
- package/src/transform-decomposition.js +46 -41
- package/src/transform-optimization.js +89 -68
- package/src/transforms2d.js +49 -16
- package/src/transforms3d.js +58 -22
- package/src/use-symbol-resolver.js +150 -110
- package/src/vector.js +67 -15
- package/src/vendor/README.md +110 -0
- package/src/vendor/inkscape-hatch-polyfill.js +401 -0
- package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
- package/src/vendor/inkscape-mesh-polyfill.js +843 -0
- package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
- package/src/verification.js +288 -124
package/src/mesh-gradient.js
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
* @module mesh-gradient
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import Decimal from
|
|
11
|
-
import { Matrix } from
|
|
12
|
-
import * as
|
|
13
|
-
import * as PolygonClip from
|
|
10
|
+
import Decimal from "decimal.js";
|
|
11
|
+
import { Matrix as _Matrix } from "./matrix.js";
|
|
12
|
+
import * as _Transforms2D from "./transforms2d.js";
|
|
13
|
+
import * as PolygonClip from "./polygon-clip.js";
|
|
14
14
|
|
|
15
15
|
Decimal.set({ precision: 80 });
|
|
16
16
|
|
|
17
|
-
const D = x => (x instanceof Decimal ? x : new Decimal(x));
|
|
17
|
+
const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
|
|
18
18
|
|
|
19
19
|
// Default samples per patch edge for rasterization
|
|
20
20
|
const DEFAULT_PATCH_SAMPLES = 16;
|
|
@@ -66,7 +66,12 @@ export function point(x, y) {
|
|
|
66
66
|
* const blue = color(0, 0, 255, 128);
|
|
67
67
|
*/
|
|
68
68
|
export function color(r, g, b, a = 255) {
|
|
69
|
-
return {
|
|
69
|
+
return {
|
|
70
|
+
r: Math.round(r),
|
|
71
|
+
g: Math.round(g),
|
|
72
|
+
b: Math.round(b),
|
|
73
|
+
a: Math.round(a),
|
|
74
|
+
};
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
/**
|
|
@@ -103,14 +108,16 @@ export function parseColor(colorStr, opacity = 1) {
|
|
|
103
108
|
if (!colorStr) return color(0, 0, 0, 255);
|
|
104
109
|
|
|
105
110
|
// Handle rgb() and rgba()
|
|
106
|
-
const rgbMatch = colorStr.match(
|
|
111
|
+
const rgbMatch = colorStr.match(
|
|
112
|
+
/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/i,
|
|
113
|
+
);
|
|
107
114
|
if (rgbMatch) {
|
|
108
115
|
const a = rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) * 255 : 255;
|
|
109
116
|
return color(
|
|
110
|
-
parseInt(rgbMatch[1]),
|
|
111
|
-
parseInt(rgbMatch[2]),
|
|
112
|
-
parseInt(rgbMatch[3]),
|
|
113
|
-
a * opacity
|
|
117
|
+
parseInt(rgbMatch[1], 10),
|
|
118
|
+
parseInt(rgbMatch[2], 10),
|
|
119
|
+
parseInt(rgbMatch[3], 10),
|
|
120
|
+
a * opacity,
|
|
114
121
|
);
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -123,30 +130,36 @@ export function parseColor(colorStr, opacity = 1) {
|
|
|
123
130
|
parseInt(hex[0] + hex[0], 16),
|
|
124
131
|
parseInt(hex[1] + hex[1], 16),
|
|
125
132
|
parseInt(hex[2] + hex[2], 16),
|
|
126
|
-
255 * opacity
|
|
133
|
+
255 * opacity,
|
|
127
134
|
);
|
|
128
135
|
} else if (hex.length === 6) {
|
|
129
136
|
return color(
|
|
130
137
|
parseInt(hex.slice(0, 2), 16),
|
|
131
138
|
parseInt(hex.slice(2, 4), 16),
|
|
132
139
|
parseInt(hex.slice(4, 6), 16),
|
|
133
|
-
255 * opacity
|
|
140
|
+
255 * opacity,
|
|
134
141
|
);
|
|
135
142
|
} else if (hex.length === 8) {
|
|
136
143
|
return color(
|
|
137
144
|
parseInt(hex.slice(0, 2), 16),
|
|
138
145
|
parseInt(hex.slice(2, 4), 16),
|
|
139
146
|
parseInt(hex.slice(4, 6), 16),
|
|
140
|
-
parseInt(hex.slice(6, 8), 16) * opacity
|
|
147
|
+
parseInt(hex.slice(6, 8), 16) * opacity,
|
|
141
148
|
);
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
// Named colors (subset for common ones)
|
|
146
153
|
const namedColors = {
|
|
147
|
-
black: [0, 0, 0],
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
black: [0, 0, 0],
|
|
155
|
+
white: [255, 255, 255],
|
|
156
|
+
red: [255, 0, 0],
|
|
157
|
+
green: [0, 128, 0],
|
|
158
|
+
blue: [0, 0, 255],
|
|
159
|
+
yellow: [255, 255, 0],
|
|
160
|
+
cyan: [0, 255, 255],
|
|
161
|
+
magenta: [255, 0, 255],
|
|
162
|
+
transparent: [0, 0, 0, 0],
|
|
150
163
|
};
|
|
151
164
|
const named = namedColors[colorStr.toLowerCase()];
|
|
152
165
|
if (named) {
|
|
@@ -192,7 +205,7 @@ export function lerpColor(c1, c2, t) {
|
|
|
192
205
|
c1.r * mt + c2.r * tNum,
|
|
193
206
|
c1.g * mt + c2.g * tNum,
|
|
194
207
|
c1.b * mt + c2.b * tNum,
|
|
195
|
-
c1.a * mt + c2.a * tNum
|
|
208
|
+
c1.a * mt + c2.a * tNum,
|
|
196
209
|
);
|
|
197
210
|
}
|
|
198
211
|
|
|
@@ -231,10 +244,22 @@ export function bilinearColor(c00, c10, c01, c11, u, v) {
|
|
|
231
244
|
const mv = 1 - vNum;
|
|
232
245
|
|
|
233
246
|
return color(
|
|
234
|
-
mu * mv * c00.r +
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
247
|
+
mu * mv * c00.r +
|
|
248
|
+
uNum * mv * c10.r +
|
|
249
|
+
mu * vNum * c01.r +
|
|
250
|
+
uNum * vNum * c11.r,
|
|
251
|
+
mu * mv * c00.g +
|
|
252
|
+
uNum * mv * c10.g +
|
|
253
|
+
mu * vNum * c01.g +
|
|
254
|
+
uNum * vNum * c11.g,
|
|
255
|
+
mu * mv * c00.b +
|
|
256
|
+
uNum * mv * c10.b +
|
|
257
|
+
mu * vNum * c01.b +
|
|
258
|
+
uNum * vNum * c11.b,
|
|
259
|
+
mu * mv * c00.a +
|
|
260
|
+
uNum * mv * c10.a +
|
|
261
|
+
mu * vNum * c01.a +
|
|
262
|
+
uNum * vNum * c11.a,
|
|
238
263
|
);
|
|
239
264
|
}
|
|
240
265
|
|
|
@@ -284,10 +309,16 @@ export function evalCubicBezier(p0, p1, p2, p3, t) {
|
|
|
284
309
|
const t3 = t2.mul(t);
|
|
285
310
|
|
|
286
311
|
return {
|
|
287
|
-
x: mt3
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
312
|
+
x: mt3
|
|
313
|
+
.mul(p0.x)
|
|
314
|
+
.plus(D(3).mul(mt2).mul(t).mul(p1.x))
|
|
315
|
+
.plus(D(3).mul(mt).mul(t2).mul(p2.x))
|
|
316
|
+
.plus(t3.mul(p3.x)),
|
|
317
|
+
y: mt3
|
|
318
|
+
.mul(p0.y)
|
|
319
|
+
.plus(D(3).mul(mt2).mul(t).mul(p1.y))
|
|
320
|
+
.plus(D(3).mul(mt).mul(t2).mul(p2.y))
|
|
321
|
+
.plus(t3.mul(p3.y)),
|
|
291
322
|
};
|
|
292
323
|
}
|
|
293
324
|
|
|
@@ -332,7 +363,7 @@ export function splitBezier(curve) {
|
|
|
332
363
|
// De Casteljau subdivision at t=0.5
|
|
333
364
|
const mid = (a, b) => ({
|
|
334
365
|
x: a.x.plus(b.x).div(2),
|
|
335
|
-
y: a.y.plus(b.y).div(2)
|
|
366
|
+
y: a.y.plus(b.y).div(2),
|
|
336
367
|
});
|
|
337
368
|
|
|
338
369
|
const q0 = p0;
|
|
@@ -345,7 +376,10 @@ export function splitBezier(curve) {
|
|
|
345
376
|
const r2 = mid(p2, p3);
|
|
346
377
|
const r3 = p3;
|
|
347
378
|
|
|
348
|
-
return [
|
|
379
|
+
return [
|
|
380
|
+
[q0, q1, q2, q3],
|
|
381
|
+
[r0, r1, r2, r3],
|
|
382
|
+
];
|
|
349
383
|
}
|
|
350
384
|
|
|
351
385
|
// ============================================================================
|
|
@@ -449,10 +483,10 @@ export class CoonsPatch {
|
|
|
449
483
|
*/
|
|
450
484
|
evaluate(u, v) {
|
|
451
485
|
// Boundary curves
|
|
452
|
-
const Lc = evalCubicBezier(...this.top, u);
|
|
453
|
-
const Ld = evalCubicBezier(...this.bottom, u);
|
|
454
|
-
const La = evalCubicBezier(...this.left, v);
|
|
455
|
-
const Lb = evalCubicBezier(...this.right, v);
|
|
486
|
+
const Lc = evalCubicBezier(...this.top, u); // L_c(u,0)
|
|
487
|
+
const Ld = evalCubicBezier(...this.bottom, u); // L_d(u,1)
|
|
488
|
+
const La = evalCubicBezier(...this.left, v); // L_a(0,v)
|
|
489
|
+
const Lb = evalCubicBezier(...this.right, v); // L_b(1,v)
|
|
456
490
|
|
|
457
491
|
// Corner points
|
|
458
492
|
const P00 = this.top[0];
|
|
@@ -474,11 +508,15 @@ export class CoonsPatch {
|
|
|
474
508
|
const Sd_y = mu.mul(La.y).plus(u.mul(Lb.y));
|
|
475
509
|
|
|
476
510
|
// Bilinear interpolation of corners
|
|
477
|
-
const B_x = mu
|
|
511
|
+
const B_x = mu
|
|
512
|
+
.mul(mv)
|
|
513
|
+
.mul(P00.x)
|
|
478
514
|
.plus(u.mul(mv).mul(P10.x))
|
|
479
515
|
.plus(mu.mul(v).mul(P01.x))
|
|
480
516
|
.plus(u.mul(v).mul(P11.x));
|
|
481
|
-
const B_y = mu
|
|
517
|
+
const B_y = mu
|
|
518
|
+
.mul(mv)
|
|
519
|
+
.mul(P00.y)
|
|
482
520
|
.plus(u.mul(mv).mul(P10.y))
|
|
483
521
|
.plus(mu.mul(v).mul(P01.y))
|
|
484
522
|
.plus(u.mul(v).mul(P11.y));
|
|
@@ -486,14 +524,17 @@ export class CoonsPatch {
|
|
|
486
524
|
// Coons formula: Sc + Sd - B
|
|
487
525
|
const pt = {
|
|
488
526
|
x: Sc_x.plus(Sd_x).minus(B_x),
|
|
489
|
-
y: Sc_y.plus(Sd_y).minus(B_y)
|
|
527
|
+
y: Sc_y.plus(Sd_y).minus(B_y),
|
|
490
528
|
};
|
|
491
529
|
|
|
492
530
|
// Color interpolation (bilinear)
|
|
493
531
|
const col = bilinearColor(
|
|
494
|
-
this.colors[0][0],
|
|
495
|
-
this.colors[
|
|
496
|
-
|
|
532
|
+
this.colors[0][0],
|
|
533
|
+
this.colors[0][1],
|
|
534
|
+
this.colors[1][0],
|
|
535
|
+
this.colors[1][1],
|
|
536
|
+
u,
|
|
537
|
+
v,
|
|
497
538
|
);
|
|
498
539
|
|
|
499
540
|
return { point: pt, color: col };
|
|
@@ -550,7 +591,12 @@ export class CoonsPatch {
|
|
|
550
591
|
const midH = [midLeft.point, midLeft.point, center.point, center.point];
|
|
551
592
|
const midH2 = [center.point, center.point, midRight.point, midRight.point];
|
|
552
593
|
const midV = [midTop.point, midTop.point, center.point, center.point];
|
|
553
|
-
const midV2 = [
|
|
594
|
+
const midV2 = [
|
|
595
|
+
center.point,
|
|
596
|
+
center.point,
|
|
597
|
+
midBottom.point,
|
|
598
|
+
midBottom.point,
|
|
599
|
+
];
|
|
554
600
|
|
|
555
601
|
// Colors at subdivided corners
|
|
556
602
|
const c00 = this.colors[0][0];
|
|
@@ -565,13 +611,25 @@ export class CoonsPatch {
|
|
|
565
611
|
|
|
566
612
|
return [
|
|
567
613
|
// Top-left
|
|
568
|
-
new CoonsPatch(topL, midV, midH, leftT, [
|
|
614
|
+
new CoonsPatch(topL, midV, midH, leftT, [
|
|
615
|
+
[c00, cTop],
|
|
616
|
+
[cLeft, cMid],
|
|
617
|
+
]),
|
|
569
618
|
// Top-right
|
|
570
|
-
new CoonsPatch(topR, rightT, midH2, midV, [
|
|
619
|
+
new CoonsPatch(topR, rightT, midH2, midV, [
|
|
620
|
+
[cTop, c10],
|
|
621
|
+
[cMid, cRight],
|
|
622
|
+
]),
|
|
571
623
|
// Bottom-left
|
|
572
|
-
new CoonsPatch(midH, midV2, bottomL, leftB, [
|
|
624
|
+
new CoonsPatch(midH, midV2, bottomL, leftB, [
|
|
625
|
+
[cLeft, cMid],
|
|
626
|
+
[c01, cBottom],
|
|
627
|
+
]),
|
|
573
628
|
// Bottom-right
|
|
574
|
-
new CoonsPatch(midH2, rightB, bottomR, midV2, [
|
|
629
|
+
new CoonsPatch(midH2, rightB, bottomR, midV2, [
|
|
630
|
+
[cMid, cRight],
|
|
631
|
+
[cBottom, c11],
|
|
632
|
+
]),
|
|
575
633
|
];
|
|
576
634
|
}
|
|
577
635
|
|
|
@@ -614,15 +672,27 @@ export class CoonsPatch {
|
|
|
614
672
|
const len2 = dx.mul(dx).plus(dy.mul(dy));
|
|
615
673
|
if (len2.lt(1e-10)) return true;
|
|
616
674
|
|
|
617
|
-
const dist1 = dx
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
675
|
+
const dist1 = dx
|
|
676
|
+
.mul(p1.y.minus(p0.y))
|
|
677
|
+
.minus(dy.mul(p1.x.minus(p0.x)))
|
|
678
|
+
.abs();
|
|
679
|
+
const dist2 = dx
|
|
680
|
+
.mul(p2.y.minus(p0.y))
|
|
681
|
+
.minus(dy.mul(p2.x.minus(p0.x)))
|
|
682
|
+
.abs();
|
|
683
|
+
|
|
684
|
+
return (
|
|
685
|
+
dist1.div(len2.sqrt()).lt(SUBDIVISION_THRESHOLD) &&
|
|
686
|
+
dist2.div(len2.sqrt()).lt(SUBDIVISION_THRESHOLD)
|
|
687
|
+
);
|
|
622
688
|
};
|
|
623
689
|
|
|
624
|
-
return
|
|
625
|
-
|
|
690
|
+
return (
|
|
691
|
+
curveFlat(this.top) &&
|
|
692
|
+
curveFlat(this.right) &&
|
|
693
|
+
curveFlat(this.bottom) &&
|
|
694
|
+
curveFlat(this.left)
|
|
695
|
+
);
|
|
626
696
|
}
|
|
627
697
|
|
|
628
698
|
/**
|
|
@@ -647,9 +717,16 @@ export class CoonsPatch {
|
|
|
647
717
|
* bbox.maxY >= 0 && bbox.minY <= viewportHeight;
|
|
648
718
|
*/
|
|
649
719
|
getBBox() {
|
|
650
|
-
const allPoints = [
|
|
651
|
-
|
|
652
|
-
|
|
720
|
+
const allPoints = [
|
|
721
|
+
...this.top,
|
|
722
|
+
...this.right,
|
|
723
|
+
...this.bottom,
|
|
724
|
+
...this.left,
|
|
725
|
+
];
|
|
726
|
+
let minX = allPoints[0].x,
|
|
727
|
+
maxX = allPoints[0].x;
|
|
728
|
+
let minY = allPoints[0].y,
|
|
729
|
+
maxY = allPoints[0].y;
|
|
653
730
|
|
|
654
731
|
for (const p of allPoints) {
|
|
655
732
|
if (p.x.lt(minX)) minX = p.x;
|
|
@@ -722,8 +799,8 @@ export class CoonsPatch {
|
|
|
722
799
|
export function parseMeshGradient(meshGradientDef) {
|
|
723
800
|
const x = D(meshGradientDef.x || 0);
|
|
724
801
|
const y = D(meshGradientDef.y || 0);
|
|
725
|
-
const type = meshGradientDef.type ||
|
|
726
|
-
const gradientUnits = meshGradientDef.gradientUnits ||
|
|
802
|
+
const type = meshGradientDef.type || "bilinear";
|
|
803
|
+
const gradientUnits = meshGradientDef.gradientUnits || "userSpaceOnUse";
|
|
727
804
|
const gradientTransform = meshGradientDef.gradientTransform || null;
|
|
728
805
|
|
|
729
806
|
const patches = [];
|
|
@@ -734,8 +811,8 @@ export function parseMeshGradient(meshGradientDef) {
|
|
|
734
811
|
const nodes = [[point(x, y)]];
|
|
735
812
|
const colors = [[]];
|
|
736
813
|
|
|
737
|
-
|
|
738
|
-
|
|
814
|
+
const _currentX = x;
|
|
815
|
+
const _currentY = y;
|
|
739
816
|
|
|
740
817
|
for (let rowIdx = 0; rowIdx < meshRows.length; rowIdx++) {
|
|
741
818
|
const row = meshRows[rowIdx];
|
|
@@ -753,23 +830,28 @@ export function parseMeshGradient(meshGradientDef) {
|
|
|
753
830
|
// Each patch has up to 4 stops defining edges
|
|
754
831
|
for (let stopIdx = 0; stopIdx < stops.length; stopIdx++) {
|
|
755
832
|
const stop = stops[stopIdx];
|
|
756
|
-
const pathData = stop.path ||
|
|
757
|
-
const stopColor = stop.color
|
|
833
|
+
const pathData = stop.path || "";
|
|
834
|
+
const stopColor = stop.color
|
|
835
|
+
? parseColor(stop.color, stop.opacity || 1)
|
|
836
|
+
: null;
|
|
758
837
|
|
|
759
838
|
// Parse path command (c/C/l/L for bezier/line)
|
|
760
839
|
const pathMatch = pathData.match(/^\s*([cClL])\s*(.*)/);
|
|
761
840
|
if (pathMatch) {
|
|
762
841
|
const cmd = pathMatch[1];
|
|
763
|
-
const
|
|
842
|
+
const _coords = pathMatch[2]
|
|
843
|
+
.trim()
|
|
844
|
+
.split(/[\s,]+/)
|
|
845
|
+
.map(Number);
|
|
764
846
|
|
|
765
|
-
if (cmd ===
|
|
847
|
+
if (cmd === "c" || cmd === "C") {
|
|
766
848
|
// Cubic bezier: c x1,y1 x2,y2 x3,y3 (relative)
|
|
767
849
|
// or C x1,y1 x2,y2 x3,y3 (absolute)
|
|
768
|
-
const
|
|
850
|
+
const _isRelative = cmd === "c";
|
|
769
851
|
// Store bezier control points for patch construction
|
|
770
|
-
} else if (cmd ===
|
|
852
|
+
} else if (cmd === "l" || cmd === "L") {
|
|
771
853
|
// Line: l dx,dy (relative) or L x,y (absolute)
|
|
772
|
-
const
|
|
854
|
+
const _isRelative = cmd === "l";
|
|
773
855
|
}
|
|
774
856
|
}
|
|
775
857
|
|
|
@@ -787,7 +869,7 @@ export function parseMeshGradient(meshGradientDef) {
|
|
|
787
869
|
gradientUnits,
|
|
788
870
|
gradientTransform,
|
|
789
871
|
x: Number(x),
|
|
790
|
-
y: Number(y)
|
|
872
|
+
y: Number(y),
|
|
791
873
|
};
|
|
792
874
|
}
|
|
793
875
|
|
|
@@ -822,32 +904,32 @@ export function parseMeshGradient(meshGradientDef) {
|
|
|
822
904
|
*/
|
|
823
905
|
export function parseMeshGradientElement(element) {
|
|
824
906
|
const data = {
|
|
825
|
-
x: element.getAttribute(
|
|
826
|
-
y: element.getAttribute(
|
|
827
|
-
type: element.getAttribute(
|
|
828
|
-
gradientUnits: element.getAttribute(
|
|
829
|
-
gradientTransform: element.getAttribute(
|
|
830
|
-
meshrows: []
|
|
907
|
+
x: element.getAttribute("x") || "0",
|
|
908
|
+
y: element.getAttribute("y") || "0",
|
|
909
|
+
type: element.getAttribute("type") || "bilinear",
|
|
910
|
+
gradientUnits: element.getAttribute("gradientUnits") || "userSpaceOnUse",
|
|
911
|
+
gradientTransform: element.getAttribute("gradientTransform"),
|
|
912
|
+
meshrows: [],
|
|
831
913
|
};
|
|
832
914
|
|
|
833
|
-
const meshRows = element.querySelectorAll(
|
|
834
|
-
meshRows.forEach(row => {
|
|
915
|
+
const meshRows = element.querySelectorAll("meshrow");
|
|
916
|
+
meshRows.forEach((row) => {
|
|
835
917
|
const rowData = { meshpatches: [] };
|
|
836
|
-
const meshPatches = row.querySelectorAll(
|
|
918
|
+
const meshPatches = row.querySelectorAll("meshpatch");
|
|
837
919
|
|
|
838
|
-
meshPatches.forEach(patch => {
|
|
920
|
+
meshPatches.forEach((patch) => {
|
|
839
921
|
const patchData = { stops: [] };
|
|
840
|
-
const stops = patch.querySelectorAll(
|
|
922
|
+
const stops = patch.querySelectorAll("stop");
|
|
841
923
|
|
|
842
|
-
stops.forEach(stop => {
|
|
843
|
-
const style = stop.getAttribute(
|
|
924
|
+
stops.forEach((stop) => {
|
|
925
|
+
const style = stop.getAttribute("style") || "";
|
|
844
926
|
const colorMatch = style.match(/stop-color:\s*([^;]+)/);
|
|
845
927
|
const opacityMatch = style.match(/stop-opacity:\s*([^;]+)/);
|
|
846
928
|
|
|
847
929
|
patchData.stops.push({
|
|
848
|
-
path: stop.getAttribute(
|
|
930
|
+
path: stop.getAttribute("path") || "",
|
|
849
931
|
color: colorMatch ? colorMatch[1].trim() : null,
|
|
850
|
-
opacity: opacityMatch ? parseFloat(opacityMatch[1]) : 1
|
|
932
|
+
opacity: opacityMatch ? parseFloat(opacityMatch[1]) : 1,
|
|
851
933
|
});
|
|
852
934
|
});
|
|
853
935
|
|
|
@@ -975,22 +1057,31 @@ function renderPatchQuad(patch, imageData, width, height) {
|
|
|
975
1057
|
for (let y = minY; y <= maxY; y++) {
|
|
976
1058
|
for (let x = minX; x <= maxX; x++) {
|
|
977
1059
|
// Convert pixel to patch (u,v) coordinates
|
|
978
|
-
const u = D(x)
|
|
979
|
-
|
|
1060
|
+
const u = D(x)
|
|
1061
|
+
.minus(bbox.minX)
|
|
1062
|
+
.div(bbox.maxX.minus(bbox.minX) || D(1));
|
|
1063
|
+
const v = D(y)
|
|
1064
|
+
.minus(bbox.minY)
|
|
1065
|
+
.div(bbox.maxY.minus(bbox.minY) || D(1));
|
|
980
1066
|
|
|
981
1067
|
if (u.gte(0) && u.lte(1) && v.gte(0) && v.lte(1)) {
|
|
982
|
-
const { color } = patch.evaluate(u, v);
|
|
1068
|
+
const { color: patchColor } = patch.evaluate(u, v);
|
|
983
1069
|
const idx = (y * width + x) * 4;
|
|
984
1070
|
|
|
985
1071
|
// Alpha blending
|
|
986
|
-
const srcA =
|
|
1072
|
+
const srcA = patchColor.a / 255;
|
|
987
1073
|
const dstA = imageData[idx + 3] / 255;
|
|
988
1074
|
const outA = srcA + dstA * (1 - srcA);
|
|
989
1075
|
|
|
990
1076
|
if (outA > 0) {
|
|
991
|
-
imageData[idx] =
|
|
992
|
-
|
|
993
|
-
imageData[idx +
|
|
1077
|
+
imageData[idx] =
|
|
1078
|
+
(patchColor.r * srcA + imageData[idx] * dstA * (1 - srcA)) / outA;
|
|
1079
|
+
imageData[idx + 1] =
|
|
1080
|
+
(patchColor.g * srcA + imageData[idx + 1] * dstA * (1 - srcA)) /
|
|
1081
|
+
outA;
|
|
1082
|
+
imageData[idx + 2] =
|
|
1083
|
+
(patchColor.b * srcA + imageData[idx + 2] * dstA * (1 - srcA)) /
|
|
1084
|
+
outA;
|
|
994
1085
|
imageData[idx + 3] = outA * 255;
|
|
995
1086
|
}
|
|
996
1087
|
}
|
|
@@ -1082,7 +1173,7 @@ function patchToPolygons(patch, subdivisions) {
|
|
|
1082
1173
|
r: (p00.color.r + p10.color.r + p01.color.r + p11.color.r) / 4,
|
|
1083
1174
|
g: (p00.color.g + p10.color.g + p01.color.g + p11.color.g) / 4,
|
|
1084
1175
|
b: (p00.color.b + p10.color.b + p01.color.b + p11.color.b) / 4,
|
|
1085
|
-
a: (p00.color.a + p10.color.a + p01.color.a + p11.color.a) / 4
|
|
1176
|
+
a: (p00.color.a + p10.color.a + p01.color.a + p11.color.a) / 4,
|
|
1086
1177
|
};
|
|
1087
1178
|
|
|
1088
1179
|
result.push({
|
|
@@ -1090,9 +1181,9 @@ function patchToPolygons(patch, subdivisions) {
|
|
|
1090
1181
|
PolygonClip.point(p00.point.x, p00.point.y),
|
|
1091
1182
|
PolygonClip.point(p10.point.x, p10.point.y),
|
|
1092
1183
|
PolygonClip.point(p11.point.x, p11.point.y),
|
|
1093
|
-
PolygonClip.point(p01.point.x, p01.point.y)
|
|
1184
|
+
PolygonClip.point(p01.point.x, p01.point.y),
|
|
1094
1185
|
],
|
|
1095
|
-
color: avgColor
|
|
1186
|
+
color: avgColor,
|
|
1096
1187
|
});
|
|
1097
1188
|
}
|
|
1098
1189
|
}
|
|
@@ -1151,12 +1242,12 @@ export function clipMeshGradient(meshData, clipPolygon, options = {}) {
|
|
|
1151
1242
|
// Clip each polygon
|
|
1152
1243
|
const clippedPolygons = [];
|
|
1153
1244
|
|
|
1154
|
-
for (const { polygon, color } of meshPolygons) {
|
|
1245
|
+
for (const { polygon, color: polyColor } of meshPolygons) {
|
|
1155
1246
|
const clipped = PolygonClip.polygonIntersection(polygon, clipPolygon);
|
|
1156
1247
|
|
|
1157
1248
|
for (const clippedPoly of clipped) {
|
|
1158
1249
|
if (clippedPoly.length >= 3) {
|
|
1159
|
-
clippedPolygons.push({ polygon: clippedPoly, color });
|
|
1250
|
+
clippedPolygons.push({ polygon: clippedPoly, color: polyColor });
|
|
1160
1251
|
}
|
|
1161
1252
|
}
|
|
1162
1253
|
}
|
|
@@ -1196,8 +1287,8 @@ export function clipMeshGradient(meshData, clipPolygon, options = {}) {
|
|
|
1196
1287
|
* ).join('\n');
|
|
1197
1288
|
*/
|
|
1198
1289
|
export function clippedMeshToSVG(clippedPolygons) {
|
|
1199
|
-
return clippedPolygons.map(({ polygon, color }) => {
|
|
1200
|
-
let pathData =
|
|
1290
|
+
return clippedPolygons.map(({ polygon, color: polyColor }) => {
|
|
1291
|
+
let pathData = "";
|
|
1201
1292
|
for (let i = 0; i < polygon.length; i++) {
|
|
1202
1293
|
const p = polygon[i];
|
|
1203
1294
|
if (i === 0) {
|
|
@@ -1206,9 +1297,9 @@ export function clippedMeshToSVG(clippedPolygons) {
|
|
|
1206
1297
|
pathData += ` L ${Number(p.x).toFixed(6)} ${Number(p.y).toFixed(6)}`;
|
|
1207
1298
|
}
|
|
1208
1299
|
}
|
|
1209
|
-
pathData +=
|
|
1300
|
+
pathData += " Z";
|
|
1210
1301
|
|
|
1211
|
-
const fill = `rgba(${Math.round(
|
|
1302
|
+
const fill = `rgba(${Math.round(polyColor.r)},${Math.round(polyColor.g)},${Math.round(polyColor.b)},${(polyColor.a / 255).toFixed(3)})`;
|
|
1212
1303
|
|
|
1213
1304
|
return { pathData, fill };
|
|
1214
1305
|
});
|