@weasel-js/labkit 1.0.3 → 1.0.4
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/_dts/{DrawCommand-uKHt4Vul.d.ts → DrawCommand-BkZztJsW.d.ts} +3 -1
- package/dist/_dts/{useExperimentState-vrttakTt.d.ts → useExperimentState-D7EQnnwJ.d.ts} +4 -0
- package/dist/{chunk-N5KTQKQA.js → chunk-BAPZPDDA.js} +628 -588
- package/dist/chunk-BAPZPDDA.js.map +1 -0
- package/dist/{chunk-73PXCRCR.js → chunk-DEWXYFEU.js} +6 -6
- package/dist/chunk-DEWXYFEU.js.map +1 -0
- package/dist/{chunk-3WPOGKUP.js → chunk-DWV7SFKR.js} +518 -217
- package/dist/chunk-DWV7SFKR.js.map +1 -0
- package/dist/{chunk-T7OKNJTY.js → chunk-KSTEW2AF.js} +18 -2
- package/dist/chunk-KSTEW2AF.js.map +1 -0
- package/dist/index.d.ts +64 -15
- package/dist/index.js +133 -24
- package/dist/index.js.map +1 -1
- package/dist/passthrough/weasel-canvas.d.ts +1 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +1 -1
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/state/index.d.ts +4 -4
- package/dist/state/index.js +2 -2
- package/dist/styles.css +163 -6
- package/dist/ui/layers/index.js +3 -3
- package/package.json +2 -1
- package/src/lab/Lab.tsx +14 -2
- package/src/lab/LabContext.ts +1 -0
- package/src/lab/WorkspaceGrid.less +37 -5
- package/src/lab/WorkspaceGrid.stories.tsx +16 -0
- package/src/lab/WorkspaceGrid.test.tsx +84 -10
- package/src/lab/WorkspaceGrid.tsx +188 -14
- package/src/lab/index.ts +0 -2
- package/src/state/helpers.ts +2 -2
- package/src/state/store.test.ts +2 -1
- package/src/state/store.ts +19 -0
- package/src/state/types.ts +3 -0
- package/src/test-setup.ts +19 -0
- package/src/workspace/Workspace.stories.tsx +1 -0
- package/src/workspace/Workspace.test.tsx +1 -0
- package/src/workspace/index.ts +1 -0
- package/src/workspace/workspaceOps.test.ts +34 -1
- package/src/workspace/workspaceOps.ts +16 -0
- package/dist/chunk-3WPOGKUP.js.map +0 -1
- package/dist/chunk-73PXCRCR.js.map +0 -1
- package/dist/chunk-N5KTQKQA.js.map +0 -1
- package/dist/chunk-T7OKNJTY.js.map +0 -1
- package/src/lab/gridDims.test.ts +0 -35
- package/src/lab/gridDims.ts +0 -13
|
@@ -3,7 +3,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
3
3
|
import earcut from 'earcut';
|
|
4
4
|
import polygonClipping from 'polygon-clipping';
|
|
5
5
|
|
|
6
|
-
// ../core/dist/chunk-
|
|
6
|
+
// ../core/dist/chunk-J3YC3GVF.js
|
|
7
7
|
|
|
8
8
|
// ../gestures/dist/index.js
|
|
9
9
|
var GESTURE_DESCRIPTORS = [
|
|
@@ -60,9 +60,8 @@ function formatRoute(r) {
|
|
|
60
60
|
const desc = getGestureDescriptor(r.gesture);
|
|
61
61
|
const phaseStr = `[${r.phases.map(formatPhaseAtom).join(",")}]`;
|
|
62
62
|
let out = `${phaseStr} ${r.gesture}`;
|
|
63
|
-
if (desc.arg) {
|
|
64
|
-
|
|
65
|
-
if (!isDefault) out += `(${r.arg})`;
|
|
63
|
+
if (desc.arg && r.arg !== void 0 && r.arg !== (desc.arg.default ?? "*")) {
|
|
64
|
+
out += `(${r.arg})`;
|
|
66
65
|
}
|
|
67
66
|
if (desc.hasTarget && r.target !== void 0 && r.target !== "*") {
|
|
68
67
|
out += ` => ${r.target}`;
|
|
@@ -288,7 +287,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
288
287
|
}
|
|
289
288
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
290
289
|
|
|
291
|
-
// ../core/dist/chunk-
|
|
290
|
+
// ../core/dist/chunk-J3YC3GVF.js
|
|
292
291
|
var DepRegistryContext = createContext(null);
|
|
293
292
|
function DepRegistryProvider({ children }) {
|
|
294
293
|
const sourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -520,7 +519,8 @@ function ActionsProvider({ children }) {
|
|
|
520
519
|
const disp = dispatcherRef.current;
|
|
521
520
|
if (!disp) return null;
|
|
522
521
|
const r = wiredDepRegRef.current ?? depRegRef.current;
|
|
523
|
-
const
|
|
522
|
+
const a = actionsRef.current.get(id);
|
|
523
|
+
const deps = !r ? {} : a?.requires ? buildDepsFromRequires(a, r) : {
|
|
524
524
|
selection: r.get("selection"),
|
|
525
525
|
scene: r.get("scene"),
|
|
526
526
|
history: r.get("history"),
|
|
@@ -528,7 +528,7 @@ function ActionsProvider({ children }) {
|
|
|
528
528
|
pointer: r.get("pointer"),
|
|
529
529
|
activeTool: r.get("activeTool"),
|
|
530
530
|
booleansAdapter: r.get("booleansAdapter")
|
|
531
|
-
}
|
|
531
|
+
};
|
|
532
532
|
return disp.beginUiOngoing(id, deps, params);
|
|
533
533
|
}
|
|
534
534
|
};
|
|
@@ -699,7 +699,7 @@ function reportRouteConflicts(scopes, warn = (m) => console.warn(m)) {
|
|
|
699
699
|
return conflicts;
|
|
700
700
|
}
|
|
701
701
|
|
|
702
|
-
// ../geom/dist/chunk-
|
|
702
|
+
// ../geom/dist/chunk-KU7FXMOA.js
|
|
703
703
|
function cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t) {
|
|
704
704
|
const u = 1 - t;
|
|
705
705
|
const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
|
|
@@ -753,6 +753,12 @@ function cubicBounds(x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
|
753
753
|
}
|
|
754
754
|
|
|
755
755
|
// ../geom/dist/index.js
|
|
756
|
+
function dot(ax, ay, bx, by) {
|
|
757
|
+
return ax * bx + ay * by;
|
|
758
|
+
}
|
|
759
|
+
function len2(x, y) {
|
|
760
|
+
return x * x + y * y;
|
|
761
|
+
}
|
|
756
762
|
function sign(n) {
|
|
757
763
|
return n > 0 ? 1 : n < 0 ? -1 : 0;
|
|
758
764
|
}
|
|
@@ -804,6 +810,15 @@ function segmentsCross(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
|
804
810
|
const d4 = sign((bx - ax) * (dy - ay) - (by - ay) * (dx - ax));
|
|
805
811
|
return d1 !== d2 && d3 !== d4;
|
|
806
812
|
}
|
|
813
|
+
function pointSegmentDist2(px, py, ax, ay, bx, by) {
|
|
814
|
+
const vx = bx - ax, vy = by - ay;
|
|
815
|
+
const wx = px - ax, wy = py - ay;
|
|
816
|
+
const vv = len2(vx, vy);
|
|
817
|
+
let t = vv === 0 ? 0 : dot(wx, wy, vx, vy) / vv;
|
|
818
|
+
t = t < 0 ? 0 : t > 1 ? 1 : t;
|
|
819
|
+
const dx = px - (ax + t * vx), dy = py - (ay + t * vy);
|
|
820
|
+
return len2(dx, dy);
|
|
821
|
+
}
|
|
807
822
|
function transformCoords(coords, m) {
|
|
808
823
|
const out = new Float64Array(coords.length);
|
|
809
824
|
const a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
|
@@ -815,7 +830,7 @@ function transformCoords(coords, m) {
|
|
|
815
830
|
return out;
|
|
816
831
|
}
|
|
817
832
|
|
|
818
|
-
// ../core/dist/chunk-
|
|
833
|
+
// ../core/dist/chunk-4Z6J4IVH.js
|
|
819
834
|
function resolveUnit(v, unitSystem) {
|
|
820
835
|
if (typeof v === "number") return v;
|
|
821
836
|
if (!unitSystem) {
|
|
@@ -900,19 +915,36 @@ function boundsOfPath(path) {
|
|
|
900
915
|
return { kind: "rect", x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
901
916
|
}
|
|
902
917
|
var DEFAULT_FLATTEN_TOLERANCE = 0.5;
|
|
918
|
+
var MIN_FLATTEN_TOLERANCE = 1e-6;
|
|
919
|
+
function usableTolerance(tolerance) {
|
|
920
|
+
return tolerance > 0 ? tolerance : MIN_FLATTEN_TOLERANCE;
|
|
921
|
+
}
|
|
922
|
+
function finite6(a, b, c, d, e, f) {
|
|
923
|
+
return Number.isFinite(a) && Number.isFinite(b) && Number.isFinite(c) && Number.isFinite(d) && Number.isFinite(e) && Number.isFinite(f);
|
|
924
|
+
}
|
|
925
|
+
function finite8(a, b, c, d, e, f, g, h) {
|
|
926
|
+
return finite6(a, b, c, d, e, f) && Number.isFinite(g) && Number.isFinite(h);
|
|
927
|
+
}
|
|
903
928
|
function distPointToLine(px, py, ax, ay, bx, by) {
|
|
904
929
|
const dx = bx - ax;
|
|
905
930
|
const dy = by - ay;
|
|
906
|
-
const
|
|
907
|
-
if (
|
|
931
|
+
const len22 = dx * dx + dy * dy;
|
|
932
|
+
if (len22 === 0) {
|
|
908
933
|
const ex = px - ax;
|
|
909
934
|
const ey = py - ay;
|
|
910
935
|
return Math.sqrt(ex * ex + ey * ey);
|
|
911
936
|
}
|
|
912
937
|
const cross = (px - ax) * dy - (py - ay) * dx;
|
|
913
|
-
return Math.abs(cross) / Math.sqrt(
|
|
938
|
+
return Math.abs(cross) / Math.sqrt(len22);
|
|
914
939
|
}
|
|
915
940
|
function flattenCubic2(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
|
|
941
|
+
if (!finite8(x0, y0, x1, y1, x2, y2, x3, y3)) {
|
|
942
|
+
out.push(x3, y3);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
flattenCubicRec(x0, y0, x1, y1, x2, y2, x3, y3, usableTolerance(tolerance), out);
|
|
946
|
+
}
|
|
947
|
+
function flattenCubicRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
|
|
916
948
|
const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);
|
|
917
949
|
const d2 = distPointToLine(x2, y2, x0, y0, x3, y3);
|
|
918
950
|
if (Math.max(d1, d2) <= tolerance) {
|
|
@@ -925,10 +957,17 @@ function flattenCubic2(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
|
|
|
925
957
|
const x012 = (x01 + x12) * 0.5, y012 = (y01 + y12) * 0.5;
|
|
926
958
|
const x123 = (x12 + x23) * 0.5, y123 = (y12 + y23) * 0.5;
|
|
927
959
|
const x0123 = (x012 + x123) * 0.5, y0123 = (y012 + y123) * 0.5;
|
|
928
|
-
|
|
929
|
-
|
|
960
|
+
flattenCubicRec(x0, y0, x01, y01, x012, y012, x0123, y0123, tolerance, out);
|
|
961
|
+
flattenCubicRec(x0123, y0123, x123, y123, x23, y23, x3, y3, tolerance, out);
|
|
930
962
|
}
|
|
931
963
|
function flattenQuadratic(x0, y0, x1, y1, x2, y2, tolerance, out) {
|
|
964
|
+
if (!finite6(x0, y0, x1, y1, x2, y2)) {
|
|
965
|
+
out.push(x2, y2);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
flattenQuadraticRec(x0, y0, x1, y1, x2, y2, usableTolerance(tolerance), out);
|
|
969
|
+
}
|
|
970
|
+
function flattenQuadraticRec(x0, y0, x1, y1, x2, y2, tolerance, out) {
|
|
932
971
|
const d = distPointToLine(x1, y1, x0, y0, x2, y2);
|
|
933
972
|
if (d <= tolerance) {
|
|
934
973
|
out.push(x2, y2);
|
|
@@ -937,11 +976,16 @@ function flattenQuadratic(x0, y0, x1, y1, x2, y2, tolerance, out) {
|
|
|
937
976
|
const x01 = (x0 + x1) * 0.5, y01 = (y0 + y1) * 0.5;
|
|
938
977
|
const x12 = (x1 + x2) * 0.5, y12 = (y1 + y2) * 0.5;
|
|
939
978
|
const x012 = (x01 + x12) * 0.5, y012 = (y01 + y12) * 0.5;
|
|
940
|
-
|
|
941
|
-
|
|
979
|
+
flattenQuadraticRec(x0, y0, x01, y01, x012, y012, tolerance, out);
|
|
980
|
+
flattenQuadraticRec(x012, y012, x12, y12, x2, y2, tolerance, out);
|
|
942
981
|
}
|
|
943
982
|
function flattenCubicWithArcLen(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, arcOut) {
|
|
944
|
-
|
|
983
|
+
if (!finite8(x0, y0, x1, y1, x2, y2, x3, y3)) {
|
|
984
|
+
out.push(x3, y3);
|
|
985
|
+
arcOut.push(0);
|
|
986
|
+
return 0;
|
|
987
|
+
}
|
|
988
|
+
return flattenCubicArcRec(x0, y0, x1, y1, x2, y2, x3, y3, usableTolerance(tolerance), out, arcOut, 0);
|
|
945
989
|
}
|
|
946
990
|
function flattenCubicArcRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, arcOut, accum) {
|
|
947
991
|
const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);
|
|
@@ -966,7 +1010,12 @@ function flattenCubicArcRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, arcO
|
|
|
966
1010
|
return accum;
|
|
967
1011
|
}
|
|
968
1012
|
function flattenQuadraticWithArcLen(x0, y0, x1, y1, x2, y2, tolerance, out, arcOut) {
|
|
969
|
-
|
|
1013
|
+
if (!finite6(x0, y0, x1, y1, x2, y2)) {
|
|
1014
|
+
out.push(x2, y2);
|
|
1015
|
+
arcOut.push(0);
|
|
1016
|
+
return 0;
|
|
1017
|
+
}
|
|
1018
|
+
return flattenQuadraticArcRec(x0, y0, x1, y1, x2, y2, usableTolerance(tolerance), out, arcOut, 0);
|
|
970
1019
|
}
|
|
971
1020
|
function flattenQuadraticArcRec(x0, y0, x1, y1, x2, y2, tolerance, out, arcOut, accum) {
|
|
972
1021
|
const d = distPointToLine(x1, y1, x0, y0, x2, y2);
|
|
@@ -1159,22 +1208,6 @@ function polylineWithinThreshold(path, px, py, t2, tolerance) {
|
|
|
1159
1208
|
if (subOpen && checkSegments(false)) return true;
|
|
1160
1209
|
return false;
|
|
1161
1210
|
}
|
|
1162
|
-
function pointSegmentDist2(px, py, ax, ay, bx, by) {
|
|
1163
|
-
const dx = bx - ax;
|
|
1164
|
-
const dy = by - ay;
|
|
1165
|
-
const len2 = dx * dx + dy * dy;
|
|
1166
|
-
if (len2 === 0) {
|
|
1167
|
-
const ex2 = px - ax, ey2 = py - ay;
|
|
1168
|
-
return ex2 * ex2 + ey2 * ey2;
|
|
1169
|
-
}
|
|
1170
|
-
let t = ((px - ax) * dx + (py - ay) * dy) / len2;
|
|
1171
|
-
if (t < 0) t = 0;
|
|
1172
|
-
else if (t > 1) t = 1;
|
|
1173
|
-
const cx = ax + t * dx;
|
|
1174
|
-
const cy = ay + t * dy;
|
|
1175
|
-
const ex = px - cx, ey = py - cy;
|
|
1176
|
-
return ex * ex + ey * ey;
|
|
1177
|
-
}
|
|
1178
1211
|
var PathBuilder = class _PathBuilder {
|
|
1179
1212
|
cmds = [];
|
|
1180
1213
|
xs = [];
|
|
@@ -1563,7 +1596,7 @@ function createDeleteOp(args) {
|
|
|
1563
1596
|
}
|
|
1564
1597
|
registerOpFactory("delete", (args) => createDeleteOp(args));
|
|
1565
1598
|
|
|
1566
|
-
// ../core/dist/chunk-
|
|
1599
|
+
// ../core/dist/chunk-CSMF654J.js
|
|
1567
1600
|
function createTransformOp(args) {
|
|
1568
1601
|
const { id, from, to, label, coalesceKey = `transform:${id}` } = args;
|
|
1569
1602
|
return {
|
|
@@ -1644,7 +1677,7 @@ function snap(strategy, opts = {}) {
|
|
|
1644
1677
|
};
|
|
1645
1678
|
}
|
|
1646
1679
|
|
|
1647
|
-
// ../core/dist/chunk-
|
|
1680
|
+
// ../core/dist/chunk-J7LUY47N.js
|
|
1648
1681
|
function lockAspectWithModifier(opts = {}) {
|
|
1649
1682
|
const { key = "shift" } = opts;
|
|
1650
1683
|
return {
|
|
@@ -2850,6 +2883,7 @@ function createHistory(adapter, options = {}) {
|
|
|
2850
2883
|
const logger = options.debug ?? SILENT;
|
|
2851
2884
|
let nextEntryId = 1;
|
|
2852
2885
|
let version = 0;
|
|
2886
|
+
let coalesceAnchorVersion = -1;
|
|
2853
2887
|
const listeners = /* @__PURE__ */ new Set();
|
|
2854
2888
|
function bump() {
|
|
2855
2889
|
version++;
|
|
@@ -2890,6 +2924,7 @@ function createHistory(adapter, options = {}) {
|
|
|
2890
2924
|
}
|
|
2891
2925
|
function canCoalesce(top, incoming) {
|
|
2892
2926
|
if (coalesceWindowMs <= 0) return false;
|
|
2927
|
+
if (version !== coalesceAnchorVersion) return false;
|
|
2893
2928
|
if (now() - top.timestamp > coalesceWindowMs) return false;
|
|
2894
2929
|
if (top.forwardOps.length === 0 || incoming.length === 0) return false;
|
|
2895
2930
|
if (top.forwardOps.length !== incoming.length) return false;
|
|
@@ -2927,9 +2962,9 @@ function createHistory(adapter, options = {}) {
|
|
|
2927
2962
|
for (const id of incoming) merged.add(id);
|
|
2928
2963
|
top.touchedIds = merged;
|
|
2929
2964
|
}
|
|
2930
|
-
dropRedo();
|
|
2931
2965
|
logger.log(`coalesce '${label}' into entry id=${top.id} (${ops.length} ops)`);
|
|
2932
2966
|
bump();
|
|
2967
|
+
coalesceAnchorVersion = version;
|
|
2933
2968
|
return;
|
|
2934
2969
|
}
|
|
2935
2970
|
logger.log(`push '${label}' (${ops.length} ops)`);
|
|
@@ -2937,6 +2972,7 @@ function createHistory(adapter, options = {}) {
|
|
|
2937
2972
|
dropRedo();
|
|
2938
2973
|
enforceLimit();
|
|
2939
2974
|
bump();
|
|
2975
|
+
coalesceAnchorVersion = version;
|
|
2940
2976
|
}
|
|
2941
2977
|
return {
|
|
2942
2978
|
apply(op, label) {
|
|
@@ -3042,7 +3078,11 @@ function createHistory(adapter, options = {}) {
|
|
|
3042
3078
|
return j;
|
|
3043
3079
|
},
|
|
3044
3080
|
resumeJournal(journal) {
|
|
3081
|
+
if (activeJournal !== null && activeJournal !== journal && activeJournal.isActive()) {
|
|
3082
|
+
throw new Error("A journal is already active \u2014 commit, cancel, or suspend it first");
|
|
3083
|
+
}
|
|
3045
3084
|
_resumeJournalInternal(journal);
|
|
3085
|
+
activeJournal = journal;
|
|
3046
3086
|
},
|
|
3047
3087
|
restore(snapshot) {
|
|
3048
3088
|
undoStack.length = 0;
|
|
@@ -3123,9 +3163,8 @@ function serialToEntry(se, custom, logger) {
|
|
|
3123
3163
|
label: se.label,
|
|
3124
3164
|
forwardOps,
|
|
3125
3165
|
baseOps,
|
|
3126
|
-
//
|
|
3127
|
-
//
|
|
3128
|
-
// freshly-typed one regardless of when it was originally pushed.
|
|
3166
|
+
// Coalescing is a within-session concept; a restored entry is never a
|
|
3167
|
+
// coalesce anchor, so its timestamp only has to be non-null.
|
|
3129
3168
|
timestamp: 0,
|
|
3130
3169
|
// Re-derive touchedIds from the rebuilt ops rather than trying to
|
|
3131
3170
|
// round-trip the Set through the serialized form (Sets aren't JSON-safe).
|
|
@@ -3133,7 +3172,7 @@ function serialToEntry(se, custom, logger) {
|
|
|
3133
3172
|
};
|
|
3134
3173
|
}
|
|
3135
3174
|
|
|
3136
|
-
// ../core/dist/chunk-
|
|
3175
|
+
// ../core/dist/chunk-AYGSXNY3.js
|
|
3137
3176
|
function worldToScreen(worldX, worldY, view) {
|
|
3138
3177
|
return [view.panX + worldX * view.zoom.x, view.panY + worldY * view.zoom.y];
|
|
3139
3178
|
}
|
|
@@ -3570,13 +3609,20 @@ var GLTextureCache = class {
|
|
|
3570
3609
|
}
|
|
3571
3610
|
gl;
|
|
3572
3611
|
map = /* @__PURE__ */ new Map();
|
|
3612
|
+
wraps = /* @__PURE__ */ new Map();
|
|
3573
3613
|
has(id) {
|
|
3574
3614
|
return this.map.has(id);
|
|
3575
3615
|
}
|
|
3576
|
-
/** Uploads once per id
|
|
3577
|
-
*
|
|
3616
|
+
/** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
|
|
3617
|
+
* from what the id was last uploaded under: the same registered image can
|
|
3618
|
+
* be a clamped shader texture in one draw and a repeating pattern tile in
|
|
3619
|
+
* the next, and first-upload-wins gave the second one the first one's mode. */
|
|
3578
3620
|
upload(id, source, wrap = "clamp") {
|
|
3579
|
-
|
|
3621
|
+
const cached2 = this.map.get(id);
|
|
3622
|
+
if (cached2) {
|
|
3623
|
+
if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached2, wrap);
|
|
3624
|
+
return id;
|
|
3625
|
+
}
|
|
3580
3626
|
const gl = this.gl;
|
|
3581
3627
|
const tex = gl.createTexture();
|
|
3582
3628
|
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
@@ -3589,8 +3635,18 @@ var GLTextureCache = class {
|
|
|
3589
3635
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);
|
|
3590
3636
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
3591
3637
|
this.map.set(id, tex);
|
|
3638
|
+
this.wraps.set(id, wrap);
|
|
3592
3639
|
return id;
|
|
3593
3640
|
}
|
|
3641
|
+
applyWrap(id, tex, wrap) {
|
|
3642
|
+
const gl = this.gl;
|
|
3643
|
+
const mode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
3644
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
3645
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);
|
|
3646
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);
|
|
3647
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
3648
|
+
this.wraps.set(id, wrap);
|
|
3649
|
+
}
|
|
3594
3650
|
/** Create a single-channel R8 texture from raw bytes (full upload).
|
|
3595
3651
|
* No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;
|
|
3596
3652
|
* UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */
|
|
@@ -3609,6 +3665,7 @@ var GLTextureCache = class {
|
|
|
3609
3665
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
3610
3666
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
3611
3667
|
this.map.set(id, tex);
|
|
3668
|
+
this.wraps.set(id, "clamp");
|
|
3612
3669
|
}
|
|
3613
3670
|
/** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */
|
|
3614
3671
|
subImageR8(id, x, y, w, h, data) {
|
|
@@ -3640,6 +3697,7 @@ var GLTextureCache = class {
|
|
|
3640
3697
|
gl.deleteTexture(tex);
|
|
3641
3698
|
}
|
|
3642
3699
|
this.map.clear();
|
|
3700
|
+
this.wraps.clear();
|
|
3643
3701
|
}
|
|
3644
3702
|
};
|
|
3645
3703
|
var GLImageCache = class {
|
|
@@ -4289,6 +4347,13 @@ var GroupState = class {
|
|
|
4289
4347
|
this.alphaStack.push(nextAlpha);
|
|
4290
4348
|
this.colorMatrixStack.push(nextCM);
|
|
4291
4349
|
}
|
|
4350
|
+
/** Drop every pushed frame, leaving the root. A frame that throws part-way
|
|
4351
|
+
* down the tree never pops, and the next frame would draw under leftovers. */
|
|
4352
|
+
reset() {
|
|
4353
|
+
this.transformStack.length = 1;
|
|
4354
|
+
this.alphaStack.length = 1;
|
|
4355
|
+
this.colorMatrixStack.length = 1;
|
|
4356
|
+
}
|
|
4292
4357
|
pop() {
|
|
4293
4358
|
if (this.transformStack.length <= 1) {
|
|
4294
4359
|
throw new Error("GroupState.pop: cannot pop root frame");
|
|
@@ -4936,16 +5001,28 @@ function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
|
4936
5001
|
return align === "center" ? slack / 2 : slack;
|
|
4937
5002
|
}
|
|
4938
5003
|
var NUM_RE = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
|
|
5004
|
+
var NUM_RE_AT = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/y;
|
|
5005
|
+
function isCommandLetter(d, j) {
|
|
5006
|
+
const c = d[j];
|
|
5007
|
+
if (!/[A-Za-z]/.test(c)) return false;
|
|
5008
|
+
if (c !== "e" && c !== "E") return true;
|
|
5009
|
+
if (!/[\d.]/.test(d[j - 1] ?? "")) return true;
|
|
5010
|
+
const k = d[j + 1] === "+" || d[j + 1] === "-" ? j + 2 : j + 1;
|
|
5011
|
+
return !/\d/.test(d[k] ?? "");
|
|
5012
|
+
}
|
|
4939
5013
|
function tokenize(d) {
|
|
4940
5014
|
const out = [];
|
|
4941
5015
|
let i = 0;
|
|
4942
5016
|
while (i < d.length) {
|
|
4943
|
-
|
|
4944
|
-
if (/[A-Za-z]/.test(ch)) {
|
|
5017
|
+
if (isCommandLetter(d, i)) {
|
|
4945
5018
|
let j = i + 1;
|
|
4946
|
-
while (j < d.length &&
|
|
5019
|
+
while (j < d.length && !isCommandLetter(d, j)) j++;
|
|
4947
5020
|
const argSlice = d.slice(i + 1, j);
|
|
4948
|
-
|
|
5021
|
+
const cmd = d[i];
|
|
5022
|
+
out.push({
|
|
5023
|
+
cmd,
|
|
5024
|
+
args: cmd === "A" || cmd === "a" ? readArcArgs(argSlice) : readNumbers(argSlice)
|
|
5025
|
+
});
|
|
4949
5026
|
i = j;
|
|
4950
5027
|
} else {
|
|
4951
5028
|
i++;
|
|
@@ -4963,6 +5040,30 @@ function readNumbers(s) {
|
|
|
4963
5040
|
}
|
|
4964
5041
|
return out;
|
|
4965
5042
|
}
|
|
5043
|
+
function readArcArgs(s) {
|
|
5044
|
+
const out = [];
|
|
5045
|
+
let i = 0;
|
|
5046
|
+
while (i < s.length) {
|
|
5047
|
+
const c = s[i];
|
|
5048
|
+
if (c === "," || c === " " || c === " " || c === "\n" || c === "\r") {
|
|
5049
|
+
i++;
|
|
5050
|
+
continue;
|
|
5051
|
+
}
|
|
5052
|
+
const slot = out.length % 7;
|
|
5053
|
+
if (slot === 3 || slot === 4) {
|
|
5054
|
+
if (c !== "0" && c !== "1") return out;
|
|
5055
|
+
out.push(c === "1" ? 1 : 0);
|
|
5056
|
+
i++;
|
|
5057
|
+
continue;
|
|
5058
|
+
}
|
|
5059
|
+
NUM_RE_AT.lastIndex = i;
|
|
5060
|
+
const m = NUM_RE_AT.exec(s);
|
|
5061
|
+
if (!m) return out;
|
|
5062
|
+
out.push(Number(m[0]));
|
|
5063
|
+
i = NUM_RE_AT.lastIndex;
|
|
5064
|
+
}
|
|
5065
|
+
return out;
|
|
5066
|
+
}
|
|
4966
5067
|
var ARG_COUNTS = {
|
|
4967
5068
|
M: 2,
|
|
4968
5069
|
L: 2,
|
|
@@ -5153,9 +5254,9 @@ function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep,
|
|
|
5153
5254
|
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
5154
5255
|
const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2;
|
|
5155
5256
|
const angle = (ux, uy, vx, vy) => {
|
|
5156
|
-
const
|
|
5257
|
+
const dot2 = ux * vx + uy * vy;
|
|
5157
5258
|
const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
|
|
5158
|
-
let a = Math.acos(Math.max(-1, Math.min(1,
|
|
5259
|
+
let a = Math.acos(Math.max(-1, Math.min(1, dot2 / len)));
|
|
5159
5260
|
if (ux * vy - uy * vx < 0) a = -a;
|
|
5160
5261
|
return a;
|
|
5161
5262
|
};
|
|
@@ -5867,42 +5968,42 @@ var MAX_VERTICES_PER_BATCH = 32768;
|
|
|
5867
5968
|
var FLOATS_PER_VERTEX = 6;
|
|
5868
5969
|
var INITIAL_VERTICES = 256;
|
|
5869
5970
|
var INITIAL_INDICES = 384;
|
|
5971
|
+
var SOLID_RING_SIZE = 64;
|
|
5972
|
+
var SOLID_RING_SLOT_VERTICES = 1024;
|
|
5973
|
+
var SOLID_RING_SLOT_INDICES = 3072;
|
|
5974
|
+
var SOLID_LARGE_RING_SIZE = 4;
|
|
5975
|
+
function doubledTo(from, need) {
|
|
5976
|
+
let capacity = from;
|
|
5977
|
+
while (capacity < need) capacity *= 2;
|
|
5978
|
+
return capacity;
|
|
5979
|
+
}
|
|
5870
5980
|
var SolidBatch = class {
|
|
5871
5981
|
gl;
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5982
|
+
aPos;
|
|
5983
|
+
aColor;
|
|
5984
|
+
/** Cycled per flush, and created on first use so a renderer that flushes
|
|
5985
|
+
* rarely allocates as few slots as it flushes. */
|
|
5986
|
+
ring = new Array(SOLID_RING_SIZE).fill(void 0);
|
|
5987
|
+
next = 0;
|
|
5988
|
+
/** The same, for flushes past a slot's capacity; these sets grow to fit. */
|
|
5989
|
+
largeRing = new Array(SOLID_LARGE_RING_SIZE).fill(void 0);
|
|
5990
|
+
nextLarge = 0;
|
|
5875
5991
|
verts = new Float32Array(INITIAL_VERTICES * FLOATS_PER_VERTEX);
|
|
5876
5992
|
idx = new Uint32Array(INITIAL_INDICES);
|
|
5877
5993
|
nVerts = 0;
|
|
5878
5994
|
nIdx = 0;
|
|
5879
|
-
/**
|
|
5880
|
-
|
|
5881
|
-
|
|
5995
|
+
/** Whether the staged run is rects alone, so its indices are the canonical
|
|
5996
|
+
* quad pattern and a slot already holding that pattern needs no upload. */
|
|
5997
|
+
pureRects = true;
|
|
5882
5998
|
constructor(gl, prog) {
|
|
5883
5999
|
const aPos = prog.attribute("a_position");
|
|
5884
6000
|
const aColor = prog.attribute("a_vertexColor");
|
|
5885
6001
|
if (aPos === void 0 || aColor === void 0) {
|
|
5886
6002
|
throw new Error("SolidBatch: vertex-color program is missing a_position / a_vertexColor");
|
|
5887
6003
|
}
|
|
5888
|
-
const vao = gl.createVertexArray();
|
|
5889
|
-
const vbo = gl.createBuffer();
|
|
5890
|
-
const ibo = gl.createBuffer();
|
|
5891
|
-
if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
|
|
5892
6004
|
this.gl = gl;
|
|
5893
|
-
this.
|
|
5894
|
-
this.
|
|
5895
|
-
this.ibo = ibo;
|
|
5896
|
-
const stride = FLOATS_PER_VERTEX * 4;
|
|
5897
|
-
gl.bindVertexArray(vao);
|
|
5898
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
5899
|
-
gl.enableVertexAttribArray(aPos);
|
|
5900
|
-
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
|
|
5901
|
-
gl.enableVertexAttribArray(aColor);
|
|
5902
|
-
gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, stride, 8);
|
|
5903
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
5904
|
-
gl.bindVertexArray(null);
|
|
5905
|
-
this.growGpu(INITIAL_VERTICES, INITIAL_INDICES);
|
|
6005
|
+
this.aPos = aPos;
|
|
6006
|
+
this.aColor = aColor;
|
|
5906
6007
|
}
|
|
5907
6008
|
get length() {
|
|
5908
6009
|
return this.nIdx;
|
|
@@ -5972,6 +6073,7 @@ var SolidBatch = class {
|
|
|
5972
6073
|
const srcIdx = mesh.indices;
|
|
5973
6074
|
const n = src.length >> 1;
|
|
5974
6075
|
this.reserve(n, srcIdx.length);
|
|
6076
|
+
this.pureRects = false;
|
|
5975
6077
|
const v = this.verts;
|
|
5976
6078
|
let i = this.nVerts * FLOATS_PER_VERTEX;
|
|
5977
6079
|
const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
|
|
@@ -5992,67 +6094,109 @@ var SolidBatch = class {
|
|
|
5992
6094
|
this.nVerts += n;
|
|
5993
6095
|
this.nIdx += srcIdx.length;
|
|
5994
6096
|
}
|
|
5995
|
-
/** Upload the staged geometry and bind
|
|
5996
|
-
* count for the caller's `drawElements`. */
|
|
6097
|
+
/** Upload the staged geometry into the next set of buffers and bind its VAO.
|
|
6098
|
+
* Returns the index count for the caller's `drawElements`. */
|
|
5997
6099
|
uploadAndBind() {
|
|
5998
6100
|
const gl = this.gl;
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
gl.bindVertexArray(this.vao);
|
|
6003
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
|
|
6101
|
+
const set = this.nVerts <= SOLID_RING_SLOT_VERTICES && this.nIdx <= SOLID_RING_SLOT_INDICES ? this.nextRingSlot() : this.nextLargeSlot();
|
|
6102
|
+
gl.bindVertexArray(set.vao);
|
|
6103
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
|
|
6004
6104
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verts, 0, this.nVerts * FLOATS_PER_VERTEX);
|
|
6005
|
-
|
|
6105
|
+
const rects = this.pureRects ? this.nVerts >> 2 : -1;
|
|
6106
|
+
if (rects < 0 || rects !== set.indexRects) {
|
|
6107
|
+
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this.idx, 0, this.nIdx);
|
|
6108
|
+
set.indexRects = rects;
|
|
6109
|
+
}
|
|
6006
6110
|
return this.nIdx;
|
|
6007
6111
|
}
|
|
6008
6112
|
reset() {
|
|
6009
6113
|
this.nVerts = 0;
|
|
6010
6114
|
this.nIdx = 0;
|
|
6115
|
+
this.pureRects = true;
|
|
6011
6116
|
}
|
|
6012
6117
|
dispose() {
|
|
6013
|
-
const
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6118
|
+
for (const set of [...this.ring, ...this.largeRing]) {
|
|
6119
|
+
if (set) this.deleteSet(set);
|
|
6120
|
+
}
|
|
6121
|
+
this.ring.fill(void 0);
|
|
6122
|
+
this.largeRing.fill(void 0);
|
|
6017
6123
|
}
|
|
6018
6124
|
/** Grow the CPU arrays so `vertices` / `indices` more fit. */
|
|
6019
6125
|
reserve(vertices, indices) {
|
|
6020
6126
|
const needVerts = (this.nVerts + vertices) * FLOATS_PER_VERTEX;
|
|
6021
6127
|
if (needVerts > this.verts.length) {
|
|
6022
|
-
|
|
6023
|
-
while (len < needVerts) len *= 2;
|
|
6024
|
-
const grown = new Float32Array(len);
|
|
6128
|
+
const grown = new Float32Array(doubledTo(this.verts.length, needVerts));
|
|
6025
6129
|
grown.set(this.verts);
|
|
6026
6130
|
this.verts = grown;
|
|
6027
6131
|
}
|
|
6028
6132
|
const needIdx = this.nIdx + indices;
|
|
6029
6133
|
if (needIdx > this.idx.length) {
|
|
6030
|
-
|
|
6031
|
-
while (len < needIdx) len *= 2;
|
|
6032
|
-
const grown = new Uint32Array(len);
|
|
6134
|
+
const grown = new Uint32Array(doubledTo(this.idx.length, needIdx));
|
|
6033
6135
|
grown.set(this.idx);
|
|
6034
6136
|
this.idx = grown;
|
|
6035
6137
|
}
|
|
6036
6138
|
}
|
|
6037
|
-
|
|
6038
|
-
|
|
6139
|
+
nextRingSlot() {
|
|
6140
|
+
const slot = this.next;
|
|
6141
|
+
this.next = (slot + 1) % SOLID_RING_SIZE;
|
|
6142
|
+
const existing = this.ring[slot];
|
|
6143
|
+
if (existing) return existing;
|
|
6144
|
+
const set = this.createSet(SOLID_RING_SLOT_VERTICES, SOLID_RING_SLOT_INDICES);
|
|
6145
|
+
this.ring[slot] = set;
|
|
6146
|
+
return set;
|
|
6147
|
+
}
|
|
6148
|
+
nextLargeSlot() {
|
|
6039
6149
|
const gl = this.gl;
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6150
|
+
const slot = this.nextLarge;
|
|
6151
|
+
this.nextLarge = (slot + 1) % SOLID_LARGE_RING_SIZE;
|
|
6152
|
+
let set = this.largeRing[slot];
|
|
6153
|
+
if (!set) {
|
|
6154
|
+
set = this.createSet(
|
|
6155
|
+
doubledTo(SOLID_RING_SLOT_VERTICES, this.nVerts),
|
|
6156
|
+
doubledTo(SOLID_RING_SLOT_INDICES, this.nIdx)
|
|
6157
|
+
);
|
|
6158
|
+
this.largeRing[slot] = set;
|
|
6159
|
+
return set;
|
|
6160
|
+
}
|
|
6161
|
+
if (this.nVerts > set.vertexCapacity) {
|
|
6162
|
+
set.vertexCapacity = doubledTo(set.vertexCapacity, this.nVerts);
|
|
6163
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
|
|
6164
|
+
gl.bufferData(gl.ARRAY_BUFFER, set.vertexCapacity * FLOATS_PER_VERTEX * 4, gl.DYNAMIC_DRAW);
|
|
6165
|
+
}
|
|
6166
|
+
if (this.nIdx > set.indexCapacity) {
|
|
6167
|
+
set.indexCapacity = doubledTo(set.indexCapacity, this.nIdx);
|
|
6168
|
+
gl.bindVertexArray(set.vao);
|
|
6169
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, set.ibo);
|
|
6170
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, set.indexCapacity * 4, gl.DYNAMIC_DRAW);
|
|
6053
6171
|
gl.bindVertexArray(null);
|
|
6054
|
-
|
|
6172
|
+
set.indexRects = -1;
|
|
6055
6173
|
}
|
|
6174
|
+
return set;
|
|
6175
|
+
}
|
|
6176
|
+
createSet(vertices, indices) {
|
|
6177
|
+
const gl = this.gl;
|
|
6178
|
+
const vao = gl.createVertexArray();
|
|
6179
|
+
const vbo = gl.createBuffer();
|
|
6180
|
+
const ibo = gl.createBuffer();
|
|
6181
|
+
if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
|
|
6182
|
+
const stride = FLOATS_PER_VERTEX * 4;
|
|
6183
|
+
gl.bindVertexArray(vao);
|
|
6184
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
6185
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertices * stride, gl.DYNAMIC_DRAW);
|
|
6186
|
+
gl.enableVertexAttribArray(this.aPos);
|
|
6187
|
+
gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, stride, 0);
|
|
6188
|
+
gl.enableVertexAttribArray(this.aColor);
|
|
6189
|
+
gl.vertexAttribPointer(this.aColor, 4, gl.FLOAT, false, stride, 8);
|
|
6190
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
6191
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices * 4, gl.DYNAMIC_DRAW);
|
|
6192
|
+
gl.bindVertexArray(null);
|
|
6193
|
+
return { vao, vbo, ibo, vertexCapacity: vertices, indexCapacity: indices, indexRects: -1 };
|
|
6194
|
+
}
|
|
6195
|
+
deleteSet(set) {
|
|
6196
|
+
const gl = this.gl;
|
|
6197
|
+
gl.deleteBuffer(set.vbo);
|
|
6198
|
+
gl.deleteBuffer(set.ibo);
|
|
6199
|
+
gl.deleteVertexArray(set.vao);
|
|
6056
6200
|
}
|
|
6057
6201
|
};
|
|
6058
6202
|
var FRAME_UPLOADS = /* @__PURE__ */ new WeakMap();
|
|
@@ -6075,6 +6219,25 @@ function sameValues(prev, next) {
|
|
|
6075
6219
|
for (let i = 0; i < prev.length; i++) if (prev[i] !== next[i]) return false;
|
|
6076
6220
|
return true;
|
|
6077
6221
|
}
|
|
6222
|
+
function setColorUniform(ctx, prog, r, g, b, a) {
|
|
6223
|
+
const loc = prog.uniform("u_color");
|
|
6224
|
+
if (loc === void 0) return;
|
|
6225
|
+
const uploaded = uploadedFor(ctx, prog);
|
|
6226
|
+
const prev = uploaded.color;
|
|
6227
|
+
if (prev !== void 0 && prev[0] === r && prev[1] === g && prev[2] === b && prev[3] === a) {
|
|
6228
|
+
return;
|
|
6229
|
+
}
|
|
6230
|
+
ctx.gl.uniform4f(loc, r, g, b, a);
|
|
6231
|
+
uploaded.color = [r, g, b, a];
|
|
6232
|
+
}
|
|
6233
|
+
function setAlphaUniform(ctx, prog, alpha) {
|
|
6234
|
+
const loc = prog.uniform("u_alpha");
|
|
6235
|
+
if (loc === void 0) return;
|
|
6236
|
+
const uploaded = uploadedFor(ctx, prog);
|
|
6237
|
+
if (uploaded.alpha === alpha) return;
|
|
6238
|
+
ctx.gl.uniform1f(loc, alpha);
|
|
6239
|
+
uploaded.alpha = alpha;
|
|
6240
|
+
}
|
|
6078
6241
|
var BATCH_MODEL = mat3.identity();
|
|
6079
6242
|
function projFor(ctx) {
|
|
6080
6243
|
let proj = FRAME_PROJ.get(ctx);
|
|
@@ -6367,8 +6530,8 @@ function flushSolids(ctx) {
|
|
|
6367
6530
|
gl.useProgram(prog.handle);
|
|
6368
6531
|
const indexCount = batch.uploadAndBind();
|
|
6369
6532
|
setProjAndModel(ctx, prog, BATCH_MODEL);
|
|
6370
|
-
|
|
6371
|
-
|
|
6533
|
+
setColorUniform(ctx, prog, 1, 1, 1, 1);
|
|
6534
|
+
setAlphaUniform(ctx, prog, staged.alpha);
|
|
6372
6535
|
setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
|
|
6373
6536
|
applyClipTest(ctx, staged.clipDepth);
|
|
6374
6537
|
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
|
|
@@ -6433,10 +6596,9 @@ function setProjAndModel(ctx, prog, model = ctx.state.transform) {
|
|
|
6433
6596
|
}
|
|
6434
6597
|
}
|
|
6435
6598
|
function setSolidPaintUniforms(ctx, prog, color, opacity) {
|
|
6436
|
-
const gl = ctx.gl;
|
|
6437
6599
|
const [r, g, b, a] = resolveColor(color);
|
|
6438
|
-
|
|
6439
|
-
|
|
6600
|
+
setColorUniform(ctx, prog, r, g, b, a * (opacity ?? 1));
|
|
6601
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
6440
6602
|
}
|
|
6441
6603
|
function drawPathFillByKind(ctx, fill, handle) {
|
|
6442
6604
|
const kind = fill.fill ?? "solid";
|
|
@@ -6482,7 +6644,7 @@ function drawPathFillPattern(ctx, fill, handle) {
|
|
|
6482
6644
|
ctx.textureCache.bind(tex.id, 0);
|
|
6483
6645
|
gl.uniform1i(ctx.patternFill.uniform("u_sampler"), 0);
|
|
6484
6646
|
gl.uniform1f(ctx.patternFill.uniform("u_opacity"), fill.opacity ?? 1);
|
|
6485
|
-
|
|
6647
|
+
setAlphaUniform(ctx, ctx.patternFill, ctx.state.alpha);
|
|
6486
6648
|
applyClipTest(ctx);
|
|
6487
6649
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
6488
6650
|
gl.bindVertexArray(null);
|
|
@@ -6506,7 +6668,7 @@ function drawPathFillGradient(ctx, fill, handle) {
|
|
|
6506
6668
|
gl.uniformMatrix3fv(ctx.gradFill.uniform("u_worldInv"), false, gradientSpaceInverse(ctx, fill.units));
|
|
6507
6669
|
ctx.gradRampCache.bind(key, 0);
|
|
6508
6670
|
gl.uniform1i(ctx.gradFill.uniform("u_ramp"), 0);
|
|
6509
|
-
|
|
6671
|
+
setAlphaUniform(ctx, ctx.gradFill, ctx.state.alpha);
|
|
6510
6672
|
gl.uniform1f(ctx.gradFill.uniform("u_opacity"), fill.opacity ?? 1);
|
|
6511
6673
|
if (fill.fill === "linear-gradient") {
|
|
6512
6674
|
gl.uniform1i(ctx.gradFill.uniform("u_gradKind"), 0);
|
|
@@ -6656,6 +6818,7 @@ function drawPathStrokeUnclipped(ctx, cmd) {
|
|
|
6656
6818
|
gl.enableVertexAttribArray(aVColorLoc);
|
|
6657
6819
|
gl.vertexAttribPointer(aVColorLoc, 4, gl.FLOAT, false, 0, 0);
|
|
6658
6820
|
}
|
|
6821
|
+
applyClipTest(ctx);
|
|
6659
6822
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
6660
6823
|
gl.bindVertexArray(null);
|
|
6661
6824
|
gl.deleteBuffer(colorVbo);
|
|
@@ -6771,7 +6934,7 @@ function drawText(ctx, cmd) {
|
|
|
6771
6934
|
preparedPrograms.add(prog);
|
|
6772
6935
|
setProjAndModel(ctx, prog);
|
|
6773
6936
|
setColorMatrixUniforms(ctx, prog);
|
|
6774
|
-
|
|
6937
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
6775
6938
|
}
|
|
6776
6939
|
drawTextGroup(ctx, group, prog, dx, dy);
|
|
6777
6940
|
}
|
|
@@ -6889,8 +7052,8 @@ function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
|
6889
7052
|
if (!ibo) throw new Error("drawTextDecorations: createBuffer (IBO) returned null");
|
|
6890
7053
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
6891
7054
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.DYNAMIC_DRAW);
|
|
6892
|
-
|
|
6893
|
-
|
|
7055
|
+
setColorUniform(ctx, prog, rgba[0], rgba[1], rgba[2], rgba[3]);
|
|
7056
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
6894
7057
|
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0);
|
|
6895
7058
|
gl.bindVertexArray(null);
|
|
6896
7059
|
gl.deleteVertexArray(vao);
|
|
@@ -6974,7 +7137,7 @@ function drawTextGroup(ctx, group, prog, dx, dy) {
|
|
|
6974
7137
|
if ("color" in group.fill) {
|
|
6975
7138
|
[r, g, b, a] = resolveColor(group.fill.color);
|
|
6976
7139
|
}
|
|
6977
|
-
|
|
7140
|
+
setColorUniform(ctx, prog, r, g, b, a);
|
|
6978
7141
|
const synthBoldAmount = group.synthetic.bold ? 0.08 : 0;
|
|
6979
7142
|
const uSynthBold = prog.uniform("u_synthBold");
|
|
6980
7143
|
if (uSynthBold !== void 0) gl.uniform1f(uSynthBold, synthBoldAmount);
|
|
@@ -7078,7 +7241,7 @@ function drawImage(ctx, cmd) {
|
|
|
7078
7241
|
);
|
|
7079
7242
|
gl.uniform1i(prog.uniform("u_sampler"), 0);
|
|
7080
7243
|
gl.uniform1f(prog.uniform("u_opacity"), cmd.opacity ?? 1);
|
|
7081
|
-
|
|
7244
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
7082
7245
|
applyClipTest(ctx);
|
|
7083
7246
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
|
|
7084
7247
|
gl.bindVertexArray(null);
|
|
@@ -7293,6 +7456,8 @@ var WeaselRenderer = class {
|
|
|
7293
7456
|
const program = new ShaderProgram(this.gl, vertSrc, fragSrc);
|
|
7294
7457
|
program.lookupUniforms([...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(fragSrc)]);
|
|
7295
7458
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
7459
|
+
const previous = this.programRegistry.get(handle.id);
|
|
7460
|
+
if (previous) this.gl.deleteProgram(previous.handle);
|
|
7296
7461
|
this.programRegistry.set(handle.id, program);
|
|
7297
7462
|
}
|
|
7298
7463
|
applyViewport() {
|
|
@@ -7390,7 +7555,7 @@ var WeaselRenderer = class {
|
|
|
7390
7555
|
}
|
|
7391
7556
|
this.meshCache.freeTransient();
|
|
7392
7557
|
this.meshCache.drainPendingDeletes();
|
|
7393
|
-
for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill]) {
|
|
7558
|
+
for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill, this.patternFill]) {
|
|
7394
7559
|
gl.deleteProgram(prog.handle);
|
|
7395
7560
|
}
|
|
7396
7561
|
for (const prog of this.programRegistry.values()) {
|
|
@@ -7417,6 +7582,7 @@ var WeaselRenderer = class {
|
|
|
7417
7582
|
const gl = this.gl;
|
|
7418
7583
|
this.meshCache.drainPendingDeletes();
|
|
7419
7584
|
resetBakeBudget(this.bakeBudget);
|
|
7585
|
+
this.groupState.reset();
|
|
7420
7586
|
gl.stencilMask(255);
|
|
7421
7587
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
7422
7588
|
const ctx = {
|
|
@@ -10986,7 +11152,7 @@ var reorderBackwardAction = {
|
|
|
10986
11152
|
}
|
|
10987
11153
|
],
|
|
10988
11154
|
eligible: { capability: "edits-page" },
|
|
10989
|
-
requires: ["selection", "scene"],
|
|
11155
|
+
requires: ["selection", "scene", "applyOps"],
|
|
10990
11156
|
invoker: {
|
|
10991
11157
|
timing: "immediate",
|
|
10992
11158
|
run: (deps, params) => {
|
|
@@ -11126,6 +11292,7 @@ function makeAlignAction(edge) {
|
|
|
11126
11292
|
icon: ICON_FOR[edge],
|
|
11127
11293
|
group: "align",
|
|
11128
11294
|
eligible: { capability: "transforms-selection" },
|
|
11295
|
+
requires: ["selection", "scene"],
|
|
11129
11296
|
// No default keybindings — six edges/centers don't fit a clean default
|
|
11130
11297
|
// chord set. Wire bindings explicitly via the actions registry override map.
|
|
11131
11298
|
invoker: {
|
|
@@ -11137,7 +11304,14 @@ function makeAlignAction(edge) {
|
|
|
11137
11304
|
alignSelection(selection, scene, edge);
|
|
11138
11305
|
}
|
|
11139
11306
|
},
|
|
11140
|
-
|
|
11307
|
+
// Deps-aware, matching `alignSelection`'s own `ids.length < 2` guard: a
|
|
11308
|
+
// constant disabled reason greys the entry out forever (see
|
|
11309
|
+
// `requiresSelection`).
|
|
11310
|
+
enabled: (deps) => {
|
|
11311
|
+
const selection = deps?.selection;
|
|
11312
|
+
const count = selection?.get().length ?? 0;
|
|
11313
|
+
return count >= 2 ? true : ActionDisabledReason.SelectionRequired;
|
|
11314
|
+
}
|
|
11141
11315
|
};
|
|
11142
11316
|
}
|
|
11143
11317
|
var alignLeftAction = makeAlignAction("left");
|
|
@@ -11218,6 +11392,7 @@ function makeDistributeAction(axis) {
|
|
|
11218
11392
|
icon: ICON_FOR2[axis],
|
|
11219
11393
|
group: "distribute",
|
|
11220
11394
|
eligible: { capability: "transforms-selection" },
|
|
11395
|
+
requires: ["selection", "scene"],
|
|
11221
11396
|
// No default keybindings. Wire bindings explicitly via the actions registry.
|
|
11222
11397
|
invoker: {
|
|
11223
11398
|
timing: "immediate",
|
|
@@ -11228,7 +11403,14 @@ function makeDistributeAction(axis) {
|
|
|
11228
11403
|
distributeSelection(selection, scene, axis);
|
|
11229
11404
|
}
|
|
11230
11405
|
},
|
|
11231
|
-
|
|
11406
|
+
// Deps-aware, matching `distributeSelection`'s own `ids.length < 3` guard:
|
|
11407
|
+
// a constant disabled reason greys the entry out forever (see
|
|
11408
|
+
// `requiresSelection`).
|
|
11409
|
+
enabled: (deps) => {
|
|
11410
|
+
const selection = deps?.selection;
|
|
11411
|
+
const count = selection?.get().length ?? 0;
|
|
11412
|
+
return count >= 3 ? true : ActionDisabledReason.SelectionRequired;
|
|
11413
|
+
}
|
|
11232
11414
|
};
|
|
11233
11415
|
}
|
|
11234
11416
|
var distributeHorizontalAction = makeDistributeAction("x");
|
|
@@ -11310,7 +11492,91 @@ function pathToMultiPolygon(path, opts = {}) {
|
|
|
11310
11492
|
}
|
|
11311
11493
|
finalizeCurrent();
|
|
11312
11494
|
if (rings.length === 0) return [];
|
|
11313
|
-
return rings.
|
|
11495
|
+
return groupRings(rings, path.fillRule);
|
|
11496
|
+
}
|
|
11497
|
+
function ringSignedArea(ring) {
|
|
11498
|
+
let a = 0;
|
|
11499
|
+
for (let i = 0, n = ring.length - 1; i < n; i++) {
|
|
11500
|
+
a += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
|
|
11501
|
+
}
|
|
11502
|
+
return a * 0.5;
|
|
11503
|
+
}
|
|
11504
|
+
function ringBox(ring) {
|
|
11505
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
11506
|
+
for (const [x, y] of ring) {
|
|
11507
|
+
if (x < minX) minX = x;
|
|
11508
|
+
if (x > maxX) maxX = x;
|
|
11509
|
+
if (y < minY) minY = y;
|
|
11510
|
+
if (y > maxY) maxY = y;
|
|
11511
|
+
}
|
|
11512
|
+
return [minX, minY, maxX, maxY];
|
|
11513
|
+
}
|
|
11514
|
+
function pointInRing(ring, px, py) {
|
|
11515
|
+
let inside = false;
|
|
11516
|
+
for (let i = 0, n = ring.length - 1; i < n; i++) {
|
|
11517
|
+
const [ax, ay] = ring[i];
|
|
11518
|
+
const [bx, by] = ring[i + 1];
|
|
11519
|
+
if (ay > py !== by > py && px < (bx - ax) * (py - ay) / (by - ay) + ax) {
|
|
11520
|
+
inside = !inside;
|
|
11521
|
+
}
|
|
11522
|
+
}
|
|
11523
|
+
return inside;
|
|
11524
|
+
}
|
|
11525
|
+
function ringContains(outer, outerBox, inner, innerBox) {
|
|
11526
|
+
if (innerBox[0] < outerBox[0] || innerBox[1] < outerBox[1] || innerBox[2] > outerBox[2] || innerBox[3] > outerBox[3]) return false;
|
|
11527
|
+
const n = inner.length - 1;
|
|
11528
|
+
let votes = 0;
|
|
11529
|
+
for (const k of [0, Math.floor(n / 3), Math.floor(2 * n / 3)]) {
|
|
11530
|
+
if (pointInRing(outer, inner[k][0], inner[k][1])) votes++;
|
|
11531
|
+
}
|
|
11532
|
+
return votes >= 2;
|
|
11533
|
+
}
|
|
11534
|
+
function groupRings(rings, fillRule) {
|
|
11535
|
+
const n = rings.length;
|
|
11536
|
+
if (n === 1) return [[rings[0]]];
|
|
11537
|
+
const boxes = rings.map(ringBox);
|
|
11538
|
+
const orient = rings.map((r) => ringSignedArea(r) >= 0 ? 1 : -1);
|
|
11539
|
+
const parent = new Array(n).fill(-1);
|
|
11540
|
+
const children = rings.map(() => []);
|
|
11541
|
+
for (let i = 0; i < n; i++) {
|
|
11542
|
+
let best = -1;
|
|
11543
|
+
let bestArea = Infinity;
|
|
11544
|
+
for (let j = 0; j < n; j++) {
|
|
11545
|
+
if (i === j) continue;
|
|
11546
|
+
if (!ringContains(rings[j], boxes[j], rings[i], boxes[i])) continue;
|
|
11547
|
+
const area = (boxes[j][2] - boxes[j][0]) * (boxes[j][3] - boxes[j][1]);
|
|
11548
|
+
if (area < bestArea) {
|
|
11549
|
+
bestArea = area;
|
|
11550
|
+
best = j;
|
|
11551
|
+
}
|
|
11552
|
+
}
|
|
11553
|
+
parent[i] = best;
|
|
11554
|
+
}
|
|
11555
|
+
for (let i = 0; i < n; i++) if (parent[i] >= 0) children[parent[i]].push(i);
|
|
11556
|
+
const filled = new Array(n);
|
|
11557
|
+
for (let i = 0; i < n; i++) {
|
|
11558
|
+
let depth = 0;
|
|
11559
|
+
let winding = 0;
|
|
11560
|
+
for (let a = i; a >= 0; a = parent[a]) {
|
|
11561
|
+
winding += orient[a];
|
|
11562
|
+
if (a !== i) depth++;
|
|
11563
|
+
}
|
|
11564
|
+
filled[i] = fillRule === "evenodd" ? depth % 2 === 0 : winding !== 0;
|
|
11565
|
+
}
|
|
11566
|
+
const out = [];
|
|
11567
|
+
for (let i = 0; i < n; i++) {
|
|
11568
|
+
if (!filled[i]) continue;
|
|
11569
|
+
if (parent[i] >= 0 && filled[parent[i]]) continue;
|
|
11570
|
+
const polygon = [rings[i]];
|
|
11571
|
+
const queue = [...children[i]];
|
|
11572
|
+
while (queue.length > 0) {
|
|
11573
|
+
const c = queue.pop();
|
|
11574
|
+
if (filled[c]) queue.push(...children[c]);
|
|
11575
|
+
else polygon.push(rings[c]);
|
|
11576
|
+
}
|
|
11577
|
+
out.push(polygon);
|
|
11578
|
+
}
|
|
11579
|
+
return out;
|
|
11314
11580
|
}
|
|
11315
11581
|
function multiPolygonToPath(mp) {
|
|
11316
11582
|
let nCmds = 0;
|
|
@@ -11623,6 +11889,7 @@ function makePathfinderAction(op) {
|
|
|
11623
11889
|
icon: ICON_FOR3[op],
|
|
11624
11890
|
group: "pathfinder",
|
|
11625
11891
|
eligible: { capability: "creates-shapes" },
|
|
11892
|
+
requires: ["selection", "booleansAdapter"],
|
|
11626
11893
|
// No default keybindings — there's no industry-standard chord set for
|
|
11627
11894
|
// boolean ops. Wire bindings explicitly via the actions registry override map.
|
|
11628
11895
|
invoker: {
|
|
@@ -13915,10 +14182,11 @@ function createDispatcher(opts) {
|
|
|
13915
14182
|
uiOngoingByAction.set(actionId, gestureId);
|
|
13916
14183
|
notify2();
|
|
13917
14184
|
let ended = false;
|
|
14185
|
+
const live = () => !ended && inFlightHandles.get(gestureId) === handle;
|
|
13918
14186
|
return {
|
|
13919
14187
|
gestureId,
|
|
13920
14188
|
update(nextParams) {
|
|
13921
|
-
if (
|
|
14189
|
+
if (!live()) return;
|
|
13922
14190
|
if (!handle.onMove) return;
|
|
13923
14191
|
try {
|
|
13924
14192
|
handle.onMove(buildUiInvocationCtx(deps, nextParams));
|
|
@@ -13928,7 +14196,10 @@ function createDispatcher(opts) {
|
|
|
13928
14196
|
notify2();
|
|
13929
14197
|
},
|
|
13930
14198
|
end(reason) {
|
|
13931
|
-
if (
|
|
14199
|
+
if (!live()) {
|
|
14200
|
+
ended = true;
|
|
14201
|
+
return;
|
|
14202
|
+
}
|
|
13932
14203
|
ended = true;
|
|
13933
14204
|
if (handle.onEnd) {
|
|
13934
14205
|
try {
|
|
@@ -15142,6 +15413,7 @@ function useTools(opts) {
|
|
|
15142
15413
|
);
|
|
15143
15414
|
const active = contributions.focused;
|
|
15144
15415
|
const getActiveOverlays = contributions.overlays;
|
|
15416
|
+
const registryKey = Object.keys(slotted.registry).sort().join("|") + "#" + slotted.ambient.map((t) => t.id).join("|");
|
|
15145
15417
|
return useMemo(
|
|
15146
15418
|
() => ({
|
|
15147
15419
|
active,
|
|
@@ -15154,7 +15426,7 @@ function useTools(opts) {
|
|
|
15154
15426
|
has,
|
|
15155
15427
|
getActiveOverlays
|
|
15156
15428
|
}),
|
|
15157
|
-
[active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays]
|
|
15429
|
+
[active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays, registryKey]
|
|
15158
15430
|
);
|
|
15159
15431
|
}
|
|
15160
15432
|
var TOOL_ACTIVATE_ID = "tool.activate";
|
|
@@ -16545,8 +16817,17 @@ function composeAffordanceLayer(id, label, affordances) {
|
|
|
16545
16817
|
// `buildAffordanceAt` share one implementation rather than two that can
|
|
16546
16818
|
// drift. This wrapper lifts the region's declared cursor and claim onto
|
|
16547
16819
|
// the binding, the way `buildAffordanceAt` lifts them onto `AffordanceHit`.
|
|
16548
|
-
hitTest: (wx, wy,
|
|
16549
|
-
const
|
|
16820
|
+
hitTest: (wx, wy, data, view, _dims, isVisible) => {
|
|
16821
|
+
const state = asChromeState(data);
|
|
16822
|
+
if (!state) return null;
|
|
16823
|
+
const hit = hitAffordanceRegions(
|
|
16824
|
+
affordances,
|
|
16825
|
+
wx,
|
|
16826
|
+
wy,
|
|
16827
|
+
state,
|
|
16828
|
+
view,
|
|
16829
|
+
isVisible ?? asIsVisible(data) ?? void 0
|
|
16830
|
+
);
|
|
16550
16831
|
if (!hit) return null;
|
|
16551
16832
|
const { cursor, strength, claimedKinds } = hit.region;
|
|
16552
16833
|
return {
|
|
@@ -17149,6 +17430,10 @@ function usePenTool(options) {
|
|
|
17149
17430
|
onEnd: (endCtx, reason) => {
|
|
17150
17431
|
const se = s();
|
|
17151
17432
|
if (reason === "cancel") {
|
|
17433
|
+
if (se.current && se.draggingHandleAt === se.current.anchors.length - 1) {
|
|
17434
|
+
se.current.anchors.pop();
|
|
17435
|
+
if (se.current.anchors.length === 0) se.current = null;
|
|
17436
|
+
}
|
|
17152
17437
|
se.draggingHandleAt = null;
|
|
17153
17438
|
forceRenderRef.current();
|
|
17154
17439
|
return;
|
|
@@ -18727,7 +19012,6 @@ function CanvasInner(props, ref) {
|
|
|
18727
19012
|
shaders,
|
|
18728
19013
|
previewIdsExtra,
|
|
18729
19014
|
previewPoseExtra,
|
|
18730
|
-
previewBoundsExtra,
|
|
18731
19015
|
gestureSource,
|
|
18732
19016
|
viewport,
|
|
18733
19017
|
backgroundFill,
|
|
@@ -18753,6 +19037,7 @@ function CanvasInner(props, ref) {
|
|
|
18753
19037
|
debugSinkRefForCtx.current = debugSink;
|
|
18754
19038
|
const adapter = adapterProp;
|
|
18755
19039
|
const canvasRef = useRef(null);
|
|
19040
|
+
const helpersForLayersRef = useRef(null);
|
|
18756
19041
|
const dimsRef = useRef({ width, height });
|
|
18757
19042
|
dimsRef.current = { width, height };
|
|
18758
19043
|
const getIsVisibleRef = useRef(getIsVisible);
|
|
@@ -18775,7 +19060,7 @@ function CanvasInner(props, ref) {
|
|
|
18775
19060
|
const isVisible = getIsVisibleRef.current?.() ?? alwaysVisible;
|
|
18776
19061
|
for (const layer of layers2) {
|
|
18777
19062
|
if (!layer.hitTest) continue;
|
|
18778
|
-
const hit = layer.hitTest(worldX, worldY,
|
|
19063
|
+
const hit = layer.hitTest(worldX, worldY, helpersForLayersRef.current, view, dims, isVisible);
|
|
18779
19064
|
if (hit) return { layerId: layer.id, hit };
|
|
18780
19065
|
}
|
|
18781
19066
|
return null;
|
|
@@ -18930,8 +19215,8 @@ function CanvasInner(props, ref) {
|
|
|
18930
19215
|
};
|
|
18931
19216
|
const selectedIdsForWiring = effectiveSelection.current;
|
|
18932
19217
|
const multiActive2 = selectedIdsForWiring.length > 1;
|
|
18933
|
-
const previewExtraRef = useRef({ previewPoseExtra
|
|
18934
|
-
previewExtraRef.current = { previewPoseExtra
|
|
19218
|
+
const previewExtraRef = useRef({ previewPoseExtra });
|
|
19219
|
+
previewExtraRef.current = { previewPoseExtra };
|
|
18935
19220
|
const effectiveBoundsOf = useMemo(() => {
|
|
18936
19221
|
return boundsOf2 ?? baseBoundsOf;
|
|
18937
19222
|
}, [boundsOf2, baseBoundsOf]);
|
|
@@ -18950,10 +19235,6 @@ function CanvasInner(props, ref) {
|
|
|
18950
19235
|
if (p != null) return geometry.getBounds(p);
|
|
18951
19236
|
}
|
|
18952
19237
|
const extras = previewExtraRef.current;
|
|
18953
|
-
if (extras.previewBoundsExtra) {
|
|
18954
|
-
const b = extras.previewBoundsExtra(id);
|
|
18955
|
-
if (b) return b;
|
|
18956
|
-
}
|
|
18957
19238
|
if (extras.previewPoseExtra) {
|
|
18958
19239
|
const p = extras.previewPoseExtra(id);
|
|
18959
19240
|
if (p != null) return geometry.getBounds(p);
|
|
@@ -18994,10 +19275,6 @@ function CanvasInner(props, ref) {
|
|
|
18994
19275
|
const p = firstPreviewPose(tools, id);
|
|
18995
19276
|
if (p != null) return geometry.getBounds(p);
|
|
18996
19277
|
}
|
|
18997
|
-
if (previewBoundsExtra) {
|
|
18998
|
-
const b = previewBoundsExtra(id);
|
|
18999
|
-
if (b) return b;
|
|
19000
|
-
}
|
|
19001
19278
|
if (previewPoseExtra) {
|
|
19002
19279
|
const p = previewPoseExtra(id);
|
|
19003
19280
|
if (p != null) return geometry.getBounds(p);
|
|
@@ -19033,6 +19310,7 @@ function CanvasInner(props, ref) {
|
|
|
19033
19310
|
getDebug: () => debugSink,
|
|
19034
19311
|
getIsVisible: () => getIsVisibleRef.current?.() ?? alwaysVisible
|
|
19035
19312
|
};
|
|
19313
|
+
helpersForLayersRef.current = helpersForLayers;
|
|
19036
19314
|
if (helpersRef) helpersRef.current = helpersForLayers;
|
|
19037
19315
|
const pointerDownRef = useRef(false);
|
|
19038
19316
|
const handlePointerDown = (e) => {
|
|
@@ -19214,6 +19492,11 @@ function CanvasInner(props, ref) {
|
|
|
19214
19492
|
);
|
|
19215
19493
|
renderer.render(commands, viewToMat3(effectiveView));
|
|
19216
19494
|
}, [layersWithDebug, width, height, effectiveView, debugSink, redrawNonce, dprProp]);
|
|
19495
|
+
useEffect(() => () => {
|
|
19496
|
+
glRendererRef.current?.dispose();
|
|
19497
|
+
glRendererRef.current = null;
|
|
19498
|
+
lastResizeRef.current = null;
|
|
19499
|
+
}, []);
|
|
19217
19500
|
const shaderIdKey = shaders?.map((h) => h.id).join("|") ?? "";
|
|
19218
19501
|
useEffect(() => {
|
|
19219
19502
|
const renderer = glRendererRef.current;
|
|
@@ -20642,6 +20925,89 @@ function poseContainsRotated(pose, wx, wy, tolerance) {
|
|
|
20642
20925
|
}
|
|
20643
20926
|
return poseContains(pose, wx, wy, tolerance);
|
|
20644
20927
|
}
|
|
20928
|
+
function hiddenLayerIds(layers) {
|
|
20929
|
+
const out = /* @__PURE__ */ new Set();
|
|
20930
|
+
for (const layer of layers ?? []) {
|
|
20931
|
+
if (layer.visible === false) out.add(layer.id);
|
|
20932
|
+
}
|
|
20933
|
+
return out;
|
|
20934
|
+
}
|
|
20935
|
+
function hitTestArea(scene, bounds) {
|
|
20936
|
+
const { x, y, width: w, height: h } = bounds;
|
|
20937
|
+
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
20938
|
+
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
20939
|
+
}
|
|
20940
|
+
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
20941
|
+
const ab = areaBounds ?? boundsOf(area);
|
|
20942
|
+
if (!ab) return [];
|
|
20943
|
+
const hits = [];
|
|
20944
|
+
const hidden = hiddenLayerIds(scene.layers);
|
|
20945
|
+
const order = scene.renderOrderNodes();
|
|
20946
|
+
for (let i = 0; i < order.length; i++) {
|
|
20947
|
+
const node = order[i];
|
|
20948
|
+
if (node.kind === "container") continue;
|
|
20949
|
+
if (hidden.size > 0 && hidden.has(node.layer)) continue;
|
|
20950
|
+
const pose = node.pose;
|
|
20951
|
+
const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
|
|
20952
|
+
const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
|
|
20953
|
+
if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
|
|
20954
|
+
continue;
|
|
20955
|
+
}
|
|
20956
|
+
if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
|
|
20957
|
+
continue;
|
|
20958
|
+
}
|
|
20959
|
+
if (areaIsRect && b.x >= ab.x && b.y >= ab.y && b.x + b.width <= ab.x + ab.width && b.y + b.height <= ab.y + ab.height) {
|
|
20960
|
+
hits.push(node.id);
|
|
20961
|
+
continue;
|
|
20962
|
+
}
|
|
20963
|
+
if (silhouette) {
|
|
20964
|
+
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
20965
|
+
hits.push(node.id);
|
|
20966
|
+
}
|
|
20967
|
+
continue;
|
|
20968
|
+
}
|
|
20969
|
+
const drawn = findShapeSilhouette(node, pose);
|
|
20970
|
+
if (drawn?.kind === "polygon") {
|
|
20971
|
+
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
20972
|
+
continue;
|
|
20973
|
+
}
|
|
20974
|
+
hits.push(node.id);
|
|
20975
|
+
}
|
|
20976
|
+
return hits;
|
|
20977
|
+
}
|
|
20978
|
+
function silhouetteOverlapsArea(sil, area) {
|
|
20979
|
+
const ns = sil.length >> 1;
|
|
20980
|
+
const na = area.length >> 1;
|
|
20981
|
+
if (ns < 1 || na < 3) return false;
|
|
20982
|
+
for (let i = 0; i < ns; i++) {
|
|
20983
|
+
if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
|
|
20984
|
+
}
|
|
20985
|
+
for (let i = 0; i < na; i++) {
|
|
20986
|
+
if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
|
|
20987
|
+
}
|
|
20988
|
+
for (let i = 0, j = ns - 1; i < ns; j = i++) {
|
|
20989
|
+
const ax = sil[j * 2], ay = sil[j * 2 + 1];
|
|
20990
|
+
const bx = sil[i * 2], by = sil[i * 2 + 1];
|
|
20991
|
+
for (let k = 0, l = na - 1; k < na; l = k++) {
|
|
20992
|
+
const cx = area[l * 2], cy = area[l * 2 + 1];
|
|
20993
|
+
const dx = area[k * 2], dy = area[k * 2 + 1];
|
|
20994
|
+
if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
|
|
20995
|
+
}
|
|
20996
|
+
}
|
|
20997
|
+
return false;
|
|
20998
|
+
}
|
|
20999
|
+
function boundsOf(coords) {
|
|
21000
|
+
if (coords.length < 2) return null;
|
|
21001
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
21002
|
+
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
21003
|
+
const x = coords[i], y = coords[i + 1];
|
|
21004
|
+
if (x < minX) minX = x;
|
|
21005
|
+
if (y < minY) minY = y;
|
|
21006
|
+
if (x > maxX) maxX = x;
|
|
21007
|
+
if (y > maxY) maxY = y;
|
|
21008
|
+
}
|
|
21009
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
21010
|
+
}
|
|
20645
21011
|
var DEFAULT_PICK_TOLERANCE_PX = 4;
|
|
20646
21012
|
function useSceneSelectTool(args) {
|
|
20647
21013
|
const { scene, selection, geometry, selectTool: opts, insertTool, layouts, getView } = args;
|
|
@@ -20739,7 +21105,9 @@ function useSceneSelectTool(args) {
|
|
|
20739
21105
|
}
|
|
20740
21106
|
const tolerance = pickTolerancePx / meanScale(getView?.()?.scale ?? { x: 1, y: 1 });
|
|
20741
21107
|
const out = [];
|
|
21108
|
+
const hidden = hiddenLayerIds(scene.layers);
|
|
20742
21109
|
for (const n of scene.renderOrderNodes()) {
|
|
21110
|
+
if (hidden.size > 0 && hidden.has(n.layer)) continue;
|
|
20743
21111
|
if (!poseContainsRotated(n.pose, wx, wy, tolerance)) continue;
|
|
20744
21112
|
if (shapePicking && !shapeCoversPoint(n, n.pose, wx, wy, { tolerance })) continue;
|
|
20745
21113
|
out.push(n.id);
|
|
@@ -21623,8 +21991,10 @@ function createPathAnchorAffordances(getAnchorState, opts = {}) {
|
|
|
21623
21991
|
const anchors = {
|
|
21624
21992
|
id: PATH_ANCHOR_CHROME_ID,
|
|
21625
21993
|
regions(state) {
|
|
21994
|
+
const editingId = getAnchorState()?.editingId ?? null;
|
|
21626
21995
|
const out = [];
|
|
21627
21996
|
for (const { id, path } of editablePaths(state)) {
|
|
21997
|
+
if (editingId !== null && id !== editingId) continue;
|
|
21628
21998
|
for (const a of enumerateAnchors(path)) {
|
|
21629
21999
|
out.push(point(
|
|
21630
22000
|
`${id}:anchor:${a.anchorIndex}`,
|
|
@@ -21879,80 +22249,6 @@ function DepRegistryProviderIfRoot({ children }) {
|
|
|
21879
22249
|
if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
21880
22250
|
return /* @__PURE__ */ jsx(DepRegistryProvider, { children });
|
|
21881
22251
|
}
|
|
21882
|
-
function hitTestArea(scene, bounds) {
|
|
21883
|
-
const { x, y, width: w, height: h } = bounds;
|
|
21884
|
-
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
21885
|
-
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
21886
|
-
}
|
|
21887
|
-
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
21888
|
-
const ab = areaBounds ?? boundsOf(area);
|
|
21889
|
-
if (!ab) return [];
|
|
21890
|
-
const hits = [];
|
|
21891
|
-
const order = scene.renderOrderNodes();
|
|
21892
|
-
for (let i = 0; i < order.length; i++) {
|
|
21893
|
-
const node = order[i];
|
|
21894
|
-
if (node.kind === "container") continue;
|
|
21895
|
-
const pose = node.pose;
|
|
21896
|
-
const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
|
|
21897
|
-
const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
|
|
21898
|
-
if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
|
|
21899
|
-
continue;
|
|
21900
|
-
}
|
|
21901
|
-
if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
|
|
21902
|
-
continue;
|
|
21903
|
-
}
|
|
21904
|
-
if (areaIsRect && b.x >= ab.x && b.y >= ab.y && b.x + b.width <= ab.x + ab.width && b.y + b.height <= ab.y + ab.height) {
|
|
21905
|
-
hits.push(node.id);
|
|
21906
|
-
continue;
|
|
21907
|
-
}
|
|
21908
|
-
if (silhouette) {
|
|
21909
|
-
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
21910
|
-
hits.push(node.id);
|
|
21911
|
-
}
|
|
21912
|
-
continue;
|
|
21913
|
-
}
|
|
21914
|
-
const drawn = findShapeSilhouette(node, pose);
|
|
21915
|
-
if (drawn?.kind === "polygon") {
|
|
21916
|
-
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
21917
|
-
continue;
|
|
21918
|
-
}
|
|
21919
|
-
hits.push(node.id);
|
|
21920
|
-
}
|
|
21921
|
-
return hits;
|
|
21922
|
-
}
|
|
21923
|
-
function silhouetteOverlapsArea(sil, area) {
|
|
21924
|
-
const ns = sil.length >> 1;
|
|
21925
|
-
const na = area.length >> 1;
|
|
21926
|
-
if (ns < 1 || na < 3) return false;
|
|
21927
|
-
for (let i = 0; i < ns; i++) {
|
|
21928
|
-
if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
|
|
21929
|
-
}
|
|
21930
|
-
for (let i = 0; i < na; i++) {
|
|
21931
|
-
if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
|
|
21932
|
-
}
|
|
21933
|
-
for (let i = 0, j = ns - 1; i < ns; j = i++) {
|
|
21934
|
-
const ax = sil[j * 2], ay = sil[j * 2 + 1];
|
|
21935
|
-
const bx = sil[i * 2], by = sil[i * 2 + 1];
|
|
21936
|
-
for (let k = 0, l = na - 1; k < na; l = k++) {
|
|
21937
|
-
const cx = area[l * 2], cy = area[l * 2 + 1];
|
|
21938
|
-
const dx = area[k * 2], dy = area[k * 2 + 1];
|
|
21939
|
-
if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
|
|
21940
|
-
}
|
|
21941
|
-
}
|
|
21942
|
-
return false;
|
|
21943
|
-
}
|
|
21944
|
-
function boundsOf(coords) {
|
|
21945
|
-
if (coords.length < 2) return null;
|
|
21946
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
21947
|
-
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
21948
|
-
const x = coords[i], y = coords[i + 1];
|
|
21949
|
-
if (x < minX) minX = x;
|
|
21950
|
-
if (y < minY) minY = y;
|
|
21951
|
-
if (x > maxX) maxX = x;
|
|
21952
|
-
if (y > maxY) maxY = y;
|
|
21953
|
-
}
|
|
21954
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
21955
|
-
}
|
|
21956
22252
|
function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize) {
|
|
21957
22253
|
const viewApiRef = useRef({
|
|
21958
22254
|
get: () => currentViewRef.current,
|
|
@@ -22724,6 +23020,7 @@ function useActionsPropResolver(actions) {
|
|
|
22724
23020
|
}
|
|
22725
23021
|
if (!prop) return;
|
|
22726
23022
|
const unregisters = [];
|
|
23023
|
+
const restores = [];
|
|
22727
23024
|
const warnedIds = /* @__PURE__ */ new Set();
|
|
22728
23025
|
for (const [slotId, entry] of Object.entries(prop)) {
|
|
22729
23026
|
if (entry === null) {
|
|
@@ -22751,10 +23048,14 @@ function useActionsPropResolver(actions) {
|
|
|
22751
23048
|
}
|
|
22752
23049
|
const { id: _drop, ...rest } = entry;
|
|
22753
23050
|
const merged = { ...existing, ...rest };
|
|
23051
|
+
restores.push(() => {
|
|
23052
|
+
reg.register(existing);
|
|
23053
|
+
});
|
|
22754
23054
|
unregisters.push(reg.register(merged));
|
|
22755
23055
|
}
|
|
22756
23056
|
return () => {
|
|
22757
23057
|
for (const u of unregisters) u();
|
|
23058
|
+
for (const r of restores) r();
|
|
22758
23059
|
};
|
|
22759
23060
|
}, [reg, actions]);
|
|
22760
23061
|
}
|
|
@@ -24506,5 +24807,5 @@ function oklchToOklab(L, C, h) {
|
|
|
24506
24807
|
}
|
|
24507
24808
|
|
|
24508
24809
|
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, UnknownIcon, buildSceneViewCommands, computeFitView2, computeIndicatorCommand, eligibleContribution, evaluateEnabled, oklabToSrgbU8, oklchToOklab, renderSceneToCanvas, rgbaToHex, useActionsRegistry, useOptionalDepRegistry };
|
|
24509
|
-
//# sourceMappingURL=chunk-
|
|
24510
|
-
//# sourceMappingURL=chunk-
|
|
24810
|
+
//# sourceMappingURL=chunk-DWV7SFKR.js.map
|
|
24811
|
+
//# sourceMappingURL=chunk-DWV7SFKR.js.map
|