@pixerate/editor 0.115.0 → 0.116.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 (117) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/dist/animation-editor/index.cjs +54 -54
  3. package/dist/animation-editor/index.d.cts +2 -2
  4. package/dist/animation-editor/index.d.ts +2 -2
  5. package/dist/animation-editor/index.js +2 -2
  6. package/dist/{chunk-DFQEX3AM.cjs → chunk-2UT4GXYA.cjs} +133 -8
  7. package/dist/chunk-2UT4GXYA.cjs.map +1 -0
  8. package/dist/{chunk-VXR7XWMF.js → chunk-7LNZAWSJ.js} +437 -41
  9. package/dist/chunk-7LNZAWSJ.js.map +1 -0
  10. package/dist/{chunk-LKCYANMW.js → chunk-ARQ7PQ6Q.js} +129 -4
  11. package/dist/chunk-ARQ7PQ6Q.js.map +1 -0
  12. package/dist/chunk-COAMRKVI.cjs +159 -0
  13. package/dist/chunk-COAMRKVI.cjs.map +1 -0
  14. package/dist/{chunk-C6C5PDKU.js → chunk-D53KSJUV.js} +3 -322
  15. package/dist/chunk-D53KSJUV.js.map +1 -0
  16. package/dist/chunk-D53VQGEJ.js +3262 -0
  17. package/dist/chunk-D53VQGEJ.js.map +1 -0
  18. package/dist/chunk-EJVDBITD.cjs +501 -0
  19. package/dist/chunk-EJVDBITD.cjs.map +1 -0
  20. package/dist/{chunk-LPLCWZTM.cjs → chunk-FODKPD44.cjs} +555 -159
  21. package/dist/chunk-FODKPD44.cjs.map +1 -0
  22. package/dist/chunk-HOCPSFGA.cjs +3262 -0
  23. package/dist/chunk-HOCPSFGA.cjs.map +1 -0
  24. package/dist/chunk-IPFRWILU.js +1908 -0
  25. package/dist/chunk-IPFRWILU.js.map +1 -0
  26. package/dist/{chunk-FV5AXTSF.cjs → chunk-JSSBHMMU.cjs} +36 -188
  27. package/dist/chunk-JSSBHMMU.cjs.map +1 -0
  28. package/dist/chunk-LF4WOZXZ.js +159 -0
  29. package/dist/chunk-LF4WOZXZ.js.map +1 -0
  30. package/dist/{chunk-26PGXHY7.cjs → chunk-M7O4BJRH.cjs} +36 -36
  31. package/dist/{chunk-26PGXHY7.cjs.map → chunk-M7O4BJRH.cjs.map} +1 -1
  32. package/dist/chunk-N3467VD6.js +501 -0
  33. package/dist/chunk-N3467VD6.js.map +1 -0
  34. package/dist/{chunk-ST7E3NEE.cjs → chunk-PAOWHJ5O.cjs} +18 -337
  35. package/dist/chunk-PAOWHJ5O.cjs.map +1 -0
  36. package/dist/{chunk-XICA4PVO.js → chunk-SEBYNBHW.js} +10 -10
  37. package/dist/chunk-SMJGE7HM.cjs +1908 -0
  38. package/dist/chunk-SMJGE7HM.cjs.map +1 -0
  39. package/dist/{chunk-MMVHBKMM.js → chunk-T2P5TIZ6.js} +2 -2
  40. package/dist/{chunk-FGW5OWJ4.js → chunk-VJUML33I.js} +4 -156
  41. package/dist/chunk-VJUML33I.js.map +1 -0
  42. package/dist/{chunk-I7FQBWMQ.cjs → chunk-ZYZGEILO.cjs} +239 -239
  43. package/dist/{chunk-I7FQBWMQ.cjs.map → chunk-ZYZGEILO.cjs.map} +1 -1
  44. package/dist/export/index.cjs +4 -2
  45. package/dist/export/index.cjs.map +1 -1
  46. package/dist/export/index.js +5 -3
  47. package/dist/{export-BJE8T0YA.d.cts → export-BXxSGBCq.d.cts} +2 -2
  48. package/dist/{export-Con3UKkm.d.ts → export-Dm44YUvS.d.ts} +2 -2
  49. package/dist/export-Dv0-ZVrV.d.cts +501 -0
  50. package/dist/export-Dv0-ZVrV.d.ts +501 -0
  51. package/dist/{retouch-Do6tBs2H.d.ts → flow-BMtHAmg4.d.ts} +561 -2
  52. package/dist/{retouch-Bxkuc0KH.d.cts → flow-COCB42TP.d.cts} +561 -2
  53. package/dist/image-editor/index.cjs +957 -795
  54. package/dist/image-editor/index.cjs.map +1 -1
  55. package/dist/image-editor/index.d.cts +6 -6
  56. package/dist/image-editor/index.d.ts +6 -6
  57. package/dist/image-editor/index.js +180 -18
  58. package/dist/image-editor/index.js.map +1 -1
  59. package/dist/index.cjs +4 -2
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.js +5 -3
  62. package/dist/index.js.map +1 -1
  63. package/dist/kernel-wasm-WYCGNZR4.js +10 -0
  64. package/dist/kernel-wasm-WYCGNZR4.js.map +1 -0
  65. package/dist/kernel-wasm-YPVAEMVN.cjs +10 -0
  66. package/dist/kernel-wasm-YPVAEMVN.cjs.map +1 -0
  67. package/dist/paint/index.cjs +165 -3
  68. package/dist/paint/index.cjs.map +1 -1
  69. package/dist/paint/index.d.cts +225 -3
  70. package/dist/paint/index.d.ts +225 -3
  71. package/dist/paint/index.js +166 -4
  72. package/dist/paint-editor/index.cjs +4690 -845
  73. package/dist/paint-editor/index.cjs.map +1 -1
  74. package/dist/paint-editor/index.d.cts +1525 -277
  75. package/dist/paint-editor/index.d.ts +1525 -277
  76. package/dist/paint-editor/index.js +4560 -715
  77. package/dist/paint-editor/index.js.map +1 -1
  78. package/dist/raster/index.cjs +4 -3
  79. package/dist/raster/index.cjs.map +1 -1
  80. package/dist/raster/index.js +10 -9
  81. package/dist/{svg-BnRj0q8i.d.cts → svg-CVdFc80-.d.cts} +2 -2
  82. package/dist/{svg-CmmBFfGT.d.ts → svg-DlEuhwoX.d.ts} +2 -2
  83. package/dist/{types-1SUQKDAw.d.ts → types-CDLGhAZp.d.ts} +40 -2
  84. package/dist/{types-yINF17Ta.d.cts → types-Tfly4Aya.d.cts} +40 -2
  85. package/dist/vector/index.cjs +1897 -975
  86. package/dist/vector/index.cjs.map +1 -1
  87. package/dist/vector/index.d.cts +292 -32
  88. package/dist/vector/index.d.ts +292 -32
  89. package/dist/vector/index.js +928 -6
  90. package/dist/vector/index.js.map +1 -1
  91. package/dist/video-editor/index.cjs +502 -2165
  92. package/dist/video-editor/index.cjs.map +1 -1
  93. package/dist/video-editor/index.d.cts +3 -501
  94. package/dist/video-editor/index.d.ts +3 -501
  95. package/dist/video-editor/index.js +151 -1814
  96. package/dist/video-editor/index.js.map +1 -1
  97. package/package.json +1 -1
  98. package/skills/pixerate-image-editor/references/photoshop-files.md +7 -3
  99. package/skills/pixerate-vector/SKILL.md +3 -0
  100. package/dist/chunk-C6C5PDKU.js.map +0 -1
  101. package/dist/chunk-DFQEX3AM.cjs.map +0 -1
  102. package/dist/chunk-FGW5OWJ4.js.map +0 -1
  103. package/dist/chunk-FV5AXTSF.cjs.map +0 -1
  104. package/dist/chunk-GX332W2C.js +0 -181
  105. package/dist/chunk-GX332W2C.js.map +0 -1
  106. package/dist/chunk-LKCYANMW.js.map +0 -1
  107. package/dist/chunk-LPLCWZTM.cjs.map +0 -1
  108. package/dist/chunk-MUUXNRU4.cjs +0 -181
  109. package/dist/chunk-MUUXNRU4.cjs.map +0 -1
  110. package/dist/chunk-ST7E3NEE.cjs.map +0 -1
  111. package/dist/chunk-TCKIXI46.cjs +0 -1136
  112. package/dist/chunk-TCKIXI46.cjs.map +0 -1
  113. package/dist/chunk-UBIXVXPR.js +0 -1136
  114. package/dist/chunk-UBIXVXPR.js.map +0 -1
  115. package/dist/chunk-VXR7XWMF.js.map +0 -1
  116. /package/dist/{chunk-XICA4PVO.js.map → chunk-SEBYNBHW.js.map} +0 -0
  117. /package/dist/{chunk-MMVHBKMM.js.map → chunk-T2P5TIZ6.js.map} +0 -0
@@ -10,11 +10,15 @@ import {
10
10
  } from "../chunk-3UFGNHSP.js";
11
11
  import {
12
12
  applyHomography,
13
+ decodePng,
13
14
  homographyFromPoints,
14
15
  homographyKeepsQuad,
16
+ isPng,
15
17
  orderQuad,
18
+ parseDataUrl,
16
19
  solve8
17
- } from "../chunk-GX332W2C.js";
20
+ } from "../chunk-N3467VD6.js";
21
+ import "../chunk-6FCKOS2B.js";
18
22
  import {
19
23
  correspondMorphContours,
20
24
  createVectorMorph,
@@ -33,7 +37,7 @@ import {
33
37
  reverseMorphContour,
34
38
  subpathToMorphContour,
35
39
  turnMorphContour
36
- } from "../chunk-MMVHBKMM.js";
40
+ } from "../chunk-T2P5TIZ6.js";
37
41
  import {
38
42
  DEFAULT_VECTOR_FONT,
39
43
  IDENTITY_MATRIX,
@@ -67,6 +71,7 @@ import {
67
71
  VECTOR_HORIZONTAL_RESIZING_LABELS,
68
72
  VECTOR_MASK_TYPES,
69
73
  VECTOR_MERGE_MODES,
74
+ VECTOR_MESH_MAX,
70
75
  VECTOR_PARAGRAPH_STYLE_KEYS,
71
76
  VECTOR_PATHFINDER_OPERATIONS,
72
77
  VECTOR_PATH_TEXT_BASELINES,
@@ -100,6 +105,7 @@ import {
100
105
  arrowheadInset,
101
106
  arrowheadPath,
102
107
  arrowheadWeight,
108
+ autoVectorFreeformGradient,
103
109
  bakeVectorTextGeometry,
104
110
  bezierToSubpaths,
105
111
  boundsFromPoints,
@@ -122,6 +128,7 @@ import {
122
128
  createOutlineTextShaper,
123
129
  createVectorEffect,
124
130
  createVectorFilter,
131
+ createVectorMeshGradient,
125
132
  cssFontFamily,
126
133
  dashIntervals,
127
134
  decomposeRigid,
@@ -182,11 +189,14 @@ import {
182
189
  isVectorConnector,
183
190
  isVectorFilterType,
184
191
  isVectorFrame,
192
+ isVectorFreeformGradient,
185
193
  isVectorGradient,
186
194
  isVectorLiveBoolean,
187
195
  isVectorMaskActive,
188
196
  isVectorMaskGroup,
189
197
  isVectorMaskType,
198
+ isVectorMeshGradient,
199
+ isVectorShadedPaint,
190
200
  knifePath,
191
201
  layoutVectorText,
192
202
  lineSubpath,
@@ -195,6 +205,7 @@ import {
195
205
  mapVectorGradient,
196
206
  mapVectorPaint,
197
207
  mapVectorPoints,
208
+ mapVectorShadedPaint,
198
209
  mapVectorSubpaths,
199
210
  mapWarpMeshPoints,
200
211
  matrixAbout,
@@ -209,6 +220,7 @@ import {
209
220
  nearestSegment,
210
221
  nearestStrokePosition,
211
222
  nearestVectorConnectionSite,
223
+ nearestVectorShadingPoint,
212
224
  needsVectorPaintLayers,
213
225
  newVectorStrokeLayer,
214
226
  nextVectorCornerKind,
@@ -236,6 +248,7 @@ import {
236
248
  pullWarpMesh,
237
249
  rectSubpath,
238
250
  removeAnchors,
251
+ removeVectorFreeformPoint,
239
252
  removeVectorGradientStop,
240
253
  removeWarpMeshSplitU,
241
254
  removeWarpMeshSplitV,
@@ -253,6 +266,7 @@ import {
253
266
  roundSubpaths,
254
267
  routeVectorConnector,
255
268
  samplePlainStops,
269
+ sampleVectorFreeform,
256
270
  sampleVectorGradient,
257
271
  sampleVectorGradientAt,
258
272
  sampleVectorPaintDefAt,
@@ -264,6 +278,7 @@ import {
264
278
  sanitizeVectorGradient,
265
279
  sanitizeVectorMask,
266
280
  sanitizeVectorResizing,
281
+ sanitizeVectorShadedPaint,
267
282
  sanitizeVectorStrokeSettings,
268
283
  sanitizeVectorStrokes,
269
284
  sanitizeVectorTextFields,
@@ -297,6 +312,7 @@ import {
297
312
  transformVectorGradient,
298
313
  transformVectorNode,
299
314
  transformVectorPaint,
315
+ transformVectorShadedPaint,
300
316
  translateAnchor,
301
317
  translateMatrix,
302
318
  trimIntervals,
@@ -338,6 +354,9 @@ import {
338
354
  vectorFrameDocument,
339
355
  vectorFrameDrawn,
340
356
  vectorFrameRectPath,
357
+ vectorFreeformField,
358
+ vectorFreeformLineSegments,
359
+ vectorFreeformSpreadScale,
341
360
  vectorGradientArea,
342
361
  vectorGradientAspect,
343
362
  vectorGradientFocalParam,
@@ -368,8 +387,11 @@ import {
368
387
  vectorMaskedGeometry,
369
388
  vectorMaskedLocalBounds,
370
389
  vectorMaskedShapes,
390
+ vectorMeshColorAt,
371
391
  vectorMeshFromOutline,
392
+ vectorMeshLines,
372
393
  vectorMeshOutline,
394
+ vectorMeshPointAt,
373
395
  vectorNodeBounds,
374
396
  vectorNodeEnvelope,
375
397
  vectorNodeFilterSvg,
@@ -388,6 +410,8 @@ import {
388
410
  vectorPathTextPositions,
389
411
  vectorPathWorldBezier,
390
412
  vectorRepeaterCopies,
413
+ vectorRgbaToCss,
414
+ vectorShadedSvgDefs,
391
415
  vectorSpatialIndex,
392
416
  vectorStacksHaveGradient,
393
417
  vectorStrokeGradientDefs,
@@ -440,7 +464,7 @@ import {
440
464
  withVectorStrokes,
441
465
  withVectorTargetPaint,
442
466
  zigzagPaths
443
- } from "../chunk-VXR7XWMF.js";
467
+ } from "../chunk-7LNZAWSJ.js";
444
468
  import {
445
469
  bezierPathBounds,
446
470
  bezierPathToSvg,
@@ -452,11 +476,13 @@ import {
452
476
  fitCubics,
453
477
  pathBoolean,
454
478
  pathToContours,
479
+ pointInBezierPath,
455
480
  segmentDerivative,
456
481
  segmentIntersections,
457
482
  segmentNearest,
458
483
  segmentPoint,
459
484
  segmentTangent,
485
+ simplifyRing,
460
486
  transformBezierPath
461
487
  } from "../chunk-YCSFS3GK.js";
462
488
  import {
@@ -556,7 +582,11 @@ function resolveVectorBlendOptions(options) {
556
582
  }
557
583
  function paintColors(p) {
558
584
  if (typeof p === "string") return [p];
559
- if (p && typeof p === "object") return p.stops.map((s) => s.color);
585
+ if (p && typeof p === "object") {
586
+ if (p.type === "mesh") return p.colors;
587
+ if (p.type === "freeform") return p.points.map((q) => q.color);
588
+ return p.stops.map((s) => s.color);
589
+ }
560
590
  return [];
561
591
  }
562
592
  function vectorBlendColorDistance(a, b) {
@@ -765,6 +795,293 @@ function releaseVectorBlend(group, newId) {
765
795
  return [...keys, group.transform ? transformVectorNode(path, m) : path];
766
796
  }
767
797
 
798
+ // src/vector/trace.ts
799
+ var VECTOR_TRACE_MAX_PIXELS = 4e6;
800
+ function signedArea(p) {
801
+ let a = 0;
802
+ for (let i = 0; i < p.length; i++) {
803
+ const [x0, y0] = p[i];
804
+ const [x1, y1] = p[(i + 1) % p.length];
805
+ a += x0 * y1 - x1 * y0;
806
+ }
807
+ return a / 2;
808
+ }
809
+ function pointInPoly(p, poly) {
810
+ let inside = false;
811
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
812
+ const [xi, yi] = poly[i];
813
+ const [xj, yj] = poly[j];
814
+ if (yi > p[1] !== yj > p[1] && p[0] < (xj - xi) * (p[1] - yi) / (yj - yi) + xi) inside = !inside;
815
+ }
816
+ return inside;
817
+ }
818
+ function vectorTraceContours(field, w, h, threshold) {
819
+ const gw = w + 2;
820
+ const gh = h + 2;
821
+ const val = (i, j) => i === 0 || j === 0 || i > w || j > h ? -Infinity : field[(j - 1) * w + (i - 1)];
822
+ const inside = (i, j) => val(i, j) >= threshold;
823
+ const pos = (i, j) => [i - 0.5, j - 0.5];
824
+ const cross2 = (a, b) => {
825
+ const va = val(a[0], a[1]);
826
+ const vb = val(b[0], b[1]);
827
+ let t;
828
+ if (Number.isFinite(va) && Number.isFinite(vb) && Math.abs(vb - va) > 1e-12) t = Math.min(1, Math.max(0, (threshold - va) / (vb - va)));
829
+ else if (Number.isFinite(va)) t = va >= threshold ? 0.5 : 0;
830
+ else t = vb >= threshold ? 0.5 : 1;
831
+ const pa = pos(a[0], a[1]);
832
+ const pb = pos(b[0], b[1]);
833
+ return [pa[0] + (pb[0] - pa[0]) * t, pa[1] + (pb[1] - pa[1]) * t];
834
+ };
835
+ const hid = (i, j) => 2 * (j * gw + i);
836
+ const vid = (i, j) => 2 * (j * gw + i) + 1;
837
+ const next = /* @__PURE__ */ new Map();
838
+ const point = /* @__PURE__ */ new Map();
839
+ for (let j = 0; j < gh - 1; j++) {
840
+ for (let i = 0; i < gw - 1; i++) {
841
+ const tl = inside(i, j);
842
+ const tr = inside(i + 1, j);
843
+ const br = inside(i + 1, j + 1);
844
+ const bl = inside(i, j + 1);
845
+ const code = +tl << 3 | +tr << 2 | +br << 1 | +bl;
846
+ if (code === 0 || code === 15) continue;
847
+ const top = hid(i, j);
848
+ const bottom = hid(i, j + 1);
849
+ const left = vid(i, j);
850
+ const right = vid(i + 1, j);
851
+ const seg = (a, b) => next.set(a, b);
852
+ const mean = () => (val(i, j) + val(i + 1, j) + val(i + 1, j + 1) + val(i, j + 1)) / 4;
853
+ switch (code) {
854
+ case 1:
855
+ seg(left, bottom);
856
+ break;
857
+ case 2:
858
+ seg(bottom, right);
859
+ break;
860
+ case 3:
861
+ seg(left, right);
862
+ break;
863
+ case 4:
864
+ seg(right, top);
865
+ break;
866
+ case 5:
867
+ if (mean() >= threshold) {
868
+ seg(left, top);
869
+ seg(right, bottom);
870
+ } else {
871
+ seg(left, bottom);
872
+ seg(right, top);
873
+ }
874
+ break;
875
+ case 6:
876
+ seg(bottom, top);
877
+ break;
878
+ case 7:
879
+ seg(left, top);
880
+ break;
881
+ case 8:
882
+ seg(top, left);
883
+ break;
884
+ case 9:
885
+ seg(top, bottom);
886
+ break;
887
+ case 10:
888
+ if (mean() >= threshold) {
889
+ seg(top, right);
890
+ seg(bottom, left);
891
+ } else {
892
+ seg(top, left);
893
+ seg(bottom, right);
894
+ }
895
+ break;
896
+ case 11:
897
+ seg(top, right);
898
+ break;
899
+ case 12:
900
+ seg(right, left);
901
+ break;
902
+ case 13:
903
+ seg(right, bottom);
904
+ break;
905
+ case 14:
906
+ seg(bottom, left);
907
+ break;
908
+ }
909
+ const edges = [
910
+ [top, [i, j], [i + 1, j]],
911
+ [bottom, [i, j + 1], [i + 1, j + 1]],
912
+ [left, [i, j], [i, j + 1]],
913
+ [right, [i + 1, j], [i + 1, j + 1]]
914
+ ];
915
+ for (const [e, a, b] of edges) if (inside(a[0], a[1]) !== inside(b[0], b[1]) && !point.has(e)) point.set(e, cross2(a, b));
916
+ }
917
+ }
918
+ const out = [];
919
+ for (const start of [...next.keys()].sort((a, b) => a - b)) {
920
+ if (!next.has(start)) continue;
921
+ const poly = [];
922
+ let e = start;
923
+ for (; ; ) {
924
+ const n = next.get(e);
925
+ if (n === void 0) break;
926
+ next.delete(e);
927
+ const p = point.get(e);
928
+ if (p) poly.push(p);
929
+ e = n;
930
+ if (e === start) break;
931
+ }
932
+ if (poly.length >= 3) out.push(poly);
933
+ }
934
+ return out;
935
+ }
936
+ function loopsOf(field, w, h, threshold, tolerance, minArea) {
937
+ const loops = [];
938
+ for (const raw of vectorTraceContours(field, w, h, threshold)) {
939
+ const area = signedArea(raw);
940
+ if (Math.abs(area) < Math.max(0, minArea)) continue;
941
+ const simple = simplifyRing(raw, Math.max(0.01, tolerance));
942
+ if (simple.length < 3) continue;
943
+ loops.push({ raw, simple, area, depth: 0 });
944
+ }
945
+ for (const l of loops) l.depth = loops.filter((o) => o !== l && Math.abs(o.area) > Math.abs(l.area) && pointInPoly(l.simple[0], o.simple)).length;
946
+ return loops.sort((a, b) => a.depth - b.depth || Math.abs(b.area) - Math.abs(a.area));
947
+ }
948
+ function loopSubpath(l, tolerance, roundness) {
949
+ if (roundness <= 0) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
950
+ const cornerAngleDeg = 25 + roundness * 125;
951
+ const cubics = fitCubics(l.raw, Math.max(0.05, tolerance), {
952
+ closed: true,
953
+ cornerAngleDeg
954
+ });
955
+ if (!cubics.length) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
956
+ const anchors = cubics.map((c, k) => {
957
+ const prev = cubics[(k - 1 + cubics.length) % cubics.length];
958
+ return {
959
+ x: c.p0[0],
960
+ y: c.p0[1],
961
+ in: { x: prev.p2[0], y: prev.p2[1] },
962
+ out: { x: c.p1[0], y: c.p1[1] }
963
+ };
964
+ });
965
+ return { closed: true, anchors };
966
+ }
967
+ function traceField(field, w, h, threshold, o) {
968
+ const tolerance = o.tolerance ?? 1;
969
+ const roundness = Math.min(1, Math.max(0, o.cornerRoundness ?? 0.5));
970
+ return loopsOf(field, w, h, threshold, tolerance, o.minArea ?? 10).map((l) => loopSubpath(l, tolerance, roundness));
971
+ }
972
+ function quantizeVectorTraceColors(pixels, count) {
973
+ const n = pixels.width * pixels.height;
974
+ const d = pixels.data;
975
+ const opaque = [];
976
+ for (let i = 0; i < n; i++) if (d[i * 4 + 3] >= 128) opaque.push(i);
977
+ const boxes = opaque.length ? [opaque] : [];
978
+ const range = (box, c) => {
979
+ let lo = 255;
980
+ let hi = 0;
981
+ for (const i of box) {
982
+ const v = d[i * 4 + c];
983
+ if (v < lo) lo = v;
984
+ if (v > hi) hi = v;
985
+ }
986
+ return hi - lo;
987
+ };
988
+ while (boxes.length < count) {
989
+ let best = -1;
990
+ let bestRange = 0;
991
+ let bestC = 0;
992
+ boxes.forEach((b, k) => {
993
+ if (b.length < 2) return;
994
+ for (let c = 0; c < 3; c++) {
995
+ const r = range(b, c);
996
+ if (r > bestRange) {
997
+ bestRange = r;
998
+ best = k;
999
+ bestC = c;
1000
+ }
1001
+ }
1002
+ });
1003
+ if (best < 0) break;
1004
+ const box = boxes[best].slice().sort((a, b) => d[a * 4 + bestC] - d[b * 4 + bestC]);
1005
+ const key = (k) => d[box[k] * 4 + bestC];
1006
+ const v = key(box.length >> 1);
1007
+ let mid = box.findIndex((_, k) => key(k) >= v);
1008
+ if (mid <= 0) mid = box.findIndex((_, k) => key(k) > v);
1009
+ boxes.splice(best, 1, box.slice(0, mid), box.slice(mid));
1010
+ }
1011
+ const palette = boxes.map((b) => {
1012
+ const s = [0, 0, 0];
1013
+ for (const i of b) for (let c = 0; c < 3; c++) s[c] += d[i * 4 + c];
1014
+ return [Math.round(s[0] / b.length), Math.round(s[1] / b.length), Math.round(s[2] / b.length)];
1015
+ });
1016
+ const index = new Int16Array(n).fill(-1);
1017
+ const counts = palette.map(() => 0);
1018
+ for (const i of opaque) {
1019
+ let best = 0;
1020
+ let bestD = Infinity;
1021
+ palette.forEach((p, k) => {
1022
+ const dd = (d[i * 4] - p[0]) ** 2 + (d[i * 4 + 1] - p[1]) ** 2 + (d[i * 4 + 2] - p[2]) ** 2;
1023
+ if (dd < bestD) {
1024
+ bestD = dd;
1025
+ best = k;
1026
+ }
1027
+ });
1028
+ index[i] = best;
1029
+ counts[best]++;
1030
+ }
1031
+ return { palette, index, counts };
1032
+ }
1033
+ var hex = (c) => `#${c.map((v) => Math.round(v).toString(16).padStart(2, "0")).join("")}`;
1034
+ function traceVectorImage(pixels, options = {}) {
1035
+ const { width: w, height: h, data } = pixels;
1036
+ if (!(w > 0 && h > 0) || data.length < w * h * 4) throw new Error("The image has no pixels (or fewer than width \xD7 height \xD7 4 values).");
1037
+ if (w * h > VECTOR_TRACE_MAX_PIXELS) throw new Error(`The image has ${w * h} pixels; scale it down to at most ${VECTOR_TRACE_MAX_PIXELS} to trace it.`);
1038
+ const n = w * h;
1039
+ if ((options.mode ?? "threshold") === "threshold") {
1040
+ const field = new Float32Array(n);
1041
+ const byAlpha = options.channel === "alpha";
1042
+ for (let i = 0; i < n; i++) {
1043
+ const a = data[i * 4 + 3] / 255;
1044
+ field[i] = byAlpha ? a : a * (1 - (0.2126 * data[i * 4] + 0.7152 * data[i * 4 + 1] + 0.0722 * data[i * 4 + 2]) / 255);
1045
+ }
1046
+ const subpaths = traceField(field, w, h, Math.min(1, Math.max(0, options.threshold ?? 0.5)), options);
1047
+ return subpaths.length ? [
1048
+ {
1049
+ id: "trace-1",
1050
+ type: "path",
1051
+ name: "Trace",
1052
+ fill: options.color ?? "#000000",
1053
+ stroke: null,
1054
+ fillRule: "evenodd",
1055
+ subpaths
1056
+ }
1057
+ ] : [];
1058
+ }
1059
+ const count = Math.min(32, Math.max(2, Math.round(options.colors ?? 6)));
1060
+ const q = quantizeVectorTraceColors(pixels, count);
1061
+ const order = q.palette.map((_, k) => k).filter((k) => q.counts[k] > 0).sort((a, b) => q.counts[b] - q.counts[a]);
1062
+ const rank = new Map(order.map((k, r) => [k, r]));
1063
+ const out = [];
1064
+ order.forEach((k, r) => {
1065
+ const field = new Uint8Array(n);
1066
+ for (let i = 0; i < n; i++) {
1067
+ const ix = q.index[i];
1068
+ field[i] = ix >= 0 && rank.get(ix) >= r ? 1 : 0;
1069
+ }
1070
+ const subpaths = traceField(field, w, h, 0.5, options);
1071
+ if (subpaths.length)
1072
+ out.push({
1073
+ id: `trace-${out.length + 1}`,
1074
+ type: "path",
1075
+ name: hex(q.palette[k]),
1076
+ fill: hex(q.palette[k]),
1077
+ stroke: null,
1078
+ fillRule: "evenodd",
1079
+ subpaths
1080
+ });
1081
+ });
1082
+ return out;
1083
+ }
1084
+
768
1085
  // src/vector/constraints/lm.ts
769
1086
  var MAX_ROWS = 800;
770
1087
  var maxAbs = (v) => v.reduce((m, a) => Number.isFinite(a) ? Math.max(m, Math.abs(a)) : Infinity, 0);
@@ -4403,6 +4720,7 @@ var VectorEditorController = class _VectorEditorController {
4403
4720
  constraintIssues: this.constraintIssues,
4404
4721
  trackingPoints: this.trackingPoints,
4405
4722
  typedEntry: this.typed,
4723
+ shadingPoint: this.getShadingSelection(),
4406
4724
  canUndo: h.canUndo,
4407
4725
  canRedo: h.canRedo,
4408
4726
  undoLabel: h.undoName,
@@ -5100,6 +5418,14 @@ var VectorEditorController = class _VectorEditorController {
5100
5418
  }
5101
5419
  downGradient(screen, p, input) {
5102
5420
  const before = this.snapshot();
5421
+ const shaded = this.shadedUnder(screen);
5422
+ if (shaded) {
5423
+ this.shadingSel = shaded.index >= 0 ? { nodeId: shaded.nodeId, index: shaded.index } : null;
5424
+ this.drag = { kind: "shading", nodeId: shaded.nodeId, index: shaded.index, start: screen, before, moved: false, alt: !!input.alt };
5425
+ this.interaction = { type: "gradient", part: "handle" };
5426
+ this.notify();
5427
+ return;
5428
+ }
5103
5429
  const a = this.getGradientAnnotator();
5104
5430
  let part = a ? hitVectorGradientAnnotator(a, screen) : null;
5105
5431
  if (a && part) {
@@ -6698,6 +7024,306 @@ var VectorEditorController = class _VectorEditorController {
6698
7024
  return { ok: true, id };
6699
7025
  }
6700
7026
  // -----------------------------------------------------------------------------------------
7027
+ // Image tracing
7028
+ /**
7029
+ * Image Trace: traces RGBA `pixels` into paths (see `traceVectorImage`) and adds them as one
7030
+ * group, the image placed with its top-left at `x, y` (default 0, 0) and `width` wide (default:
7031
+ * one unit per pixel; the height follows). One undo step; selects the group. Returns its id and
7032
+ * the traced paths' ids, bottom to top.
7033
+ */
7034
+ traceImage(pixels, options = {}) {
7035
+ if (options.threshold !== void 0 && !(options.threshold >= 0 && options.threshold <= 1)) return { ok: false, message: "threshold must be from 0 to 1." };
7036
+ if (options.colors !== void 0 && !(Number.isInteger(options.colors) && options.colors >= 2 && options.colors <= 32)) return { ok: false, message: "colors must be a whole number from 2 to 32." };
7037
+ if (options.tolerance !== void 0 && !(options.tolerance > 0)) return { ok: false, message: "tolerance must be above 0 (pixels)." };
7038
+ if (options.width !== void 0 && !(options.width > 0)) return { ok: false, message: "width must be above 0." };
7039
+ if (options.color !== void 0 && !parseVectorColor(options.color)) return { ok: false, message: `"${options.color}" is not a colour.` };
7040
+ let traced;
7041
+ try {
7042
+ traced = traceVectorImage(pixels, options);
7043
+ } catch (e) {
7044
+ return { ok: false, message: e.message };
7045
+ }
7046
+ if (!traced.length) return { ok: false, message: "Nothing to trace: no shape reached the threshold (try another threshold, the alpha channel, or colour mode)." };
7047
+ const k = (options.width ?? pixels.width) / pixels.width;
7048
+ const m = [k, 0, 0, k, options.x ?? 0, options.y ?? 0];
7049
+ const children = traced.map((n) => ({ ...transformVectorNode(n, m), id: this.newId() }));
7050
+ const id = this.newId();
7051
+ this.change("Image Trace", () => {
7052
+ this.setDoc([...this.doc.nodes, { id, type: "group", name: options.name ?? "Image Trace", children }]);
7053
+ this.selection = [id];
7054
+ this.anchors = [];
7055
+ this.editingId = null;
7056
+ });
7057
+ return { ok: true, id, ids: children.map((c) => c.id) };
7058
+ }
7059
+ // -----------------------------------------------------------------------------------------
7060
+ // Gradient meshes and freeform gradients
7061
+ /** The selected point of a mesh or freeform fill (Gradient tool), or null. */
7062
+ shadingSel = null;
7063
+ /** Selected paths with a mesh or freeform fill. */
7064
+ shadedPaths() {
7065
+ return this.gradientPaths().filter((n) => isVectorShadedPaint(n.fill));
7066
+ }
7067
+ /** The shaded fill's point under `screen` (index), or a freeform shape under it (index -1), or null. */
7068
+ shadedUnder(screen) {
7069
+ const p = this.screenToDocument(screen);
7070
+ for (const n of this.shadedPaths()) {
7071
+ const world = this.worldOf(n.id);
7072
+ const fill = n.fill;
7073
+ const z = this.viewport.zoom;
7074
+ let best = -1;
7075
+ let bestD = 8;
7076
+ fill.points.forEach((q, k) => {
7077
+ const w = applyMatrix(world, q);
7078
+ const d = Math.hypot(w.x - p.x, w.y - p.y) * z;
7079
+ if (d <= bestD) {
7080
+ best = k;
7081
+ bestD = d;
7082
+ }
7083
+ });
7084
+ if (best >= 0) return { nodeId: n.id, index: best };
7085
+ if (isVectorFreeformGradient(fill) && hitTestVectorNode(n, p, 0, this.parentMatrixOf(n.id))) return { nodeId: n.id, index: -1 };
7086
+ }
7087
+ return null;
7088
+ }
7089
+ parentMatrixOf(id) {
7090
+ return vectorParentMatrix(this.doc.nodes, id) ?? IDENTITY_MATRIX;
7091
+ }
7092
+ moveShading(drag, screen, p) {
7093
+ if (drag.index < 0) return;
7094
+ if (!drag.moved && Math.hypot(screen.x - drag.start.x, screen.y - drag.start.y) < 3) return;
7095
+ drag.moved = true;
7096
+ const node = this.getNode(drag.nodeId);
7097
+ if (!node || !isVectorShadedPaint(node.fill)) return;
7098
+ const local = this.toLocal(drag.nodeId, p);
7099
+ const fill = mapVectorShadedPaint(node.fill, (q) => q);
7100
+ const moved = isVectorMeshGradient(fill) ? { ...fill, points: fill.points.map((q, k) => k === drag.index ? { x: local.x, y: local.y } : q) } : { ...fill, points: fill.points.map((q, k) => k === drag.index ? { ...q, x: local.x, y: local.y } : q) };
7101
+ this.replaceNode(drag.nodeId, normalizeVectorPaintStacks({ ...node, fill: moved }, "shorthand"));
7102
+ this.notify(true);
7103
+ }
7104
+ upShading(drag) {
7105
+ if (drag.moved) return this.commit(this.getNode(drag.nodeId) && isVectorMeshGradient(this.getNode(drag.nodeId).fill) ? "Move Mesh Point" : "Move Freeform Point", drag.before);
7106
+ const at = this.screenToDocument(drag.start);
7107
+ if (drag.index < 0) {
7108
+ const r = this.addFreeformPoint(drag.nodeId, at);
7109
+ if (r.ok) {
7110
+ this.shadingSel = { nodeId: drag.nodeId, index: r.index };
7111
+ this.notify();
7112
+ }
7113
+ return;
7114
+ }
7115
+ if (drag.alt && isVectorFreeformGradient(this.getNode(drag.nodeId).fill)) {
7116
+ if (this.removeFreeformPoint(drag.nodeId, drag.index).ok) {
7117
+ this.shadingSel = null;
7118
+ this.notify();
7119
+ }
7120
+ return;
7121
+ }
7122
+ this.notify();
7123
+ }
7124
+ /** The selected point of a mesh or freeform fill (Gradient tool), with its paint's kind, or null. */
7125
+ getShadingSelection() {
7126
+ const sel = this.shadingSel;
7127
+ const node = sel ? this.getNode(sel.nodeId) : void 0;
7128
+ if (!sel || node?.type !== "path" || !isVectorShadedPaint(node.fill) || sel.index >= node.fill.points.length) return null;
7129
+ return { ...sel, kind: node.fill.type };
7130
+ }
7131
+ /** The path `id` (or a message why it cannot take a mesh or freeform fill). */
7132
+ shadingTarget(id) {
7133
+ const n = this.getNode(id);
7134
+ if (!n) return `There is no node "${id}".`;
7135
+ if (n.type !== "path") return `"${id}" is a ${n.type}: meshes and freeform gradients fill paths.`;
7136
+ if (n.locked) return `"${id}" is locked; unlock it first.`;
7137
+ return n;
7138
+ }
7139
+ /** Sets each node's fill in one undo step. */
7140
+ setFills(label, fills, merge) {
7141
+ this.change(
7142
+ label,
7143
+ () => {
7144
+ this.setDoc(mapTree(this.doc.nodes, (n) => fills.has(n.id) && n.type === "path" ? normalizeVectorPaintStacks({ ...n, fill: fills.get(n.id) }, "shorthand") : n));
7145
+ },
7146
+ merge
7147
+ );
7148
+ }
7149
+ /**
7150
+ * Gives paths (default: the selection) a gradient mesh over their own bounds: `rows` ×
7151
+ * `columns` patches (1–32) with points evenly spread, coloured by `colors` (one per point, row by
7152
+ * row), the four `corners` (top-left, top-right, bottom-right, bottom-left) blended, or the
7153
+ * current fill colour. One undo step.
7154
+ */
7155
+ setMeshGradient(input, options = {}) {
7156
+ const { rows, columns } = input;
7157
+ if (!Number.isInteger(rows) || !Number.isInteger(columns) || rows < 1 || columns < 1 || rows > VECTOR_MESH_MAX || columns > VECTOR_MESH_MAX)
7158
+ return { ok: false, message: `rows and columns must be whole numbers from 1 to ${VECTOR_MESH_MAX}.` };
7159
+ const n = (rows + 1) * (columns + 1);
7160
+ if (input.colors && input.colors.length !== n) return { ok: false, message: `colors needs ${n} colours ((rows + 1) \xD7 (columns + 1), row by row); got ${input.colors.length}.` };
7161
+ if (input.corners && input.corners.length !== 4) return { ok: false, message: "corners needs four colours: top-left, top-right, bottom-right, bottom-left." };
7162
+ const bad = [...input.colors ?? [], ...input.corners ?? []].find((c) => !parseVectorColor(c));
7163
+ if (bad !== void 0) return { ok: false, message: `"${bad}" is not a colour.` };
7164
+ const ids = options.ids ?? this.selection;
7165
+ if (!ids.length) return { ok: false, message: "Select a path first." };
7166
+ const fills = /* @__PURE__ */ new Map();
7167
+ for (const id of ids) {
7168
+ const t = this.shadingTarget(id);
7169
+ if (typeof t === "string") return { ok: false, message: t };
7170
+ const b = vectorPathLocalBounds(t);
7171
+ if (!b || !(b.width > 0 && b.height > 0)) return { ok: false, message: `"${id}" has no area to fill.` };
7172
+ const base = typeof t.fill === "string" && parseVectorColor(t.fill) ? t.fill : "#ffffff";
7173
+ fills.set(id, createVectorMeshGradient(b, rows, columns, input.colors ? { colors: input.colors } : input.corners ? { corners: input.corners } : { colors: Array.from({ length: n }, () => base) }));
7174
+ }
7175
+ this.setFills("Gradient Mesh", fills);
7176
+ return { ok: true };
7177
+ }
7178
+ /**
7179
+ * Gives paths (default: the selection) a freeform gradient: `points` in document units (each
7180
+ * `{ x, y, color, opacity?, spread? }`) with optional `lines` through them, or, without points,
7181
+ * four or more points placed inside each path and coloured along `colors` (default the current
7182
+ * fill to white). One undo step.
7183
+ */
7184
+ setFreeformGradient(input = {}, options = {}) {
7185
+ const ids = options.ids ?? this.selection;
7186
+ if (!ids.length) return { ok: false, message: "Select a path first." };
7187
+ const fills = /* @__PURE__ */ new Map();
7188
+ for (const id of ids) {
7189
+ const t = this.shadingTarget(id);
7190
+ if (typeof t === "string") return { ok: false, message: t };
7191
+ const b = vectorPathLocalBounds(t);
7192
+ if (!b) return { ok: false, message: `"${id}" has no area to fill.` };
7193
+ let f;
7194
+ if (input.points?.length) {
7195
+ f = sanitizeVectorShadedPaint({ type: "freeform", points: input.points.map((q) => ({ ...q, ...this.toLocal(id, q) })), ...input.lines ? { lines: input.lines } : {} });
7196
+ if (!f) return { ok: false, message: "Each point needs x, y (numbers) and a colour." };
7197
+ if (input.lines && (f.lines?.length ?? 0) !== input.lines.length) return { ok: false, message: `Each line needs two or more point indices from 0 to ${input.points.length - 1}, none twice in a row.` };
7198
+ } else {
7199
+ const bad = (input.colors ?? []).find((c) => !parseVectorColor(c));
7200
+ if (bad !== void 0) return { ok: false, message: `"${bad}" is not a colour.` };
7201
+ const outline = subpathsToBezier(t.subpaths);
7202
+ const inside = (q) => pointInBezierPath(outline, [q.x, q.y], t.fillRule ?? "nonzero");
7203
+ const base = typeof t.fill === "string" && parseVectorColor(t.fill) ? t.fill : "#000000";
7204
+ f = autoVectorFreeformGradient(b, input.colors?.length ? input.colors : [base, "#ffffff"], inside);
7205
+ }
7206
+ fills.set(id, f);
7207
+ }
7208
+ this.setFills("Freeform Gradient", fills);
7209
+ return { ok: true };
7210
+ }
7211
+ /** The mesh or freeform fill of path `id` with its points in document units, or null. */
7212
+ getShading(id) {
7213
+ const n = this.getNode(id);
7214
+ if (n?.type !== "path" || !isVectorShadedPaint(n.fill)) return null;
7215
+ const world = this.worldOf(id);
7216
+ return mapVectorShadedPaint(n.fill, (q) => applyMatrix(world, q));
7217
+ }
7218
+ /** Checks a shading point edit: the path, its shaded fill and the index. */
7219
+ shadingPoint(id, index, kind) {
7220
+ const t = this.shadingTarget(id);
7221
+ if (typeof t === "string") return t;
7222
+ const paint = t.fill;
7223
+ if (kind === "mesh" ? !isVectorMeshGradient(paint) : !isVectorFreeformGradient(paint))
7224
+ return `"${id}" has no ${kind === "mesh" ? "gradient mesh" : "freeform gradient"} fill (add one with ${kind === "mesh" ? "vector.setMeshGradient" : "vector.setFreeformGradient"}).`;
7225
+ const p = paint;
7226
+ if (!Number.isInteger(index) || index < 0 || index >= p.points.length) return `Point ${index} is out of range: the ${kind} has ${p.points.length} points (0 to ${p.points.length - 1}).`;
7227
+ return { node: t, paint: p };
7228
+ }
7229
+ /**
7230
+ * Changes point `index` (row by row) of path `id`'s gradient mesh: moves it to `x, y` (document
7231
+ * units) and / or sets its `color` and `opacity`. One undo step (merged while `merge` is the same key).
7232
+ */
7233
+ setMeshPoint(id, index, patch, options = {}) {
7234
+ const r = this.shadingPoint(id, index, "mesh");
7235
+ if (typeof r === "string") return { ok: false, message: r };
7236
+ if (patch.color !== void 0 && !parseVectorColor(patch.color)) return { ok: false, message: `"${patch.color}" is not a colour.` };
7237
+ if (patch.x === void 0 !== (patch.y === void 0)) return { ok: false, message: "Give both x and y to move the point." };
7238
+ const m = r.paint;
7239
+ const at = patch.x !== void 0 ? this.toLocal(id, { x: patch.x, y: patch.y }) : m.points[index];
7240
+ const next = {
7241
+ ...m,
7242
+ points: m.points.map((q, k) => k === index ? { x: at.x, y: at.y } : q),
7243
+ colors: m.colors.map((c, k) => k === index && patch.color !== void 0 ? patch.color : c)
7244
+ };
7245
+ if (patch.opacity !== void 0) {
7246
+ const ops = m.opacities ?? m.points.map(() => 1);
7247
+ next.opacities = ops.map((o, k) => k === index ? Math.min(1, Math.max(0, patch.opacity)) : o);
7248
+ }
7249
+ this.setFills("Edit Mesh Point", /* @__PURE__ */ new Map([[id, next]]), options.merge);
7250
+ return { ok: true };
7251
+ }
7252
+ /** Adds a point to path `id`'s freeform gradient at `x, y` (document units), coloured `color` (default: the colour there now). One undo step. Returns its index. */
7253
+ addFreeformPoint(id, point) {
7254
+ const t = this.shadingTarget(id);
7255
+ if (typeof t === "string") return { ok: false, message: t };
7256
+ if (!isVectorFreeformGradient(t.fill)) return { ok: false, message: `"${id}" has no freeform gradient fill (add one with vector.setFreeformGradient).` };
7257
+ if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return { ok: false, message: "x and y must be numbers." };
7258
+ if (point.color !== void 0 && !parseVectorColor(point.color)) return { ok: false, message: `"${point.color}" is not a colour.` };
7259
+ const local = this.toLocal(id, point);
7260
+ const f = t.fill;
7261
+ const b = vectorPathLocalBounds(t);
7262
+ const here = sampleVectorFreeform(vectorFreeformField(f, b ? vectorFreeformSpreadScale(b) : 1), local);
7263
+ const next = {
7264
+ ...f,
7265
+ points: [
7266
+ ...f.points,
7267
+ {
7268
+ x: local.x,
7269
+ y: local.y,
7270
+ color: point.color ?? vectorRgbaToCss([here[0], here[1], here[2], 1]),
7271
+ ...point.opacity !== void 0 && point.opacity < 1 ? { opacity: Math.max(0, point.opacity) } : {},
7272
+ ...point.spread ? { spread: Math.min(1, Math.max(0, point.spread)) } : {}
7273
+ }
7274
+ ]
7275
+ };
7276
+ this.setFills("Add Freeform Point", /* @__PURE__ */ new Map([[id, next]]));
7277
+ return { ok: true, index: next.points.length - 1 };
7278
+ }
7279
+ /** Changes point `index` of path `id`'s freeform gradient: position (document units), colour, opacity, spread. One undo step (merged while `merge` is the same key). */
7280
+ setFreeformPoint(id, index, patch, options = {}) {
7281
+ const r = this.shadingPoint(id, index, "freeform");
7282
+ if (typeof r === "string") return { ok: false, message: r };
7283
+ if (patch.color !== void 0 && !parseVectorColor(patch.color)) return { ok: false, message: `"${patch.color}" is not a colour.` };
7284
+ if (patch.x === void 0 !== (patch.y === void 0)) return { ok: false, message: "Give both x and y to move the point." };
7285
+ const f = r.paint;
7286
+ const at = patch.x !== void 0 ? this.toLocal(id, { x: patch.x, y: patch.y }) : null;
7287
+ const next = {
7288
+ ...f,
7289
+ points: f.points.map((q, k) => {
7290
+ if (k !== index) return q;
7291
+ const out = { ...q, ...at ? { x: at.x, y: at.y } : {}, ...patch.color !== void 0 ? { color: patch.color } : {} };
7292
+ if (patch.opacity !== void 0) {
7293
+ if (patch.opacity >= 1) delete out.opacity;
7294
+ else out.opacity = Math.max(0, patch.opacity);
7295
+ }
7296
+ if (patch.spread !== void 0) {
7297
+ if (patch.spread <= 0) delete out.spread;
7298
+ else out.spread = Math.min(1, patch.spread);
7299
+ }
7300
+ return out;
7301
+ })
7302
+ };
7303
+ this.setFills("Edit Freeform Point", /* @__PURE__ */ new Map([[id, next]]), options.merge);
7304
+ return { ok: true };
7305
+ }
7306
+ /** Removes point `index` of path `id`'s freeform gradient (never the last one); lines through it close up. One undo step. */
7307
+ removeFreeformPoint(id, index) {
7308
+ const r = this.shadingPoint(id, index, "freeform");
7309
+ if (typeof r === "string") return { ok: false, message: r };
7310
+ const next = removeVectorFreeformPoint(r.paint, index);
7311
+ if (!next) return { ok: false, message: "A freeform gradient keeps at least one point." };
7312
+ this.setFills("Remove Freeform Point", /* @__PURE__ */ new Map([[id, next]]));
7313
+ return { ok: true };
7314
+ }
7315
+ /** Replaces the lines of path `id`'s freeform gradient: each a list of two or more point indices it runs through. One undo step. */
7316
+ setFreeformLines(id, lines) {
7317
+ const t = this.shadingTarget(id);
7318
+ if (typeof t === "string") return { ok: false, message: t };
7319
+ if (!isVectorFreeformGradient(t.fill)) return { ok: false, message: `"${id}" has no freeform gradient fill (add one with vector.setFreeformGradient).` };
7320
+ const f = sanitizeVectorShadedPaint({ ...t.fill, lines });
7321
+ if ((f.lines?.length ?? 0) !== lines.length) return { ok: false, message: `Each line needs two or more point indices from 0 to ${t.fill.points.length - 1}, none twice in a row.` };
7322
+ const { lines: _l, ...rest } = t.fill;
7323
+ this.setFills("Freeform Lines", /* @__PURE__ */ new Map([[id, lines.length ? { ...rest, lines: f.lines } : rest]]));
7324
+ return { ok: true };
7325
+ }
7326
+ // -----------------------------------------------------------------------------------------
6701
7327
  // Object snap tracking and typed entry (after cadcraft's snap/tracking.rs and its direct
6702
7328
  // distance entry)
6703
7329
  trackingPoints = [];
@@ -8914,6 +9540,8 @@ var VectorEditorController = class _VectorEditorController {
8914
9540
  return;
8915
9541
  case "gradient":
8916
9542
  return this.moveGradient(drag, screen, p, input);
9543
+ case "shading":
9544
+ return this.moveShading(drag, screen, p);
8917
9545
  case "envelope":
8918
9546
  return this.moveEnvelope(drag, p);
8919
9547
  case "move": {
@@ -9316,6 +9944,8 @@ var VectorEditorController = class _VectorEditorController {
9316
9944
  return this.upBuilder(drag);
9317
9945
  case "gradient":
9318
9946
  return this.upGradient(drag);
9947
+ case "shading":
9948
+ return this.upShading(drag);
9319
9949
  case "stroke": {
9320
9950
  this.notify();
9321
9951
  if (drag.tool === "knife") this.knife(drag.points);
@@ -11109,6 +11739,40 @@ function outlinePaths(node, parent, entered) {
11109
11739
  if ((hasOwnVectorAppearance(node) || isVectorMaskActive(node)) && !entered.has(node.id)) return [transformBezierPath(vectorAppearanceOutline(node), m)];
11110
11740
  return node.children.filter((c) => c.visible !== false).flatMap((c) => outlinePaths(c, m, entered));
11111
11741
  }
11742
+ function shadingOverlay(state) {
11743
+ if (state.tool !== "gradient") return [];
11744
+ const out = [];
11745
+ const seen = /* @__PURE__ */ new Set();
11746
+ for (const id of state.selection) {
11747
+ const root = findVectorNode(state.document.nodes, id);
11748
+ if (!root) continue;
11749
+ for (const n of collectPathNodes(root)) {
11750
+ if (seen.has(n.id) || !isVectorShadedPaint(n.fill)) continue;
11751
+ seen.add(n.id);
11752
+ const world = vectorWorldMatrix(state.document.nodes, n.id);
11753
+ if (!world) continue;
11754
+ const sc = (q) => toScreen(state, applyMatrix(world, q));
11755
+ const curve = (c) => {
11756
+ const [a, b, d, e] = c.map(sc);
11757
+ return `M${fmt2(a.x)} ${fmt2(a.y)}C${fmt2(b.x)} ${fmt2(b.y)} ${fmt2(d.x)} ${fmt2(d.y)} ${fmt2(e.x)} ${fmt2(e.y)}`;
11758
+ };
11759
+ const fill = n.fill;
11760
+ const lines = [];
11761
+ if (isVectorMeshGradient(fill)) {
11762
+ const l = vectorMeshLines(fill);
11763
+ for (const row of [...l.rows, ...l.columns]) lines.push(row.map(curve).join(""));
11764
+ } else (fill.lines ?? []).forEach((_, k) => lines.push(vectorFreeformLineSegments(fill, k).map(curve).join("")));
11765
+ const sel = state.shadingPoint && state.shadingPoint.nodeId === n.id ? state.shadingPoint.index : -1;
11766
+ out.push({
11767
+ nodeId: n.id,
11768
+ kind: fill.type,
11769
+ lines,
11770
+ points: fill.points.map((q, index) => ({ index, ...sc(q), color: isVectorMeshGradient(fill) ? fill.colors[index] : fill.points[index].color, selected: index === sel }))
11771
+ });
11772
+ }
11773
+ }
11774
+ return out;
11775
+ }
11112
11776
  function connectorSiteOverlay(state) {
11113
11777
  if (state.tool !== "connector" || !state.pointer) return [];
11114
11778
  const drawing = state.interaction?.type === "draw" && state.interaction.tool === "connector" ? state.interaction.nodeId : void 0;
@@ -11380,6 +12044,7 @@ function getVectorOverlayRenderData(state) {
11380
12044
  constraints: constraintOverlay(state),
11381
12045
  frames: frameLabels(state),
11382
12046
  connectorSites: connectorSiteOverlay(state),
12047
+ shading: shadingOverlay(state),
11383
12048
  trackingPoints: (state.trackingPoints ?? []).map((p) => toScreen(state, p)),
11384
12049
  typedEntry: state.typedEntry ? { text: state.typedEntry, ...state.pointer ? toScreen(state, state.pointer) : { x: 0, y: 0 } } : null
11385
12050
  };
@@ -11687,8 +12352,8 @@ function vectorPaintToHex(paint) {
11687
12352
  if (typeof paint !== "string") return null;
11688
12353
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(paint.trim());
11689
12354
  if (!m) return null;
11690
- const hex = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
11691
- return `#${hex.toLowerCase()}`;
12355
+ const hex2 = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
12356
+ return `#${hex2.toLowerCase()}`;
11692
12357
  }
11693
12358
  var VECTOR_GRADIENT_TYPE_LABELS = { linear: "Linear", radial: "Radial", conic: "Conic", reflected: "Reflected", diamond: "Diamond" };
11694
12359
  var VECTOR_GRADIENT_SPREAD_LABELS = { pad: "Pad", reflect: "Reflect", repeat: "Repeat" };
@@ -11975,6 +12640,8 @@ var STYLE_PROPS = {
11975
12640
  fillRule: { type: "string", enum: ["nonzero", "evenodd"], description: "Which areas count as inside (default nonzero)." }
11976
12641
  };
11977
12642
  var checkPaint = (paint, where) => {
12643
+ if (paint && typeof paint === "object" && (paint.type === "mesh" || paint.type === "freeform"))
12644
+ return sanitizeVectorShadedPaint(paint) ?? fail(`${where} is not a valid mesh or freeform gradient (see vector.setMeshGradient / vector.setFreeformGradient).`);
11978
12645
  if (paint && typeof paint === "object") return sanitizeVectorGradient(paint) ?? fail(`${where} is not a valid gradient: give a type and at least one stop { offset, color }.`);
11979
12646
  return paint;
11980
12647
  };
@@ -12928,6 +13595,235 @@ function createVectorCommands(controller, options = {}) {
12928
13595
  issues: state.constraintIssues
12929
13596
  };
12930
13597
  };
13598
+ const traceCommands = () => [
13599
+ {
13600
+ ...base,
13601
+ id: `${prefix}traceImage`,
13602
+ label: "Image Trace\u2026",
13603
+ keywords: ["trace", "image trace", "live trace", "vectorize", "bitmap to vector", "png to svg", "autotrace", "posterize"],
13604
+ description: "Traces a PNG (base64 data URL) into vector paths and adds them as one group: `threshold` mode makes one even-odd path where the image is dark (or opaque with channel alpha); `color` mode makes a stacked path per colour (median-cut to `colors`, the most common at the bottom). Placed with its top-left at x, y and `width` wide (default one unit per pixel). One undo step. Returns { id, paths: [{ id, name, fill, subpaths }] }.",
13605
+ params: {
13606
+ type: "object",
13607
+ properties: {
13608
+ image: { type: "string", description: "The image as a data:image/png;base64,\u2026 URL (up to 4 million pixels)." },
13609
+ mode: { type: "string", enum: ["threshold", "color"], description: "threshold (one colour, default) or color (a path per colour)." },
13610
+ channel: { type: "string", enum: ["luminance", "alpha"], description: "Threshold mode: trace dark pixels (luminance, default) or opaque ones (alpha)." },
13611
+ threshold: { type: "number", minimum: 0, maximum: 1, description: "Threshold mode: inside where darkness (or alpha) reaches this (default 0.5)." },
13612
+ color: { type: "string", description: "Threshold mode: the fill (default black)." },
13613
+ colors: { type: "integer", minimum: 2, maximum: 32, description: "Colour mode: how many colours (default 6)." },
13614
+ tolerance: { type: "number", exclusiveMinimum: 0, description: "How far the outline may stray from the pixels, in pixels (default 1; larger is simpler)." },
13615
+ minArea: { type: "number", minimum: 0, description: "Drop shapes smaller than this many square pixels (default 10)." },
13616
+ cornerRoundness: { type: "number", minimum: 0, maximum: 1, description: "0: polygons with sharp corners; 1: smooth curves (default 0.5)." },
13617
+ x: { type: "number", description: "Left edge in document units (default 0)." },
13618
+ y: { type: "number", description: "Top edge in document units (default 0)." },
13619
+ width: { type: "number", exclusiveMinimum: 0, description: "Width in document units (default: the image width in pixels)." },
13620
+ name: { type: "string", description: 'Group name (default "Image Trace").' }
13621
+ },
13622
+ required: ["image"],
13623
+ additionalProperties: false
13624
+ },
13625
+ undoable: true,
13626
+ run: (_ctx, params) => {
13627
+ const { image, ...options2 } = params;
13628
+ const parsed = parseDataUrl(image);
13629
+ if (!parsed || !isPng(parsed.bytes)) fail("image must be a PNG data URL (data:image/png;base64,\u2026).");
13630
+ let decoded;
13631
+ try {
13632
+ decoded = decodePng(parsed.bytes, { maxPixels: VECTOR_TRACE_MAX_PIXELS });
13633
+ } catch (e) {
13634
+ return fail(`The PNG could not be read: ${e.message}`);
13635
+ }
13636
+ const r = controller.traceImage(decoded, options2);
13637
+ check(r);
13638
+ return {
13639
+ id: r.id,
13640
+ paths: r.ids.map((id) => {
13641
+ const n = controller.getNode(id);
13642
+ return { id, name: n.name, fill: n.fill, subpaths: n.subpaths.length };
13643
+ })
13644
+ };
13645
+ }
13646
+ }
13647
+ ];
13648
+ const shadingResult = (id) => ({ id, shading: controller.getShading(id) ?? fail(`"${id}" has no mesh or freeform fill.`) });
13649
+ const COLOR = { type: "string", description: "A CSS colour." };
13650
+ const FREEFORM_POINT = {
13651
+ type: "object",
13652
+ properties: {
13653
+ x: { type: "number", description: "Document units." },
13654
+ y: { type: "number", description: "Document units." },
13655
+ color: COLOR,
13656
+ opacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity 0\u20131 (default 1)." },
13657
+ spread: { type: "number", minimum: 0, maximum: 1, description: "Radius of pure colour round the point, as a fraction of half the shape's longer side (default 0)." }
13658
+ },
13659
+ required: ["x", "y", "color"],
13660
+ additionalProperties: false
13661
+ };
13662
+ const LINES = { type: "array", items: { type: "array", items: { type: "integer", minimum: 0 }, minItems: 2 }, description: "Lines, each the point indices (two or more) a smooth curve runs through; the colours flow along it." };
13663
+ const shadingCommands = () => [
13664
+ {
13665
+ ...base,
13666
+ id: `${prefix}getShading`,
13667
+ label: "Get Mesh or Freeform Gradient",
13668
+ readOnly: true,
13669
+ keywords: ["mesh", "gradient mesh", "freeform", "freeform gradient", "points"],
13670
+ description: `Returns a path's mesh or freeform fill with its points in document units: { id, shading } where shading is { type: "mesh", rows, columns, points (row by row), colors, opacities? } or { type: "freeform", points: [{ x, y, color, opacity?, spread? }], lines? }. Point indices are what the edit commands take.`,
13671
+ params: { type: "object", properties: { id: { type: "string", description: "The path (from vector.list)." } }, required: ["id"], additionalProperties: false },
13672
+ run: (_ctx, params) => shadingResult(params.id)
13673
+ },
13674
+ {
13675
+ ...base,
13676
+ id: `${prefix}setMeshGradient`,
13677
+ label: "Create Gradient Mesh\u2026",
13678
+ keywords: ["mesh", "gradient mesh", "coons", "patch", "shading", "realistic", "smooth shading"],
13679
+ description: "Fills paths (`ids`, default the selection) with a gradient mesh over their bounds: rows \xD7 columns smooth patches whose points blend colours. Colour it with `colors` (one per point, (rows + 1) \xD7 (columns + 1), row by row) or the four `corners`, else the current fill colour, then move and recolour points with vector.setMeshPoint. One undo step. Returns { meshes: [{ id, shading }] }.",
13680
+ params: {
13681
+ type: "object",
13682
+ properties: {
13683
+ ids: { ...IDS, description: "Paths to fill (default: the selection)." },
13684
+ rows: { type: "integer", minimum: 1, maximum: 32, description: "Patch rows." },
13685
+ columns: { type: "integer", minimum: 1, maximum: 32, description: "Patch columns." },
13686
+ colors: { type: "array", items: COLOR, description: "A colour per point, row by row ((rows + 1) \xD7 (columns + 1) of them)." },
13687
+ corners: { type: "array", items: COLOR, minItems: 4, maxItems: 4, description: "Top-left, top-right, bottom-right and bottom-left colours, blended across." }
13688
+ },
13689
+ required: ["rows", "columns"],
13690
+ additionalProperties: false
13691
+ },
13692
+ undoable: true,
13693
+ enabled: enabledFor,
13694
+ run: (_ctx, params) => {
13695
+ const list2 = ids(params);
13696
+ check(controller.setMeshGradient({ rows: params.rows, columns: params.columns, ...params.colors ? { colors: params.colors } : {}, ...params.corners ? { corners: params.corners } : {} }, { ids: list2 }));
13697
+ return { meshes: list2.map(shadingResult) };
13698
+ }
13699
+ },
13700
+ {
13701
+ ...base,
13702
+ id: `${prefix}setMeshPoint`,
13703
+ label: "Edit Mesh Point",
13704
+ keywords: ["mesh", "mesh point", "colour", "move point"],
13705
+ description: "Moves point `index` (row by row, from vector.getShading) of a path's gradient mesh to x, y (document units, both or neither) and / or sets its colour and opacity. One undo step. Returns { id, shading }.",
13706
+ params: {
13707
+ type: "object",
13708
+ properties: {
13709
+ id: { type: "string", description: "The path." },
13710
+ index: { type: "integer", minimum: 0, description: "Point index, row by row." },
13711
+ x: { type: "number", description: "New position (document units)." },
13712
+ y: { type: "number", description: "New position (document units)." },
13713
+ color: COLOR,
13714
+ opacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity 0\u20131." }
13715
+ },
13716
+ required: ["id", "index"],
13717
+ additionalProperties: false
13718
+ },
13719
+ undoable: true,
13720
+ run: (_ctx, params) => {
13721
+ const { id, index, ...patch } = params;
13722
+ if (!Object.keys(patch).length) fail("Pass at least one of x / y, color, opacity.");
13723
+ check(controller.setMeshPoint(id, index, patch));
13724
+ return shadingResult(id);
13725
+ }
13726
+ },
13727
+ {
13728
+ ...base,
13729
+ id: `${prefix}setFreeformGradient`,
13730
+ label: "Create Freeform Gradient\u2026",
13731
+ keywords: ["freeform", "freeform gradient", "colour points", "shading", "blend colours"],
13732
+ description: "Fills paths (`ids`, default the selection) with a freeform gradient: colour `points` placed anywhere (document units) blending into each other, with optional `lines` that carry colour along smooth curves. Without points, four or more are placed inside each path, coloured along `colors` (default its fill to white). One undo step. Returns { gradients: [{ id, shading }] }.",
13733
+ params: {
13734
+ type: "object",
13735
+ properties: {
13736
+ ids: { ...IDS, description: "Paths to fill (default: the selection)." },
13737
+ points: { type: "array", items: FREEFORM_POINT, description: "The colour points (document units)." },
13738
+ lines: LINES,
13739
+ colors: { type: "array", items: COLOR, description: "Without points: colours the placed points take, first to last." }
13740
+ },
13741
+ additionalProperties: false
13742
+ },
13743
+ undoable: true,
13744
+ enabled: enabledFor,
13745
+ run: (_ctx, params) => {
13746
+ const { ids: _i, ...input } = params ?? {};
13747
+ const list2 = ids(params);
13748
+ check(controller.setFreeformGradient(input, { ids: list2 }));
13749
+ return { gradients: list2.map(shadingResult) };
13750
+ }
13751
+ },
13752
+ {
13753
+ ...base,
13754
+ id: `${prefix}addFreeformPoint`,
13755
+ label: "Add Freeform Point",
13756
+ keywords: ["freeform", "colour point", "add point"],
13757
+ description: "Adds a colour point to a path's freeform gradient at x, y (document units), coloured `color` (default: the colour there now). One undo step. Returns { id, index, shading }.",
13758
+ params: {
13759
+ type: "object",
13760
+ properties: { id: { type: "string", description: "The path." }, x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." }, color: COLOR, opacity: FREEFORM_POINT.properties.opacity, spread: FREEFORM_POINT.properties.spread },
13761
+ required: ["id", "x", "y"],
13762
+ additionalProperties: false
13763
+ },
13764
+ undoable: true,
13765
+ run: (_ctx, params) => {
13766
+ const { id, ...point } = params;
13767
+ const r = controller.addFreeformPoint(id, point);
13768
+ check(r);
13769
+ return { ...shadingResult(id), index: r.index };
13770
+ }
13771
+ },
13772
+ {
13773
+ ...base,
13774
+ id: `${prefix}setFreeformPoint`,
13775
+ label: "Edit Freeform Point",
13776
+ keywords: ["freeform", "colour point", "move point", "spread"],
13777
+ description: "Changes point `index` (from vector.getShading) of a path's freeform gradient: x, y (document units, both or neither), color, opacity, spread. One undo step. Returns { id, shading }.",
13778
+ params: {
13779
+ type: "object",
13780
+ properties: {
13781
+ id: { type: "string", description: "The path." },
13782
+ index: { type: "integer", minimum: 0, description: "Point index." },
13783
+ x: { type: "number", description: "New position (document units)." },
13784
+ y: { type: "number", description: "New position (document units)." },
13785
+ color: COLOR,
13786
+ opacity: FREEFORM_POINT.properties.opacity,
13787
+ spread: FREEFORM_POINT.properties.spread
13788
+ },
13789
+ required: ["id", "index"],
13790
+ additionalProperties: false
13791
+ },
13792
+ undoable: true,
13793
+ run: (_ctx, params) => {
13794
+ const { id, index, ...patch } = params;
13795
+ if (!Object.keys(patch).length) fail("Pass at least one of x / y, color, opacity, spread.");
13796
+ check(controller.setFreeformPoint(id, index, patch));
13797
+ return shadingResult(id);
13798
+ }
13799
+ },
13800
+ {
13801
+ ...base,
13802
+ id: `${prefix}removeFreeformPoint`,
13803
+ label: "Remove Freeform Point",
13804
+ keywords: ["freeform", "colour point", "delete point"],
13805
+ description: "Removes point `index` of a path's freeform gradient (never the last one); lines through it close up and later indices shift down. One undo step. Returns { id, shading }.",
13806
+ params: { type: "object", properties: { id: { type: "string", description: "The path." }, index: { type: "integer", minimum: 0, description: "Point index." } }, required: ["id", "index"], additionalProperties: false },
13807
+ undoable: true,
13808
+ run: (_ctx, params) => {
13809
+ check(controller.removeFreeformPoint(params.id, params.index));
13810
+ return shadingResult(params.id);
13811
+ }
13812
+ },
13813
+ {
13814
+ ...base,
13815
+ id: `${prefix}setFreeformLines`,
13816
+ label: "Freeform Lines",
13817
+ keywords: ["freeform", "lines", "colour lines", "curve"],
13818
+ description: "Replaces the lines of a path's freeform gradient (an empty list removes them): each line is the point indices a smooth curve runs through, carrying the colours along it. One undo step. Returns { id, shading }.",
13819
+ params: { type: "object", properties: { id: { type: "string", description: "The path." }, lines: LINES }, required: ["id", "lines"], additionalProperties: false },
13820
+ undoable: true,
13821
+ run: (_ctx, params) => {
13822
+ check(controller.setFreeformLines(params.id, params.lines));
13823
+ return shadingResult(params.id);
13824
+ }
13825
+ }
13826
+ ];
12931
13827
  const snapResult = () => {
12932
13828
  const { grid, objects, guides, threshold, angles, tracking, trackPolar, polarRelative } = controller.getState().snap;
12933
13829
  return { grid, objects, guides, threshold, angles: [...angles], tracking: tracking !== false, trackPolar: !!trackPolar, polarRelative: !!polarRelative };
@@ -13453,6 +14349,8 @@ function createVectorCommands(controller, options = {}) {
13453
14349
  ...frameCommands(),
13454
14350
  ...connectorCommands(),
13455
14351
  ...trackingCommands(),
14352
+ ...shadingCommands(),
14353
+ ...traceCommands(),
13456
14354
  {
13457
14355
  ...base,
13458
14356
  id: `${prefix}setCorners`,
@@ -14426,6 +15324,7 @@ export {
14426
15324
  VECTOR_MAX_CONSTRAINTS,
14427
15325
  VECTOR_MAX_TRACKING_POINTS,
14428
15326
  VECTOR_MERGE_MODES,
15327
+ VECTOR_MESH_MAX,
14429
15328
  VECTOR_OVERLAY_THEME,
14430
15329
  VECTOR_PARAGRAPH_STYLE_KEYS,
14431
15330
  VECTOR_PATHFINDER_ACTIONS,
@@ -14449,6 +15348,7 @@ export {
14449
15348
  VECTOR_TOOLS,
14450
15349
  VECTOR_TOOL_GLYPHS,
14451
15350
  VECTOR_TOOL_SHORTCUTS,
15351
+ VECTOR_TRACE_MAX_PIXELS,
14452
15352
  VECTOR_TRANSFORM_HANDLES,
14453
15353
  VECTOR_VERTICAL_RESIZING,
14454
15354
  VECTOR_VERTICAL_RESIZING_LABELS,
@@ -14484,6 +15384,7 @@ export {
14484
15384
  arrowheadPath,
14485
15385
  arrowheadWeight,
14486
15386
  autoConstrainVector,
15387
+ autoVectorFreeformGradient,
14487
15388
  bakeVectorTextGeometry,
14488
15389
  bendSegment,
14489
15390
  bezierToSubpaths,
@@ -14511,6 +15412,7 @@ export {
14511
15412
  createVectorCommands,
14512
15413
  createVectorEffect,
14513
15414
  createVectorFilter,
15415
+ createVectorMeshGradient,
14514
15416
  createVectorMorph,
14515
15417
  cssFontFamily,
14516
15418
  curvatureMoveAnchor,
@@ -14617,11 +15519,14 @@ export {
14617
15519
  isVectorConnector,
14618
15520
  isVectorFilterType,
14619
15521
  isVectorFrame,
15522
+ isVectorFreeformGradient,
14620
15523
  isVectorGradient,
14621
15524
  isVectorLiveBoolean,
14622
15525
  isVectorMaskActive,
14623
15526
  isVectorMaskGroup,
14624
15527
  isVectorMaskType,
15528
+ isVectorMeshGradient,
15529
+ isVectorShadedPaint,
14625
15530
  isVectorTextTarget,
14626
15531
  joinSubpaths,
14627
15532
  knifePath,
@@ -14634,6 +15539,7 @@ export {
14634
15539
  mapWarpMeshPoints as mapVectorMeshPoints,
14635
15540
  mapVectorPaint,
14636
15541
  mapVectorPoints,
15542
+ mapVectorShadedPaint,
14637
15543
  mapVectorSubpaths,
14638
15544
  matrixRotation,
14639
15545
  measureVectorConstraint,
@@ -14649,6 +15555,7 @@ export {
14649
15555
  nearestSegment,
14650
15556
  nearestStrokePosition,
14651
15557
  nearestVectorConnectionSite,
15558
+ nearestVectorShadingPoint,
14652
15559
  needsVectorPaintLayers,
14653
15560
  newVectorStrokeLayer,
14654
15561
  nextVectorConstraintName,
@@ -14679,6 +15586,7 @@ export {
14679
15586
  puckerBloatPaths,
14680
15587
  pullWarpMesh as pullVectorMesh,
14681
15588
  purgeVectorConstraints,
15589
+ quantizeVectorTraceColors,
14682
15590
  recognizeInkShape,
14683
15591
  rectSubpath,
14684
15592
  refineMorphContour,
@@ -14688,6 +15596,7 @@ export {
14688
15596
  removeAnchors,
14689
15597
  removeAnchorsKeepingShape,
14690
15598
  removeRedundantPoints,
15599
+ removeVectorFreeformPoint,
14691
15600
  removeVectorGradientStop,
14692
15601
  removeWarpMeshSplitU as removeVectorMeshSplitU,
14693
15602
  removeWarpMeshSplitV as removeVectorMeshSplitV,
@@ -14709,6 +15618,7 @@ export {
14709
15618
  routeVectorConnector,
14710
15619
  samplePlainStops,
14711
15620
  samplePolyline,
15621
+ sampleVectorFreeform,
14712
15622
  sampleVectorGradient,
14713
15623
  sampleVectorGradientAt,
14714
15624
  sampleVectorPaintDefAt,
@@ -14720,6 +15630,7 @@ export {
14720
15630
  sanitizeVectorGradient,
14721
15631
  sanitizeVectorMask,
14722
15632
  sanitizeVectorResizing,
15633
+ sanitizeVectorShadedPaint,
14723
15634
  sanitizeVectorStrokeSettings,
14724
15635
  sanitizeVectorStrokes,
14725
15636
  sanitizeVectorTextFields,
@@ -14759,10 +15670,12 @@ export {
14759
15670
  toVectorKeyInput,
14760
15671
  toVectorModifiers,
14761
15672
  toVectorPointerInput,
15673
+ traceVectorImage,
14762
15674
  transformVectorEffects,
14763
15675
  transformVectorGradient,
14764
15676
  transformVectorNode,
14765
15677
  transformVectorPaint,
15678
+ transformVectorShadedPaint,
14766
15679
  translateAnchor,
14767
15680
  translateMatrix,
14768
15681
  trimIntervals,
@@ -14827,6 +15740,9 @@ export {
14827
15740
  vectorFrameDocument,
14828
15741
  vectorFrameDrawn,
14829
15742
  vectorFrameRectPath,
15743
+ vectorFreeformField,
15744
+ vectorFreeformLineSegments,
15745
+ vectorFreeformSpreadScale,
14830
15746
  vectorGradientArea,
14831
15747
  vectorGradientAspect,
14832
15748
  vectorGradientBarPosition,
@@ -14863,13 +15779,16 @@ export {
14863
15779
  vectorMaskedGeometry,
14864
15780
  vectorMaskedLocalBounds,
14865
15781
  vectorMaskedShapes,
15782
+ vectorMeshColorAt,
14866
15783
  warpMeshColumns as vectorMeshColumns,
14867
15784
  warpMeshControlBounds as vectorMeshControlBounds,
14868
15785
  warpMeshEditPoints as vectorMeshEditPoints,
14869
15786
  vectorMeshFromOutline,
14870
15787
  warpMeshGridCurves as vectorMeshGridCurves,
15788
+ vectorMeshLines,
14871
15789
  vectorMeshOutline,
14872
15790
  warpMeshParamAt as vectorMeshParamAt,
15791
+ vectorMeshPointAt,
14873
15792
  warpMeshRows as vectorMeshRows,
14874
15793
  vectorNodeBounds,
14875
15794
  vectorNodeEnvelope,
@@ -14898,6 +15817,8 @@ export {
14898
15817
  vectorRepeaterCopies,
14899
15818
  vectorResizeCursor,
14900
15819
  vectorResultStyle,
15820
+ vectorRgbaToCss,
15821
+ vectorShadedSvgDefs,
14901
15822
  vectorShapeBuilderHit,
14902
15823
  vectorShapeBuilderMap,
14903
15824
  vectorShearForSide,
@@ -14935,6 +15856,7 @@ export {
14935
15856
  vectorTextSpans,
14936
15857
  vectorTextStyleAt,
14937
15858
  vectorToolLabel,
15859
+ vectorTraceContours,
14938
15860
  vectorTrackingPaths,
14939
15861
  warpPresetGrid as vectorWarpPresetGrid,
14940
15862
  warpPresetMesh as vectorWarpPresetMesh,