@pixerate/editor 0.190.0 → 0.191.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.
- package/dist/{chunk-T4U4O6DS.cjs → chunk-2UR4OX3B.cjs} +6 -9
- package/dist/{chunk-M7XIC2FC.js → chunk-RQ2EM5TY.js} +333 -0
- package/dist/{chunk-7ZWLDVFH.js → chunk-TXYXF32N.js} +6 -9
- package/dist/{chunk-5UEFNX24.cjs → chunk-ZQGZWFPU.cjs} +344 -11
- package/dist/image-editor/controller.d.cts +23 -1
- package/dist/image-editor/controller.d.ts +23 -1
- package/dist/image-editor/index.cjs +1056 -896
- package/dist/image-editor/index.js +173 -13
- package/dist/image-editor/layers/panel.d.cts +1 -1
- package/dist/image-editor/layers/panel.d.ts +1 -1
- package/dist/image-editor/layers/vector-mask.d.cts +2 -2
- package/dist/image-editor/layers/vector-mask.d.ts +2 -2
- package/dist/image-editor/selection/index.d.cts +1 -0
- package/dist/image-editor/selection/index.d.ts +1 -0
- package/dist/image-editor/selection/path.d.cts +37 -0
- package/dist/image-editor/selection/path.d.ts +37 -0
- package/dist/paint-editor/index.cjs +6 -6
- package/dist/paint-editor/index.js +1 -1
- package/dist/vector/index.cjs +21 -303
- package/dist/vector/index.js +15 -297
- package/dist/vector/trace.d.cts +8 -1
- package/dist/vector/trace.d.ts +8 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +3 -1
- package/skills/pixerate-image-selection-masking/SKILL.md +6 -2
package/dist/vector/index.js
CHANGED
|
@@ -9,9 +9,14 @@ import {
|
|
|
9
9
|
pathfinder
|
|
10
10
|
} from "../chunk-65MDXWA7.js";
|
|
11
11
|
import {
|
|
12
|
+
VECTOR_TRACE_MAX_PIXELS,
|
|
12
13
|
loadOpenTypeFont,
|
|
13
|
-
parseOpenTypeFont
|
|
14
|
-
|
|
14
|
+
parseOpenTypeFont,
|
|
15
|
+
quantizeVectorTraceColors,
|
|
16
|
+
traceField,
|
|
17
|
+
traceVectorImage,
|
|
18
|
+
vectorTraceContours
|
|
19
|
+
} from "../chunk-RQ2EM5TY.js";
|
|
15
20
|
import {
|
|
16
21
|
applyHomography,
|
|
17
22
|
homographyFromPoints,
|
|
@@ -533,8 +538,7 @@ import {
|
|
|
533
538
|
import {
|
|
534
539
|
fitCubicWithTangents,
|
|
535
540
|
fitCubics,
|
|
536
|
-
parseSvgPath
|
|
537
|
-
simplifyRing
|
|
541
|
+
parseSvgPath
|
|
538
542
|
} from "../chunk-PWBZH3FK.js";
|
|
539
543
|
import {
|
|
540
544
|
EffectUnsupportedError,
|
|
@@ -575,293 +579,6 @@ import "../chunk-YBAPT7NL.js";
|
|
|
575
579
|
import "../chunk-MHLU6GRJ.js";
|
|
576
580
|
import "../chunk-6ONMQ6WF.js";
|
|
577
581
|
|
|
578
|
-
// src/vector/trace.ts
|
|
579
|
-
var VECTOR_TRACE_MAX_PIXELS = 4e6;
|
|
580
|
-
function signedArea(p) {
|
|
581
|
-
let a = 0;
|
|
582
|
-
for (let i = 0; i < p.length; i++) {
|
|
583
|
-
const [x0, y0] = p[i];
|
|
584
|
-
const [x1, y1] = p[(i + 1) % p.length];
|
|
585
|
-
a += x0 * y1 - x1 * y0;
|
|
586
|
-
}
|
|
587
|
-
return a / 2;
|
|
588
|
-
}
|
|
589
|
-
function pointInPoly(p, poly) {
|
|
590
|
-
let inside = false;
|
|
591
|
-
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
592
|
-
const [xi, yi] = poly[i];
|
|
593
|
-
const [xj, yj] = poly[j];
|
|
594
|
-
if (yi > p[1] !== yj > p[1] && p[0] < (xj - xi) * (p[1] - yi) / (yj - yi) + xi) inside = !inside;
|
|
595
|
-
}
|
|
596
|
-
return inside;
|
|
597
|
-
}
|
|
598
|
-
function vectorTraceContours(field, w, h, threshold) {
|
|
599
|
-
const gw = w + 2;
|
|
600
|
-
const gh = h + 2;
|
|
601
|
-
const val = (i, j) => i === 0 || j === 0 || i > w || j > h ? -Infinity : field[(j - 1) * w + (i - 1)];
|
|
602
|
-
const inside = (i, j) => val(i, j) >= threshold;
|
|
603
|
-
const pos = (i, j) => [i - 0.5, j - 0.5];
|
|
604
|
-
const cross2 = (a, b) => {
|
|
605
|
-
const va = val(a[0], a[1]);
|
|
606
|
-
const vb = val(b[0], b[1]);
|
|
607
|
-
let t;
|
|
608
|
-
if (Number.isFinite(va) && Number.isFinite(vb) && Math.abs(vb - va) > 1e-12) t = Math.min(1, Math.max(0, (threshold - va) / (vb - va)));
|
|
609
|
-
else if (Number.isFinite(va)) t = va >= threshold ? 0.5 : 0;
|
|
610
|
-
else t = vb >= threshold ? 0.5 : 1;
|
|
611
|
-
const pa = pos(a[0], a[1]);
|
|
612
|
-
const pb = pos(b[0], b[1]);
|
|
613
|
-
return [pa[0] + (pb[0] - pa[0]) * t, pa[1] + (pb[1] - pa[1]) * t];
|
|
614
|
-
};
|
|
615
|
-
const hid = (i, j) => 2 * (j * gw + i);
|
|
616
|
-
const vid = (i, j) => 2 * (j * gw + i) + 1;
|
|
617
|
-
const next = /* @__PURE__ */ new Map();
|
|
618
|
-
const point = /* @__PURE__ */ new Map();
|
|
619
|
-
for (let j = 0; j < gh - 1; j++) {
|
|
620
|
-
for (let i = 0; i < gw - 1; i++) {
|
|
621
|
-
const tl = inside(i, j);
|
|
622
|
-
const tr = inside(i + 1, j);
|
|
623
|
-
const br = inside(i + 1, j + 1);
|
|
624
|
-
const bl = inside(i, j + 1);
|
|
625
|
-
const code = +tl << 3 | +tr << 2 | +br << 1 | +bl;
|
|
626
|
-
if (code === 0 || code === 15) continue;
|
|
627
|
-
const top = hid(i, j);
|
|
628
|
-
const bottom = hid(i, j + 1);
|
|
629
|
-
const left = vid(i, j);
|
|
630
|
-
const right = vid(i + 1, j);
|
|
631
|
-
const seg = (a, b) => next.set(a, b);
|
|
632
|
-
const mean = () => (val(i, j) + val(i + 1, j) + val(i + 1, j + 1) + val(i, j + 1)) / 4;
|
|
633
|
-
switch (code) {
|
|
634
|
-
case 1:
|
|
635
|
-
seg(left, bottom);
|
|
636
|
-
break;
|
|
637
|
-
case 2:
|
|
638
|
-
seg(bottom, right);
|
|
639
|
-
break;
|
|
640
|
-
case 3:
|
|
641
|
-
seg(left, right);
|
|
642
|
-
break;
|
|
643
|
-
case 4:
|
|
644
|
-
seg(right, top);
|
|
645
|
-
break;
|
|
646
|
-
case 5:
|
|
647
|
-
if (mean() >= threshold) {
|
|
648
|
-
seg(left, top);
|
|
649
|
-
seg(right, bottom);
|
|
650
|
-
} else {
|
|
651
|
-
seg(left, bottom);
|
|
652
|
-
seg(right, top);
|
|
653
|
-
}
|
|
654
|
-
break;
|
|
655
|
-
case 6:
|
|
656
|
-
seg(bottom, top);
|
|
657
|
-
break;
|
|
658
|
-
case 7:
|
|
659
|
-
seg(left, top);
|
|
660
|
-
break;
|
|
661
|
-
case 8:
|
|
662
|
-
seg(top, left);
|
|
663
|
-
break;
|
|
664
|
-
case 9:
|
|
665
|
-
seg(top, bottom);
|
|
666
|
-
break;
|
|
667
|
-
case 10:
|
|
668
|
-
if (mean() >= threshold) {
|
|
669
|
-
seg(top, right);
|
|
670
|
-
seg(bottom, left);
|
|
671
|
-
} else {
|
|
672
|
-
seg(top, left);
|
|
673
|
-
seg(bottom, right);
|
|
674
|
-
}
|
|
675
|
-
break;
|
|
676
|
-
case 11:
|
|
677
|
-
seg(top, right);
|
|
678
|
-
break;
|
|
679
|
-
case 12:
|
|
680
|
-
seg(right, left);
|
|
681
|
-
break;
|
|
682
|
-
case 13:
|
|
683
|
-
seg(right, bottom);
|
|
684
|
-
break;
|
|
685
|
-
case 14:
|
|
686
|
-
seg(bottom, left);
|
|
687
|
-
break;
|
|
688
|
-
}
|
|
689
|
-
const edges = [
|
|
690
|
-
[top, [i, j], [i + 1, j]],
|
|
691
|
-
[bottom, [i, j + 1], [i + 1, j + 1]],
|
|
692
|
-
[left, [i, j], [i, j + 1]],
|
|
693
|
-
[right, [i + 1, j], [i + 1, j + 1]]
|
|
694
|
-
];
|
|
695
|
-
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));
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
const out = [];
|
|
699
|
-
for (const start of [...next.keys()].sort((a, b) => a - b)) {
|
|
700
|
-
if (!next.has(start)) continue;
|
|
701
|
-
const poly = [];
|
|
702
|
-
let e = start;
|
|
703
|
-
for (; ; ) {
|
|
704
|
-
const n2 = next.get(e);
|
|
705
|
-
if (n2 === void 0) break;
|
|
706
|
-
next.delete(e);
|
|
707
|
-
const p = point.get(e);
|
|
708
|
-
if (p) poly.push(p);
|
|
709
|
-
e = n2;
|
|
710
|
-
if (e === start) break;
|
|
711
|
-
}
|
|
712
|
-
if (poly.length >= 3) out.push(poly);
|
|
713
|
-
}
|
|
714
|
-
return out;
|
|
715
|
-
}
|
|
716
|
-
function loopsOf(field, w, h, threshold, tolerance, minArea) {
|
|
717
|
-
const loops = [];
|
|
718
|
-
for (const raw of vectorTraceContours(field, w, h, threshold)) {
|
|
719
|
-
const area = signedArea(raw);
|
|
720
|
-
if (Math.abs(area) < Math.max(0, minArea)) continue;
|
|
721
|
-
const simple = simplifyRing(raw, Math.max(0.01, tolerance));
|
|
722
|
-
if (simple.length < 3) continue;
|
|
723
|
-
loops.push({ raw, simple, area, depth: 0 });
|
|
724
|
-
}
|
|
725
|
-
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;
|
|
726
|
-
return loops.sort((a, b) => a.depth - b.depth || Math.abs(b.area) - Math.abs(a.area));
|
|
727
|
-
}
|
|
728
|
-
function loopSubpath(l, tolerance, roundness) {
|
|
729
|
-
if (roundness <= 0) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
|
|
730
|
-
const cornerAngleDeg = 25 + roundness * 125;
|
|
731
|
-
const cubics = fitCubics(l.raw, Math.max(0.05, tolerance), {
|
|
732
|
-
closed: true,
|
|
733
|
-
cornerAngleDeg
|
|
734
|
-
});
|
|
735
|
-
if (!cubics.length) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
|
|
736
|
-
const anchors = cubics.map((c, k) => {
|
|
737
|
-
const prev = cubics[(k - 1 + cubics.length) % cubics.length];
|
|
738
|
-
return {
|
|
739
|
-
x: c.p0[0],
|
|
740
|
-
y: c.p0[1],
|
|
741
|
-
in: { x: prev.p2[0], y: prev.p2[1] },
|
|
742
|
-
out: { x: c.p1[0], y: c.p1[1] }
|
|
743
|
-
};
|
|
744
|
-
});
|
|
745
|
-
return { closed: true, anchors };
|
|
746
|
-
}
|
|
747
|
-
function traceField(field, w, h, threshold, o) {
|
|
748
|
-
const tolerance = o.tolerance ?? 1;
|
|
749
|
-
const roundness = Math.min(1, Math.max(0, o.cornerRoundness ?? 0.5));
|
|
750
|
-
return loopsOf(field, w, h, threshold, tolerance, o.minArea ?? 10).map((l) => loopSubpath(l, tolerance, roundness));
|
|
751
|
-
}
|
|
752
|
-
function quantizeVectorTraceColors(pixels, count) {
|
|
753
|
-
const n2 = pixels.width * pixels.height;
|
|
754
|
-
const d = pixels.data;
|
|
755
|
-
const opaque = [];
|
|
756
|
-
for (let i = 0; i < n2; i++) if (d[i * 4 + 3] >= 128) opaque.push(i);
|
|
757
|
-
const boxes = opaque.length ? [opaque] : [];
|
|
758
|
-
const range = (box, c) => {
|
|
759
|
-
let lo = 255;
|
|
760
|
-
let hi = 0;
|
|
761
|
-
for (const i of box) {
|
|
762
|
-
const v = d[i * 4 + c];
|
|
763
|
-
if (v < lo) lo = v;
|
|
764
|
-
if (v > hi) hi = v;
|
|
765
|
-
}
|
|
766
|
-
return hi - lo;
|
|
767
|
-
};
|
|
768
|
-
while (boxes.length < count) {
|
|
769
|
-
let best = -1;
|
|
770
|
-
let bestRange = 0;
|
|
771
|
-
let bestC = 0;
|
|
772
|
-
boxes.forEach((b, k) => {
|
|
773
|
-
if (b.length < 2) return;
|
|
774
|
-
for (let c = 0; c < 3; c++) {
|
|
775
|
-
const r = range(b, c);
|
|
776
|
-
if (r > bestRange) {
|
|
777
|
-
bestRange = r;
|
|
778
|
-
best = k;
|
|
779
|
-
bestC = c;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
if (best < 0) break;
|
|
784
|
-
const box = boxes[best].slice().sort((a, b) => d[a * 4 + bestC] - d[b * 4 + bestC]);
|
|
785
|
-
const key = (k) => d[box[k] * 4 + bestC];
|
|
786
|
-
const v = key(box.length >> 1);
|
|
787
|
-
let mid = box.findIndex((_, k) => key(k) >= v);
|
|
788
|
-
if (mid <= 0) mid = box.findIndex((_, k) => key(k) > v);
|
|
789
|
-
boxes.splice(best, 1, box.slice(0, mid), box.slice(mid));
|
|
790
|
-
}
|
|
791
|
-
const palette = boxes.map((b) => {
|
|
792
|
-
const s = [0, 0, 0];
|
|
793
|
-
for (const i of b) for (let c = 0; c < 3; c++) s[c] += d[i * 4 + c];
|
|
794
|
-
return [Math.round(s[0] / b.length), Math.round(s[1] / b.length), Math.round(s[2] / b.length)];
|
|
795
|
-
});
|
|
796
|
-
const index = new Int16Array(n2).fill(-1);
|
|
797
|
-
const counts = palette.map(() => 0);
|
|
798
|
-
for (const i of opaque) {
|
|
799
|
-
let best = 0;
|
|
800
|
-
let bestD = Infinity;
|
|
801
|
-
palette.forEach((p, k) => {
|
|
802
|
-
const dd = (d[i * 4] - p[0]) ** 2 + (d[i * 4 + 1] - p[1]) ** 2 + (d[i * 4 + 2] - p[2]) ** 2;
|
|
803
|
-
if (dd < bestD) {
|
|
804
|
-
bestD = dd;
|
|
805
|
-
best = k;
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
index[i] = best;
|
|
809
|
-
counts[best]++;
|
|
810
|
-
}
|
|
811
|
-
return { palette, index, counts };
|
|
812
|
-
}
|
|
813
|
-
var hex = (c) => `#${c.map((v) => Math.round(v).toString(16).padStart(2, "0")).join("")}`;
|
|
814
|
-
function traceVectorImage(pixels, options = {}) {
|
|
815
|
-
const { width: w, height: h, data } = pixels;
|
|
816
|
-
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).");
|
|
817
|
-
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.`);
|
|
818
|
-
const n2 = w * h;
|
|
819
|
-
if ((options.mode ?? "threshold") === "threshold") {
|
|
820
|
-
const field = new Float32Array(n2);
|
|
821
|
-
const byAlpha = options.channel === "alpha";
|
|
822
|
-
for (let i = 0; i < n2; i++) {
|
|
823
|
-
const a = data[i * 4 + 3] / 255;
|
|
824
|
-
field[i] = byAlpha ? a : a * (1 - (0.2126 * data[i * 4] + 0.7152 * data[i * 4 + 1] + 0.0722 * data[i * 4 + 2]) / 255);
|
|
825
|
-
}
|
|
826
|
-
const subpaths = traceField(field, w, h, Math.min(1, Math.max(0, options.threshold ?? 0.5)), options);
|
|
827
|
-
return subpaths.length ? [
|
|
828
|
-
{
|
|
829
|
-
id: "trace-1",
|
|
830
|
-
type: "path",
|
|
831
|
-
name: "Trace",
|
|
832
|
-
fill: options.color ?? "#000000",
|
|
833
|
-
stroke: null,
|
|
834
|
-
fillRule: "evenodd",
|
|
835
|
-
subpaths
|
|
836
|
-
}
|
|
837
|
-
] : [];
|
|
838
|
-
}
|
|
839
|
-
const count = Math.min(32, Math.max(2, Math.round(options.colors ?? 6)));
|
|
840
|
-
const q = quantizeVectorTraceColors(pixels, count);
|
|
841
|
-
const order = q.palette.map((_, k) => k).filter((k) => q.counts[k] > 0).sort((a, b) => q.counts[b] - q.counts[a]);
|
|
842
|
-
const rank = new Map(order.map((k, r) => [k, r]));
|
|
843
|
-
const out = [];
|
|
844
|
-
order.forEach((k, r) => {
|
|
845
|
-
const field = new Uint8Array(n2);
|
|
846
|
-
for (let i = 0; i < n2; i++) {
|
|
847
|
-
const ix = q.index[i];
|
|
848
|
-
field[i] = ix >= 0 && rank.get(ix) >= r ? 1 : 0;
|
|
849
|
-
}
|
|
850
|
-
const subpaths = traceField(field, w, h, 0.5, options);
|
|
851
|
-
if (subpaths.length)
|
|
852
|
-
out.push({
|
|
853
|
-
id: `trace-${out.length + 1}`,
|
|
854
|
-
type: "path",
|
|
855
|
-
name: hex(q.palette[k]),
|
|
856
|
-
fill: hex(q.palette[k]),
|
|
857
|
-
stroke: null,
|
|
858
|
-
fillRule: "evenodd",
|
|
859
|
-
subpaths
|
|
860
|
-
});
|
|
861
|
-
});
|
|
862
|
-
return out;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
582
|
// src/vector/stylize.ts
|
|
866
583
|
var VECTOR_STYLIZE_STYLES = ["mosaic", "halftone", "lowPoly", "hatch"];
|
|
867
584
|
var VECTOR_STYLIZE_LABELS = { mosaic: "Mosaic", halftone: "Halftone", lowPoly: "Low Poly", hatch: "Engraving" };
|
|
@@ -889,7 +606,7 @@ function sampleBox(px, cx, cy, size) {
|
|
|
889
606
|
return [s[0] / s[3], s[1] / s[3], s[2] / s[3], s[3] / count];
|
|
890
607
|
}
|
|
891
608
|
var darkness = (c) => (1 - (0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]) / 255) * (c[3] / 255);
|
|
892
|
-
var
|
|
609
|
+
var hex = (c) => `#${c.slice(0, 3).map((v) => Math.round(Math.min(255, Math.max(0, v))).toString(16).padStart(2, "0")).join("")}`;
|
|
893
610
|
function paletteOf(colors, count) {
|
|
894
611
|
const data = new Uint8ClampedArray(colors.length * 4);
|
|
895
612
|
colors.forEach((c, i) => {
|
|
@@ -899,7 +616,7 @@ function paletteOf(colors, count) {
|
|
|
899
616
|
data[i * 4 + 3] = c[3];
|
|
900
617
|
});
|
|
901
618
|
const q = quantizeVectorTraceColors({ width: colors.length, height: 1, data }, Math.min(32, Math.max(2, Math.round(count))));
|
|
902
|
-
return { palette: q.palette.map(
|
|
619
|
+
return { palette: q.palette.map(hex), index: q.index };
|
|
903
620
|
}
|
|
904
621
|
function byColour(shapes, palette, index, name, extra = {}) {
|
|
905
622
|
const groups = palette.map(() => []);
|
|
@@ -3835,8 +3552,8 @@ function vectorPaintToHex(paint) {
|
|
|
3835
3552
|
if (typeof paint !== "string") return null;
|
|
3836
3553
|
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(paint.trim());
|
|
3837
3554
|
if (!m) return null;
|
|
3838
|
-
const
|
|
3839
|
-
return `#${
|
|
3555
|
+
const hex2 = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
|
|
3556
|
+
return `#${hex2.toLowerCase()}`;
|
|
3840
3557
|
}
|
|
3841
3558
|
var VECTOR_GRADIENT_TYPE_LABELS = { linear: "Linear", radial: "Radial", conic: "Conic", reflected: "Reflected", diamond: "Diamond" };
|
|
3842
3559
|
var VECTOR_GRADIENT_SPREAD_LABELS = { pad: "Pad", reflect: "Reflect", repeat: "Repeat" };
|
|
@@ -4728,8 +4445,8 @@ function subpathOf(prop, frame) {
|
|
|
4728
4445
|
}
|
|
4729
4446
|
function cssColor(c) {
|
|
4730
4447
|
const k = c.slice(0, 3).some((v) => v > 1) ? 255 : 1;
|
|
4731
|
-
const
|
|
4732
|
-
return `#${
|
|
4448
|
+
const hex2 = (v) => Math.round(Math.min(1, Math.max(0, v / k)) * 255).toString(16).padStart(2, "0");
|
|
4449
|
+
return `#${hex2(c[0] ?? 0)}${hex2(c[1] ?? 0)}${hex2(c[2] ?? 0)}`;
|
|
4733
4450
|
}
|
|
4734
4451
|
function geometryOf(it, frame, notes) {
|
|
4735
4452
|
switch (it.ty) {
|
|
@@ -19228,6 +18945,7 @@ export {
|
|
|
19228
18945
|
toVectorKeyInput,
|
|
19229
18946
|
toVectorModifiers,
|
|
19230
18947
|
toVectorPointerInput,
|
|
18948
|
+
traceField,
|
|
19231
18949
|
traceVectorImage,
|
|
19232
18950
|
transformVectorEffects,
|
|
19233
18951
|
transformVectorGradient,
|
package/dist/vector/trace.d.cts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* stacked: a colour's shape covers the colours traced above it too, so no gaps open between them.
|
|
12
12
|
*/
|
|
13
13
|
import type { Point } from '../canvas/types.cjs';
|
|
14
|
-
import type { VectorPathNode } from './types.cjs';
|
|
14
|
+
import type { VectorPathNode, VectorSubpath } from './types.cjs';
|
|
15
15
|
/** RGBA pixels, row by row (8 bits a channel). */
|
|
16
16
|
export interface VectorTracePixels {
|
|
17
17
|
width: number;
|
|
@@ -36,6 +36,11 @@ export interface VectorTraceOptions {
|
|
|
36
36
|
minArea?: number;
|
|
37
37
|
/** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
|
|
38
38
|
cornerRoundness?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Give hard pixel edges their square corners back: marching squares cuts each corner where an
|
|
41
|
+
* axis-aligned edge turns with a half-pixel diagonal, which skews a traced rectangle (default false).
|
|
42
|
+
*/
|
|
43
|
+
squareCorners?: boolean;
|
|
39
44
|
}
|
|
40
45
|
/** The most pixels traced (larger images are refused; scale them down first). */
|
|
41
46
|
export declare const VECTOR_TRACE_MAX_PIXELS = 4000000;
|
|
@@ -44,6 +49,8 @@ export declare const VECTOR_TRACE_MAX_PIXELS = 4000000;
|
|
|
44
49
|
* y + 0.5)): the closed contours where the field reaches `threshold`, in pixel coordinates.
|
|
45
50
|
*/
|
|
46
51
|
export declare function vectorTraceContours(field: ArrayLike<number>, w: number, h: number, threshold: number): Point[][];
|
|
52
|
+
/** The subpaths of a field's outline (holes included; fill even-odd), in its pixel coordinates (pixel centres at +0.5). */
|
|
53
|
+
export declare function traceField(field: ArrayLike<number>, w: number, h: number, threshold: number, o: VectorTraceOptions): VectorSubpath[];
|
|
47
54
|
/**
|
|
48
55
|
* The palette of the opaque pixels quantised to `count` colours (median cut on RGB) and each
|
|
49
56
|
* pixel's palette index (−1 for transparent pixels), with how many pixels each colour got.
|
package/dist/vector/trace.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* stacked: a colour's shape covers the colours traced above it too, so no gaps open between them.
|
|
12
12
|
*/
|
|
13
13
|
import type { Point } from '../canvas/types.js';
|
|
14
|
-
import type { VectorPathNode } from './types.js';
|
|
14
|
+
import type { VectorPathNode, VectorSubpath } from './types.js';
|
|
15
15
|
/** RGBA pixels, row by row (8 bits a channel). */
|
|
16
16
|
export interface VectorTracePixels {
|
|
17
17
|
width: number;
|
|
@@ -36,6 +36,11 @@ export interface VectorTraceOptions {
|
|
|
36
36
|
minArea?: number;
|
|
37
37
|
/** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
|
|
38
38
|
cornerRoundness?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Give hard pixel edges their square corners back: marching squares cuts each corner where an
|
|
41
|
+
* axis-aligned edge turns with a half-pixel diagonal, which skews a traced rectangle (default false).
|
|
42
|
+
*/
|
|
43
|
+
squareCorners?: boolean;
|
|
39
44
|
}
|
|
40
45
|
/** The most pixels traced (larger images are refused; scale them down first). */
|
|
41
46
|
export declare const VECTOR_TRACE_MAX_PIXELS = 4000000;
|
|
@@ -44,6 +49,8 @@ export declare const VECTOR_TRACE_MAX_PIXELS = 4000000;
|
|
|
44
49
|
* y + 0.5)): the closed contours where the field reaches `threshold`, in pixel coordinates.
|
|
45
50
|
*/
|
|
46
51
|
export declare function vectorTraceContours(field: ArrayLike<number>, w: number, h: number, threshold: number): Point[][];
|
|
52
|
+
/** The subpaths of a field's outline (holes included; fill even-odd), in its pixel coordinates (pixel centres at +0.5). */
|
|
53
|
+
export declare function traceField(field: ArrayLike<number>, w: number, h: number, threshold: number, o: VectorTraceOptions): VectorSubpath[];
|
|
47
54
|
/**
|
|
48
55
|
* The palette of the opaque pixels quantised to `count` colours (median cut on RGB) and each
|
|
49
56
|
* pixel's palette index (−1 for transparent pixels), with how many pixels each colour got.
|
package/package.json
CHANGED
|
@@ -314,11 +314,13 @@ in the source frame at the document's bit depth, colour mode and profile. Read
|
|
|
314
314
|
| `remove_shape_component` | Delete one component (not the last: delete the layer instead). Returns the shape. |
|
|
315
315
|
| `merge_shape_components` | Merge Shape Components: the combined area becomes one plain component (live shapes become paths); the look does not change. Returns the shape. |
|
|
316
316
|
| `get_vector_mask` | Read-only. A layer's vector mask: enabled, linked, inverted, fill rule, density, feather, bounds and components (subpaths in document px), or null. Read it before editing the path. |
|
|
317
|
-
| `add_vector_mask` | Layer › Vector Mask on any layer but a shape: `from` `path` (a `shape`, `subpaths` or `components`, like `add_shape_layer`), `reveal-all` or `hide-all` (empty path). Reveals inside the path, sharp at any size; it combines with the raster mask. Returns the vector mask. |
|
|
317
|
+
| `add_vector_mask` | Layer › Vector Mask on any layer but a shape: `from` `path` (a `shape`, `subpaths` or `components`, like `add_shape_layer`), `selection` (traced, `tolerance` px), `reveal-all` or `hide-all` (empty path). Reveals inside the path, sharp at any size; it combines with the raster mask. Returns the vector mask. |
|
|
318
318
|
| `set_vector_mask_path` | Replace the vector mask's path (properties kept). Returns the vector mask. |
|
|
319
319
|
| `set_vector_mask_properties` | `enabled`, `linked` (false: stays put when the layer moves), `inverted`, `density`, `feather` px; the path is kept. Returns the vector mask. |
|
|
320
320
|
| `rasterize_vector_mask` | Multiply the path into the raster mask (adding one) and drop the vector mask, so it can be painted. Returns the mask summary. |
|
|
321
321
|
| `remove_vector_mask` | Delete the vector mask (the raster mask stays). |
|
|
322
|
+
| `selection_to_path` | Read-only. Make Work Path: the selection's outline as closed Bézier subpaths (`tolerance` px, `cornerRoundness`, `minArea`); returns `svg` and `fillRule: "evenodd"` to pass to `add_vector_mask`, `set_vector_mask_path` or `add_shape_layer`. |
|
|
323
|
+
| `path_to_selection` | Make Selection: fill a path (`shape`, `subpaths`, `components`) into the selection by `mode`, with `feather` and `antiAlias`. Returns the selection. |
|
|
322
324
|
| `convert_to_smart_object` | Convert sibling `layerIds` (default the active layer) to one smart object at the top one's position, optional `name`. One pixel layer keeps its id, look and properties (filters become smart filters); several layers, groups or adjustments nest as a layer document. Returns the `layer` and the `smartObject` summary. |
|
|
323
325
|
| `rasterize_smart_object` | Turn a smart object into a pixel layer holding what it shows now (smart filters baked; same id, mask, effects, opacity, blend). Destructive: the contents and filters are gone. Returns the layer. |
|
|
324
326
|
| `set_smart_object_transform` | Set a smart object's transform absolutely: `corners`, `matrix` (contents → document), or `scaleX` / `scaleY` (percent of the contents), `rotate`, `skewX`, `skewY`, `x`, `y` (where the contents' centre goes; unset numbers keep their values); `warp` preset or mesh in contents px, `null` removes it; `interpolation`. Re-renders from the contents. Returns `placement`, `masksMoved` and `bounds`. |
|
|
@@ -67,8 +67,10 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
67
67
|
`add_smart_filter`) are also limited to an active selection.
|
|
68
68
|
- A cutout: `layer_via_copy` (the original stays), or `add_layer_mask` `from: "selection"` on
|
|
69
69
|
the layer itself. For a hard geometric or traced edge that must stay sharp and editable
|
|
70
|
-
(a product, a screen, a window), `add_vector_mask` with the path instead
|
|
71
|
-
|
|
70
|
+
(a product, a screen, a window), `add_vector_mask` with the path instead (`from:
|
|
71
|
+
"selection"` traces the current selection); it moves and transforms with the layer, and
|
|
72
|
+
`rasterize_vector_mask` turns it into paintable pixels. Trace with `tolerance` 0.5–1 px for
|
|
73
|
+
a tight edge; check the result with `set_mask_view` before you go on.
|
|
72
74
|
- Painting or fills inside it: keep the selection active and paint; `stroke_selection` for
|
|
73
75
|
an outline that follows it.
|
|
74
76
|
- Removing what it covers: `content_aware_fill` `output: "new-layer"`.
|
|
@@ -103,6 +105,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
103
105
|
| An existing layer mask | `load_selection_from_mask` (`source: "vector"` for a vector mask) |
|
|
104
106
|
| A cutout, shape or text already on its own layer | `select_layer_transparency` (exact, soft where the layer is) |
|
|
105
107
|
| A dead pixel row or column | `select_line`, then fill or heal it |
|
|
108
|
+
| Smooth curves you can describe (a logo, a road, a lens) | `path_to_selection` with Bézier `subpaths` or SVG path data |
|
|
106
109
|
|
|
107
110
|
## Recipes
|
|
108
111
|
|
|
@@ -199,6 +202,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
199
202
|
- `set_mask_view`: a layer mask alone in grey or as a red overlay, to check it
|
|
200
203
|
- `add_vector_mask`, `set_vector_mask_path`, `set_vector_mask_properties`, `get_vector_mask`: sharp, editable path masks
|
|
201
204
|
- `rasterize_vector_mask`, `remove_vector_mask`: turn a vector mask into pixels, or drop it
|
|
205
|
+
- `selection_to_path`, `path_to_selection`: a selection's outline as Bézier paths, and a path back into a selection
|
|
202
206
|
- `modify_selection`: expand, contract, feather, border, smooth
|
|
203
207
|
- `refine_selection`: snap the edge to the image
|
|
204
208
|
- `transform_selection`: move or scale the outline only
|