@weasel-js/core 1.0.3 → 1.1.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/CHANGELOG.md +251 -0
- package/dist/{chunk-SMCZRSYB.js → chunk-3B4QEB2G.js} +810 -228
- package/dist/chunk-3B4QEB2G.js.map +1 -0
- package/dist/{chunk-U45SODC2.js → chunk-4Z6J4IVH.js} +43 -27
- package/dist/chunk-4Z6J4IVH.js.map +1 -0
- package/dist/{chunk-3KGMJMOS.js → chunk-CSMF654J.js} +3 -3
- package/dist/{chunk-3KGMJMOS.js.map → chunk-CSMF654J.js.map} +1 -1
- package/dist/{chunk-TR3HZN34.js → chunk-J3YC3GVF.js} +5 -4
- package/dist/chunk-J3YC3GVF.js.map +1 -0
- package/dist/{chunk-3JYFAQIB.js → chunk-J7LUY47N.js} +3 -3
- package/dist/{chunk-3JYFAQIB.js.map → chunk-J7LUY47N.js.map} +1 -1
- package/dist/clipboard.d.ts +1 -1
- package/dist/clone.d.ts +2 -2
- package/dist/{geometry-5X6TrAkX.d.ts → geometry-C56YbnfD.d.ts} +1 -1
- package/dist/{grid-sfilv5HP.d.ts → grid-DqOi0Vgi.d.ts} +1 -1
- package/dist/index.d.ts +179 -59
- package/dist/index.js +5 -5
- package/dist/insert.d.ts +3 -3
- package/dist/insert.js +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/move.js +3 -3
- package/dist/move.js.map +1 -1
- package/dist/{options-CbsIgQci.d.ts → options-C6HYCKP7.d.ts} +1 -1
- package/dist/{pointSnapToGrid-C3wowsQy.d.ts → pointSnapToGrid-BBtn5bus.d.ts} +2 -2
- package/dist/{registry-CxnkXbG8.d.ts → registry-DGBrjGbB.d.ts} +4 -4
- package/dist/renderer.d.ts +29 -13
- package/dist/renderer.js +5 -5
- package/dist/resize.d.ts +4 -4
- package/dist/resize.js +2 -2
- package/dist/routing.d.ts +4 -4
- package/dist/routing.js +1 -1
- package/dist/{types-ByZxoD6P.d.ts → types-BhrifbZ1.d.ts} +1 -1
- package/dist/{types-DpQ0cWYC.d.ts → types-DbysOO-2.d.ts} +10 -3
- package/package.json +6 -6
- package/dist/chunk-SMCZRSYB.js.map +0 -1
- package/dist/chunk-TR3HZN34.js.map +0 -1
- package/dist/chunk-U45SODC2.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-
|
|
2
|
-
import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-
|
|
3
|
-
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-
|
|
4
|
-
import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-
|
|
1
|
+
import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-J3YC3GVF.js';
|
|
2
|
+
import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-CSMF654J.js';
|
|
3
|
+
import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-J7LUY47N.js';
|
|
4
|
+
import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-4Z6J4IVH.js';
|
|
5
5
|
import { dispatchApplyBatch, dlog, dwarn, usePointerContext, PointerContextProvider, applyOpsTo, extractWeaselClipboardFromSvg, parseWeaselClipboardText, materializePaste, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, sniffWeaselClipboardText } from './chunk-JOSUVS6C.js';
|
|
6
6
|
import { createSetSelectionOp } from './chunk-BHVYVFGV.js';
|
|
7
7
|
import { registerOpFactory, createDeleteOp, createInsertOp, rebuildOp } from './chunk-GVCNT7UH.js';
|
|
@@ -10,7 +10,7 @@ import { createContext, createElement, forwardRef, useMemo, useRef, useState, us
|
|
|
10
10
|
import { subscribeGlyphReady, DEFAULT_BAKE_BUDGET, TEXT_VERT_SRC, TEXT_FRAG_SRC, TEXT_SDF_UNIFORMS, TEXT_SDF_ATTRIBUTES, TEXT_FRAG_R8_SRC, markAllFontsNotUploaded, resetBakeBudget, glyphGeneration, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, resolveFontVariant, resolveGlyphFallback, glyphOutline } from '@weasel-js/font';
|
|
11
11
|
export { canQueryLocalFonts, canQueryLocalFonts as canQueryLocalFonts2, enableLocalFontOutlines, enableLocalFontOutlines as enableLocalFontOutlines2, hasFontOutlines, hasFontOutlines as hasFontOutlines2, isCanvasFont, isCanvasFont as isCanvasFont2, listFontOutlines, listFontOutlines as listFontOutlines2, outlineStatus, outlineStatus as outlineStatus2, registerCanvasFont, registerCanvasFont as registerCanvasFont2, registerFont, registerFont as registerFont2, registerFontOutlines, registerFontOutlines as registerFontOutlines2, subscribeGlyphReady, subscribeGlyphReady as subscribeGlyphReady2, unregisterCanvasFont, unregisterCanvasFont as unregisterCanvasFont2, unregisterFontOutlines, unregisterFontOutlines as unregisterFontOutlines2 } from '@weasel-js/font';
|
|
12
12
|
import earcut from 'earcut';
|
|
13
|
-
import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross } from '@weasel-js/geom';
|
|
13
|
+
import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross, pointSegmentDist2 } from '@weasel-js/geom';
|
|
14
14
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
15
15
|
import polygonClipping from 'polygon-clipping';
|
|
16
16
|
import { matchSpec, RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, parseTargetSpec, mimeMatchesGlob } from '@weasel-js/gestures';
|
|
@@ -19,7 +19,7 @@ import { createHistory } from '@weasel-js/history';
|
|
|
19
19
|
export * from '@weasel-js/history';
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var VERSION = "1.0
|
|
22
|
+
var VERSION = "1.1.0" ;
|
|
23
23
|
|
|
24
24
|
// src/features/grid/roundToCell.ts
|
|
25
25
|
function roundToCell(value, cellSize) {
|
|
@@ -524,13 +524,20 @@ var GLTextureCache = class {
|
|
|
524
524
|
}
|
|
525
525
|
gl;
|
|
526
526
|
map = /* @__PURE__ */ new Map();
|
|
527
|
+
wraps = /* @__PURE__ */ new Map();
|
|
527
528
|
has(id) {
|
|
528
529
|
return this.map.has(id);
|
|
529
530
|
}
|
|
530
|
-
/** Uploads once per id
|
|
531
|
-
*
|
|
531
|
+
/** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
|
|
532
|
+
* from what the id was last uploaded under: the same registered image can
|
|
533
|
+
* be a clamped shader texture in one draw and a repeating pattern tile in
|
|
534
|
+
* the next, and first-upload-wins gave the second one the first one's mode. */
|
|
532
535
|
upload(id, source, wrap = "clamp") {
|
|
533
|
-
|
|
536
|
+
const cached = this.map.get(id);
|
|
537
|
+
if (cached) {
|
|
538
|
+
if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);
|
|
539
|
+
return id;
|
|
540
|
+
}
|
|
534
541
|
const gl = this.gl;
|
|
535
542
|
const tex = gl.createTexture();
|
|
536
543
|
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
@@ -543,8 +550,18 @@ var GLTextureCache = class {
|
|
|
543
550
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);
|
|
544
551
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
545
552
|
this.map.set(id, tex);
|
|
553
|
+
this.wraps.set(id, wrap);
|
|
546
554
|
return id;
|
|
547
555
|
}
|
|
556
|
+
applyWrap(id, tex, wrap) {
|
|
557
|
+
const gl = this.gl;
|
|
558
|
+
const mode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
559
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
560
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);
|
|
561
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);
|
|
562
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
563
|
+
this.wraps.set(id, wrap);
|
|
564
|
+
}
|
|
548
565
|
/** Create a single-channel R8 texture from raw bytes (full upload).
|
|
549
566
|
* No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;
|
|
550
567
|
* UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */
|
|
@@ -563,6 +580,7 @@ var GLTextureCache = class {
|
|
|
563
580
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
564
581
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
565
582
|
this.map.set(id, tex);
|
|
583
|
+
this.wraps.set(id, "clamp");
|
|
566
584
|
}
|
|
567
585
|
/** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */
|
|
568
586
|
subImageR8(id, x, y, w, h, data) {
|
|
@@ -594,6 +612,7 @@ var GLTextureCache = class {
|
|
|
594
612
|
gl.deleteTexture(tex);
|
|
595
613
|
}
|
|
596
614
|
this.map.clear();
|
|
615
|
+
this.wraps.clear();
|
|
597
616
|
}
|
|
598
617
|
};
|
|
599
618
|
|
|
@@ -1282,6 +1301,13 @@ var GroupState = class {
|
|
|
1282
1301
|
this.alphaStack.push(nextAlpha);
|
|
1283
1302
|
this.colorMatrixStack.push(nextCM);
|
|
1284
1303
|
}
|
|
1304
|
+
/** Drop every pushed frame, leaving the root. A frame that throws part-way
|
|
1305
|
+
* down the tree never pops, and the next frame would draw under leftovers. */
|
|
1306
|
+
reset() {
|
|
1307
|
+
this.transformStack.length = 1;
|
|
1308
|
+
this.alphaStack.length = 1;
|
|
1309
|
+
this.colorMatrixStack.length = 1;
|
|
1310
|
+
}
|
|
1285
1311
|
pop() {
|
|
1286
1312
|
if (this.transformStack.length <= 1) {
|
|
1287
1313
|
throw new Error("GroupState.pop: cannot pop root frame");
|
|
@@ -1935,16 +1961,28 @@ function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
|
1935
1961
|
|
|
1936
1962
|
// src/features/paths/pathFromD.ts
|
|
1937
1963
|
var NUM_RE = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
|
|
1964
|
+
var NUM_RE_AT = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/y;
|
|
1965
|
+
function isCommandLetter(d, j) {
|
|
1966
|
+
const c = d[j];
|
|
1967
|
+
if (!/[A-Za-z]/.test(c)) return false;
|
|
1968
|
+
if (c !== "e" && c !== "E") return true;
|
|
1969
|
+
if (!/[\d.]/.test(d[j - 1] ?? "")) return true;
|
|
1970
|
+
const k = d[j + 1] === "+" || d[j + 1] === "-" ? j + 2 : j + 1;
|
|
1971
|
+
return !/\d/.test(d[k] ?? "");
|
|
1972
|
+
}
|
|
1938
1973
|
function tokenize(d) {
|
|
1939
1974
|
const out = [];
|
|
1940
1975
|
let i = 0;
|
|
1941
1976
|
while (i < d.length) {
|
|
1942
|
-
|
|
1943
|
-
if (/[A-Za-z]/.test(ch)) {
|
|
1977
|
+
if (isCommandLetter(d, i)) {
|
|
1944
1978
|
let j = i + 1;
|
|
1945
|
-
while (j < d.length &&
|
|
1979
|
+
while (j < d.length && !isCommandLetter(d, j)) j++;
|
|
1946
1980
|
const argSlice = d.slice(i + 1, j);
|
|
1947
|
-
|
|
1981
|
+
const cmd = d[i];
|
|
1982
|
+
out.push({
|
|
1983
|
+
cmd,
|
|
1984
|
+
args: cmd === "A" || cmd === "a" ? readArcArgs(argSlice) : readNumbers(argSlice)
|
|
1985
|
+
});
|
|
1948
1986
|
i = j;
|
|
1949
1987
|
} else {
|
|
1950
1988
|
i++;
|
|
@@ -1962,6 +2000,30 @@ function readNumbers(s) {
|
|
|
1962
2000
|
}
|
|
1963
2001
|
return out;
|
|
1964
2002
|
}
|
|
2003
|
+
function readArcArgs(s) {
|
|
2004
|
+
const out = [];
|
|
2005
|
+
let i = 0;
|
|
2006
|
+
while (i < s.length) {
|
|
2007
|
+
const c = s[i];
|
|
2008
|
+
if (c === "," || c === " " || c === " " || c === "\n" || c === "\r") {
|
|
2009
|
+
i++;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
const slot = out.length % 7;
|
|
2013
|
+
if (slot === 3 || slot === 4) {
|
|
2014
|
+
if (c !== "0" && c !== "1") return out;
|
|
2015
|
+
out.push(c === "1" ? 1 : 0);
|
|
2016
|
+
i++;
|
|
2017
|
+
continue;
|
|
2018
|
+
}
|
|
2019
|
+
NUM_RE_AT.lastIndex = i;
|
|
2020
|
+
const m = NUM_RE_AT.exec(s);
|
|
2021
|
+
if (!m) return out;
|
|
2022
|
+
out.push(Number(m[0]));
|
|
2023
|
+
i = NUM_RE_AT.lastIndex;
|
|
2024
|
+
}
|
|
2025
|
+
return out;
|
|
2026
|
+
}
|
|
1965
2027
|
var ARG_COUNTS = {
|
|
1966
2028
|
M: 2,
|
|
1967
2029
|
L: 2,
|
|
@@ -2879,42 +2941,42 @@ var MAX_VERTICES_PER_BATCH = 32768;
|
|
|
2879
2941
|
var FLOATS_PER_VERTEX = 6;
|
|
2880
2942
|
var INITIAL_VERTICES = 256;
|
|
2881
2943
|
var INITIAL_INDICES = 384;
|
|
2944
|
+
var SOLID_RING_SIZE = 64;
|
|
2945
|
+
var SOLID_RING_SLOT_VERTICES = 1024;
|
|
2946
|
+
var SOLID_RING_SLOT_INDICES = 3072;
|
|
2947
|
+
var SOLID_LARGE_RING_SIZE = 4;
|
|
2948
|
+
function doubledTo(from, need) {
|
|
2949
|
+
let capacity = from;
|
|
2950
|
+
while (capacity < need) capacity *= 2;
|
|
2951
|
+
return capacity;
|
|
2952
|
+
}
|
|
2882
2953
|
var SolidBatch = class {
|
|
2883
2954
|
gl;
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2955
|
+
aPos;
|
|
2956
|
+
aColor;
|
|
2957
|
+
/** Cycled per flush, and created on first use so a renderer that flushes
|
|
2958
|
+
* rarely allocates as few slots as it flushes. */
|
|
2959
|
+
ring = new Array(SOLID_RING_SIZE).fill(void 0);
|
|
2960
|
+
next = 0;
|
|
2961
|
+
/** The same, for flushes past a slot's capacity; these sets grow to fit. */
|
|
2962
|
+
largeRing = new Array(SOLID_LARGE_RING_SIZE).fill(void 0);
|
|
2963
|
+
nextLarge = 0;
|
|
2887
2964
|
verts = new Float32Array(INITIAL_VERTICES * FLOATS_PER_VERTEX);
|
|
2888
2965
|
idx = new Uint32Array(INITIAL_INDICES);
|
|
2889
2966
|
nVerts = 0;
|
|
2890
2967
|
nIdx = 0;
|
|
2891
|
-
/**
|
|
2892
|
-
|
|
2893
|
-
|
|
2968
|
+
/** Whether the staged run is rects alone, so its indices are the canonical
|
|
2969
|
+
* quad pattern and a slot already holding that pattern needs no upload. */
|
|
2970
|
+
pureRects = true;
|
|
2894
2971
|
constructor(gl, prog) {
|
|
2895
2972
|
const aPos = prog.attribute("a_position");
|
|
2896
2973
|
const aColor = prog.attribute("a_vertexColor");
|
|
2897
2974
|
if (aPos === void 0 || aColor === void 0) {
|
|
2898
2975
|
throw new Error("SolidBatch: vertex-color program is missing a_position / a_vertexColor");
|
|
2899
2976
|
}
|
|
2900
|
-
const vao = gl.createVertexArray();
|
|
2901
|
-
const vbo = gl.createBuffer();
|
|
2902
|
-
const ibo = gl.createBuffer();
|
|
2903
|
-
if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
|
|
2904
2977
|
this.gl = gl;
|
|
2905
|
-
this.
|
|
2906
|
-
this.
|
|
2907
|
-
this.ibo = ibo;
|
|
2908
|
-
const stride = FLOATS_PER_VERTEX * 4;
|
|
2909
|
-
gl.bindVertexArray(vao);
|
|
2910
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
2911
|
-
gl.enableVertexAttribArray(aPos);
|
|
2912
|
-
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
|
|
2913
|
-
gl.enableVertexAttribArray(aColor);
|
|
2914
|
-
gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, stride, 8);
|
|
2915
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
2916
|
-
gl.bindVertexArray(null);
|
|
2917
|
-
this.growGpu(INITIAL_VERTICES, INITIAL_INDICES);
|
|
2978
|
+
this.aPos = aPos;
|
|
2979
|
+
this.aColor = aColor;
|
|
2918
2980
|
}
|
|
2919
2981
|
get length() {
|
|
2920
2982
|
return this.nIdx;
|
|
@@ -2984,6 +3046,7 @@ var SolidBatch = class {
|
|
|
2984
3046
|
const srcIdx = mesh.indices;
|
|
2985
3047
|
const n = src.length >> 1;
|
|
2986
3048
|
this.reserve(n, srcIdx.length);
|
|
3049
|
+
this.pureRects = false;
|
|
2987
3050
|
const v = this.verts;
|
|
2988
3051
|
let i = this.nVerts * FLOATS_PER_VERTEX;
|
|
2989
3052
|
const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
|
|
@@ -3004,67 +3067,109 @@ var SolidBatch = class {
|
|
|
3004
3067
|
this.nVerts += n;
|
|
3005
3068
|
this.nIdx += srcIdx.length;
|
|
3006
3069
|
}
|
|
3007
|
-
/** Upload the staged geometry and bind
|
|
3008
|
-
* count for the caller's `drawElements`. */
|
|
3070
|
+
/** Upload the staged geometry into the next set of buffers and bind its VAO.
|
|
3071
|
+
* Returns the index count for the caller's `drawElements`. */
|
|
3009
3072
|
uploadAndBind() {
|
|
3010
3073
|
const gl = this.gl;
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
gl.bindVertexArray(this.vao);
|
|
3015
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
|
|
3074
|
+
const set = this.nVerts <= SOLID_RING_SLOT_VERTICES && this.nIdx <= SOLID_RING_SLOT_INDICES ? this.nextRingSlot() : this.nextLargeSlot();
|
|
3075
|
+
gl.bindVertexArray(set.vao);
|
|
3076
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
|
|
3016
3077
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verts, 0, this.nVerts * FLOATS_PER_VERTEX);
|
|
3017
|
-
|
|
3078
|
+
const rects = this.pureRects ? this.nVerts >> 2 : -1;
|
|
3079
|
+
if (rects < 0 || rects !== set.indexRects) {
|
|
3080
|
+
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this.idx, 0, this.nIdx);
|
|
3081
|
+
set.indexRects = rects;
|
|
3082
|
+
}
|
|
3018
3083
|
return this.nIdx;
|
|
3019
3084
|
}
|
|
3020
3085
|
reset() {
|
|
3021
3086
|
this.nVerts = 0;
|
|
3022
3087
|
this.nIdx = 0;
|
|
3088
|
+
this.pureRects = true;
|
|
3023
3089
|
}
|
|
3024
3090
|
dispose() {
|
|
3025
|
-
const
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3091
|
+
for (const set of [...this.ring, ...this.largeRing]) {
|
|
3092
|
+
if (set) this.deleteSet(set);
|
|
3093
|
+
}
|
|
3094
|
+
this.ring.fill(void 0);
|
|
3095
|
+
this.largeRing.fill(void 0);
|
|
3029
3096
|
}
|
|
3030
3097
|
/** Grow the CPU arrays so `vertices` / `indices` more fit. */
|
|
3031
3098
|
reserve(vertices, indices) {
|
|
3032
3099
|
const needVerts = (this.nVerts + vertices) * FLOATS_PER_VERTEX;
|
|
3033
3100
|
if (needVerts > this.verts.length) {
|
|
3034
|
-
|
|
3035
|
-
while (len < needVerts) len *= 2;
|
|
3036
|
-
const grown = new Float32Array(len);
|
|
3101
|
+
const grown = new Float32Array(doubledTo(this.verts.length, needVerts));
|
|
3037
3102
|
grown.set(this.verts);
|
|
3038
3103
|
this.verts = grown;
|
|
3039
3104
|
}
|
|
3040
3105
|
const needIdx = this.nIdx + indices;
|
|
3041
3106
|
if (needIdx > this.idx.length) {
|
|
3042
|
-
|
|
3043
|
-
while (len < needIdx) len *= 2;
|
|
3044
|
-
const grown = new Uint32Array(len);
|
|
3107
|
+
const grown = new Uint32Array(doubledTo(this.idx.length, needIdx));
|
|
3045
3108
|
grown.set(this.idx);
|
|
3046
3109
|
this.idx = grown;
|
|
3047
3110
|
}
|
|
3048
3111
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3112
|
+
nextRingSlot() {
|
|
3113
|
+
const slot = this.next;
|
|
3114
|
+
this.next = (slot + 1) % SOLID_RING_SIZE;
|
|
3115
|
+
const existing = this.ring[slot];
|
|
3116
|
+
if (existing) return existing;
|
|
3117
|
+
const set = this.createSet(SOLID_RING_SLOT_VERTICES, SOLID_RING_SLOT_INDICES);
|
|
3118
|
+
this.ring[slot] = set;
|
|
3119
|
+
return set;
|
|
3120
|
+
}
|
|
3121
|
+
nextLargeSlot() {
|
|
3051
3122
|
const gl = this.gl;
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3123
|
+
const slot = this.nextLarge;
|
|
3124
|
+
this.nextLarge = (slot + 1) % SOLID_LARGE_RING_SIZE;
|
|
3125
|
+
let set = this.largeRing[slot];
|
|
3126
|
+
if (!set) {
|
|
3127
|
+
set = this.createSet(
|
|
3128
|
+
doubledTo(SOLID_RING_SLOT_VERTICES, this.nVerts),
|
|
3129
|
+
doubledTo(SOLID_RING_SLOT_INDICES, this.nIdx)
|
|
3130
|
+
);
|
|
3131
|
+
this.largeRing[slot] = set;
|
|
3132
|
+
return set;
|
|
3133
|
+
}
|
|
3134
|
+
if (this.nVerts > set.vertexCapacity) {
|
|
3135
|
+
set.vertexCapacity = doubledTo(set.vertexCapacity, this.nVerts);
|
|
3136
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
|
|
3137
|
+
gl.bufferData(gl.ARRAY_BUFFER, set.vertexCapacity * FLOATS_PER_VERTEX * 4, gl.DYNAMIC_DRAW);
|
|
3138
|
+
}
|
|
3139
|
+
if (this.nIdx > set.indexCapacity) {
|
|
3140
|
+
set.indexCapacity = doubledTo(set.indexCapacity, this.nIdx);
|
|
3141
|
+
gl.bindVertexArray(set.vao);
|
|
3142
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, set.ibo);
|
|
3143
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, set.indexCapacity * 4, gl.DYNAMIC_DRAW);
|
|
3065
3144
|
gl.bindVertexArray(null);
|
|
3066
|
-
|
|
3145
|
+
set.indexRects = -1;
|
|
3067
3146
|
}
|
|
3147
|
+
return set;
|
|
3148
|
+
}
|
|
3149
|
+
createSet(vertices, indices) {
|
|
3150
|
+
const gl = this.gl;
|
|
3151
|
+
const vao = gl.createVertexArray();
|
|
3152
|
+
const vbo = gl.createBuffer();
|
|
3153
|
+
const ibo = gl.createBuffer();
|
|
3154
|
+
if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
|
|
3155
|
+
const stride = FLOATS_PER_VERTEX * 4;
|
|
3156
|
+
gl.bindVertexArray(vao);
|
|
3157
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
3158
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertices * stride, gl.DYNAMIC_DRAW);
|
|
3159
|
+
gl.enableVertexAttribArray(this.aPos);
|
|
3160
|
+
gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, stride, 0);
|
|
3161
|
+
gl.enableVertexAttribArray(this.aColor);
|
|
3162
|
+
gl.vertexAttribPointer(this.aColor, 4, gl.FLOAT, false, stride, 8);
|
|
3163
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
3164
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices * 4, gl.DYNAMIC_DRAW);
|
|
3165
|
+
gl.bindVertexArray(null);
|
|
3166
|
+
return { vao, vbo, ibo, vertexCapacity: vertices, indexCapacity: indices, indexRects: -1 };
|
|
3167
|
+
}
|
|
3168
|
+
deleteSet(set) {
|
|
3169
|
+
const gl = this.gl;
|
|
3170
|
+
gl.deleteBuffer(set.vbo);
|
|
3171
|
+
gl.deleteBuffer(set.ibo);
|
|
3172
|
+
gl.deleteVertexArray(set.vao);
|
|
3068
3173
|
}
|
|
3069
3174
|
};
|
|
3070
3175
|
|
|
@@ -3089,6 +3194,25 @@ function sameValues(prev, next) {
|
|
|
3089
3194
|
for (let i = 0; i < prev.length; i++) if (prev[i] !== next[i]) return false;
|
|
3090
3195
|
return true;
|
|
3091
3196
|
}
|
|
3197
|
+
function setColorUniform(ctx, prog, r, g, b, a) {
|
|
3198
|
+
const loc = prog.uniform("u_color");
|
|
3199
|
+
if (loc === void 0) return;
|
|
3200
|
+
const uploaded = uploadedFor(ctx, prog);
|
|
3201
|
+
const prev = uploaded.color;
|
|
3202
|
+
if (prev !== void 0 && prev[0] === r && prev[1] === g && prev[2] === b && prev[3] === a) {
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
ctx.gl.uniform4f(loc, r, g, b, a);
|
|
3206
|
+
uploaded.color = [r, g, b, a];
|
|
3207
|
+
}
|
|
3208
|
+
function setAlphaUniform(ctx, prog, alpha) {
|
|
3209
|
+
const loc = prog.uniform("u_alpha");
|
|
3210
|
+
if (loc === void 0) return;
|
|
3211
|
+
const uploaded = uploadedFor(ctx, prog);
|
|
3212
|
+
if (uploaded.alpha === alpha) return;
|
|
3213
|
+
ctx.gl.uniform1f(loc, alpha);
|
|
3214
|
+
uploaded.alpha = alpha;
|
|
3215
|
+
}
|
|
3092
3216
|
var BATCH_MODEL = mat3.identity();
|
|
3093
3217
|
function projFor(ctx) {
|
|
3094
3218
|
let proj = FRAME_PROJ.get(ctx);
|
|
@@ -3381,8 +3505,8 @@ function flushSolids(ctx) {
|
|
|
3381
3505
|
gl.useProgram(prog.handle);
|
|
3382
3506
|
const indexCount = batch.uploadAndBind();
|
|
3383
3507
|
setProjAndModel(ctx, prog, BATCH_MODEL);
|
|
3384
|
-
|
|
3385
|
-
|
|
3508
|
+
setColorUniform(ctx, prog, 1, 1, 1, 1);
|
|
3509
|
+
setAlphaUniform(ctx, prog, staged.alpha);
|
|
3386
3510
|
setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
|
|
3387
3511
|
applyClipTest(ctx, staged.clipDepth);
|
|
3388
3512
|
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
|
|
@@ -3447,10 +3571,9 @@ function setProjAndModel(ctx, prog, model = ctx.state.transform) {
|
|
|
3447
3571
|
}
|
|
3448
3572
|
}
|
|
3449
3573
|
function setSolidPaintUniforms(ctx, prog, color, opacity) {
|
|
3450
|
-
const gl = ctx.gl;
|
|
3451
3574
|
const [r, g, b, a] = resolveColor(color);
|
|
3452
|
-
|
|
3453
|
-
|
|
3575
|
+
setColorUniform(ctx, prog, r, g, b, a * (opacity ?? 1));
|
|
3576
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
3454
3577
|
}
|
|
3455
3578
|
function drawPathFillByKind(ctx, fill, handle) {
|
|
3456
3579
|
const kind = fill.fill ?? "solid";
|
|
@@ -3496,7 +3619,7 @@ function drawPathFillPattern(ctx, fill, handle) {
|
|
|
3496
3619
|
ctx.textureCache.bind(tex.id, 0);
|
|
3497
3620
|
gl.uniform1i(ctx.patternFill.uniform("u_sampler"), 0);
|
|
3498
3621
|
gl.uniform1f(ctx.patternFill.uniform("u_opacity"), fill.opacity ?? 1);
|
|
3499
|
-
|
|
3622
|
+
setAlphaUniform(ctx, ctx.patternFill, ctx.state.alpha);
|
|
3500
3623
|
applyClipTest(ctx);
|
|
3501
3624
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
3502
3625
|
gl.bindVertexArray(null);
|
|
@@ -3520,7 +3643,7 @@ function drawPathFillGradient(ctx, fill, handle) {
|
|
|
3520
3643
|
gl.uniformMatrix3fv(ctx.gradFill.uniform("u_worldInv"), false, gradientSpaceInverse(ctx, fill.units));
|
|
3521
3644
|
ctx.gradRampCache.bind(key, 0);
|
|
3522
3645
|
gl.uniform1i(ctx.gradFill.uniform("u_ramp"), 0);
|
|
3523
|
-
|
|
3646
|
+
setAlphaUniform(ctx, ctx.gradFill, ctx.state.alpha);
|
|
3524
3647
|
gl.uniform1f(ctx.gradFill.uniform("u_opacity"), fill.opacity ?? 1);
|
|
3525
3648
|
if (fill.fill === "linear-gradient") {
|
|
3526
3649
|
gl.uniform1i(ctx.gradFill.uniform("u_gradKind"), 0);
|
|
@@ -3670,6 +3793,7 @@ function drawPathStrokeUnclipped(ctx, cmd) {
|
|
|
3670
3793
|
gl.enableVertexAttribArray(aVColorLoc);
|
|
3671
3794
|
gl.vertexAttribPointer(aVColorLoc, 4, gl.FLOAT, false, 0, 0);
|
|
3672
3795
|
}
|
|
3796
|
+
applyClipTest(ctx);
|
|
3673
3797
|
gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
|
|
3674
3798
|
gl.bindVertexArray(null);
|
|
3675
3799
|
gl.deleteBuffer(colorVbo);
|
|
@@ -3785,7 +3909,7 @@ function drawText(ctx, cmd) {
|
|
|
3785
3909
|
preparedPrograms.add(prog);
|
|
3786
3910
|
setProjAndModel(ctx, prog);
|
|
3787
3911
|
setColorMatrixUniforms(ctx, prog);
|
|
3788
|
-
|
|
3912
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
3789
3913
|
}
|
|
3790
3914
|
drawTextGroup(ctx, group, prog, dx, dy);
|
|
3791
3915
|
}
|
|
@@ -3903,8 +4027,8 @@ function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
|
3903
4027
|
if (!ibo) throw new Error("drawTextDecorations: createBuffer (IBO) returned null");
|
|
3904
4028
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
3905
4029
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.DYNAMIC_DRAW);
|
|
3906
|
-
|
|
3907
|
-
|
|
4030
|
+
setColorUniform(ctx, prog, rgba[0], rgba[1], rgba[2], rgba[3]);
|
|
4031
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
3908
4032
|
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0);
|
|
3909
4033
|
gl.bindVertexArray(null);
|
|
3910
4034
|
gl.deleteVertexArray(vao);
|
|
@@ -3988,7 +4112,7 @@ function drawTextGroup(ctx, group, prog, dx, dy) {
|
|
|
3988
4112
|
if ("color" in group.fill) {
|
|
3989
4113
|
[r, g, b, a] = resolveColor(group.fill.color);
|
|
3990
4114
|
}
|
|
3991
|
-
|
|
4115
|
+
setColorUniform(ctx, prog, r, g, b, a);
|
|
3992
4116
|
const synthBoldAmount = group.synthetic.bold ? 0.08 : 0;
|
|
3993
4117
|
const uSynthBold = prog.uniform("u_synthBold");
|
|
3994
4118
|
if (uSynthBold !== void 0) gl.uniform1f(uSynthBold, synthBoldAmount);
|
|
@@ -4092,7 +4216,7 @@ function drawImage(ctx, cmd) {
|
|
|
4092
4216
|
);
|
|
4093
4217
|
gl.uniform1i(prog.uniform("u_sampler"), 0);
|
|
4094
4218
|
gl.uniform1f(prog.uniform("u_opacity"), cmd.opacity ?? 1);
|
|
4095
|
-
|
|
4219
|
+
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
4096
4220
|
applyClipTest(ctx);
|
|
4097
4221
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
|
|
4098
4222
|
gl.bindVertexArray(null);
|
|
@@ -4321,6 +4445,8 @@ var WeaselRenderer = class {
|
|
|
4321
4445
|
const program = new ShaderProgram(this.gl, vertSrc, fragSrc);
|
|
4322
4446
|
program.lookupUniforms([...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(fragSrc)]);
|
|
4323
4447
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
4448
|
+
const previous = this.programRegistry.get(handle.id);
|
|
4449
|
+
if (previous) this.gl.deleteProgram(previous.handle);
|
|
4324
4450
|
this.programRegistry.set(handle.id, program);
|
|
4325
4451
|
}
|
|
4326
4452
|
applyViewport() {
|
|
@@ -4419,7 +4545,7 @@ var WeaselRenderer = class {
|
|
|
4419
4545
|
}
|
|
4420
4546
|
this.meshCache.freeTransient();
|
|
4421
4547
|
this.meshCache.drainPendingDeletes();
|
|
4422
|
-
for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill]) {
|
|
4548
|
+
for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill, this.patternFill]) {
|
|
4423
4549
|
gl.deleteProgram(prog.handle);
|
|
4424
4550
|
}
|
|
4425
4551
|
for (const prog of this.programRegistry.values()) {
|
|
@@ -4446,6 +4572,7 @@ var WeaselRenderer = class {
|
|
|
4446
4572
|
const gl = this.gl;
|
|
4447
4573
|
this.meshCache.drainPendingDeletes();
|
|
4448
4574
|
resetBakeBudget(this.bakeBudget);
|
|
4575
|
+
this.groupState.reset();
|
|
4449
4576
|
gl.stencilMask(255);
|
|
4450
4577
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
4451
4578
|
const ctx = {
|
|
@@ -5202,9 +5329,15 @@ function beginPointerDrag(payload, source, startX, startY, ghostFn) {
|
|
|
5202
5329
|
function onUp(e) {
|
|
5203
5330
|
if (!activeDrag) return;
|
|
5204
5331
|
const zone = findZone(e.clientX, e.clientY, payload.kind);
|
|
5332
|
+
const dropped = activeDrag.payload;
|
|
5205
5333
|
activeDrag.lastZone?.onOver?.(false);
|
|
5206
|
-
|
|
5207
|
-
|
|
5334
|
+
try {
|
|
5335
|
+
if (zone) zone.onDrop(dropped, e.clientX, e.clientY);
|
|
5336
|
+
} catch (err) {
|
|
5337
|
+
console.error("weasel pointerDrag: drop zone onDrop threw", err);
|
|
5338
|
+
} finally {
|
|
5339
|
+
cleanup();
|
|
5340
|
+
}
|
|
5208
5341
|
}
|
|
5209
5342
|
function onCancel() {
|
|
5210
5343
|
activeDrag?.lastZone?.onOver?.(false);
|
|
@@ -5225,6 +5358,10 @@ var DRAG_THRESHOLD_PX_SQ = 25;
|
|
|
5225
5358
|
function useDragHandle(getPayload, options) {
|
|
5226
5359
|
const optsRef = useRef(options);
|
|
5227
5360
|
optsRef.current = options;
|
|
5361
|
+
const pendingTeardownRef = useRef(null);
|
|
5362
|
+
useEffect(() => () => {
|
|
5363
|
+
pendingTeardownRef.current?.();
|
|
5364
|
+
}, []);
|
|
5228
5365
|
const onPointerDown = useCallback((e) => {
|
|
5229
5366
|
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
5230
5367
|
const target = e.currentTarget;
|
|
@@ -5260,10 +5397,12 @@ function useDragHandle(getPayload, options) {
|
|
|
5260
5397
|
document.removeEventListener("pointermove", onMove);
|
|
5261
5398
|
document.removeEventListener("pointerup", onEnd);
|
|
5262
5399
|
document.removeEventListener("pointercancel", onEnd);
|
|
5400
|
+
pendingTeardownRef.current = null;
|
|
5263
5401
|
}
|
|
5264
5402
|
document.addEventListener("pointermove", onMove);
|
|
5265
5403
|
document.addEventListener("pointerup", onEnd);
|
|
5266
5404
|
document.addEventListener("pointercancel", onEnd);
|
|
5405
|
+
pendingTeardownRef.current = cleanup;
|
|
5267
5406
|
}, [getPayload]);
|
|
5268
5407
|
return { onPointerDown, style: { touchAction: "none" } };
|
|
5269
5408
|
}
|
|
@@ -5817,7 +5956,7 @@ var IDENTITY_POSE_COMPOSITION = {
|
|
|
5817
5956
|
compose: (_parent, child) => child,
|
|
5818
5957
|
decompose: (_parent, world) => world
|
|
5819
5958
|
};
|
|
5820
|
-
function composeWorldPose(adapter, id,
|
|
5959
|
+
function composeWorldPose(adapter, id, compose2) {
|
|
5821
5960
|
const chain = [id];
|
|
5822
5961
|
const seen = /* @__PURE__ */ new Set([id]);
|
|
5823
5962
|
let cursor = adapter.getParent(id);
|
|
@@ -5829,7 +5968,7 @@ function composeWorldPose(adapter, id, compose) {
|
|
|
5829
5968
|
}
|
|
5830
5969
|
let world = adapter.getPose(chain[chain.length - 1]);
|
|
5831
5970
|
for (let i = chain.length - 2; i >= 0; i--) {
|
|
5832
|
-
world =
|
|
5971
|
+
world = compose2(world, adapter.getPose(chain[i]));
|
|
5833
5972
|
}
|
|
5834
5973
|
return world;
|
|
5835
5974
|
}
|
|
@@ -5843,9 +5982,9 @@ function composeRectPose(parent, child) {
|
|
|
5843
5982
|
function translateRectPose(pose, dx, dy) {
|
|
5844
5983
|
return { ...pose, x: pose.x + dx, y: pose.y + dy };
|
|
5845
5984
|
}
|
|
5846
|
-
function rebaseLocalPose(adapter, worldPose, newParentId,
|
|
5985
|
+
function rebaseLocalPose(adapter, worldPose, newParentId, compose2, decompose) {
|
|
5847
5986
|
if (newParentId === null) return worldPose;
|
|
5848
|
-
const parentWorld = composeWorldPose(adapter, newParentId,
|
|
5987
|
+
const parentWorld = composeWorldPose(adapter, newParentId, compose2);
|
|
5849
5988
|
return decompose(parentWorld, worldPose);
|
|
5850
5989
|
}
|
|
5851
5990
|
function decomposeRectPose(parent, world) {
|
|
@@ -5855,10 +5994,10 @@ function decomposeRectPose(parent, world) {
|
|
|
5855
5994
|
y: world.y - parent.y
|
|
5856
5995
|
};
|
|
5857
5996
|
}
|
|
5858
|
-
function worldPoseLookup(adapter,
|
|
5997
|
+
function worldPoseLookup(adapter, compose2) {
|
|
5859
5998
|
return (id) => {
|
|
5860
5999
|
try {
|
|
5861
|
-
return composeWorldPose(adapter, id,
|
|
6000
|
+
return composeWorldPose(adapter, id, compose2);
|
|
5862
6001
|
} catch {
|
|
5863
6002
|
return null;
|
|
5864
6003
|
}
|
|
@@ -8916,9 +9055,17 @@ function arraysEqual(a, b) {
|
|
|
8916
9055
|
return true;
|
|
8917
9056
|
}
|
|
8918
9057
|
function createPartitionedReorderOp(args) {
|
|
8919
|
-
const { ids, fn, label } = args;
|
|
8920
|
-
|
|
9058
|
+
const { ids, direction, fn, label, prevOrders } = args;
|
|
9059
|
+
const argsForSerial = {
|
|
9060
|
+
ids,
|
|
9061
|
+
direction,
|
|
9062
|
+
label,
|
|
9063
|
+
prevOrders: prevOrders ? prevOrders.map((e) => ({ ...e, before: e.before.slice() })) : void 0
|
|
9064
|
+
};
|
|
9065
|
+
let restore = prevOrders ? prevOrders.map((e) => ({ ...e, before: e.before.slice() })) : null;
|
|
8921
9066
|
return {
|
|
9067
|
+
name: "reorder",
|
|
9068
|
+
args: argsForSerial,
|
|
8922
9069
|
label,
|
|
8923
9070
|
apply(adapter) {
|
|
8924
9071
|
const a = adapter;
|
|
@@ -8941,6 +9088,7 @@ function createPartitionedReorderOp(args) {
|
|
|
8941
9088
|
mutated = true;
|
|
8942
9089
|
}
|
|
8943
9090
|
restore = snapshots;
|
|
9091
|
+
argsForSerial.prevOrders = snapshots.map((e) => ({ ...e, before: e.before.slice() }));
|
|
8944
9092
|
return mutated;
|
|
8945
9093
|
},
|
|
8946
9094
|
invert() {
|
|
@@ -8972,10 +9120,17 @@ var REORDER_DIRECTIONS = {
|
|
|
8972
9120
|
back: { fn: sendToBack, defaultLabel: "Send to back" }
|
|
8973
9121
|
};
|
|
8974
9122
|
function createReorderOp(args) {
|
|
8975
|
-
const { ids, direction, label } = args;
|
|
9123
|
+
const { ids, direction, label, prevOrders } = args;
|
|
8976
9124
|
const { fn, defaultLabel } = REORDER_DIRECTIONS[direction];
|
|
8977
|
-
return createPartitionedReorderOp({
|
|
9125
|
+
return createPartitionedReorderOp({
|
|
9126
|
+
ids,
|
|
9127
|
+
direction,
|
|
9128
|
+
fn,
|
|
9129
|
+
label: label ?? defaultLabel,
|
|
9130
|
+
prevOrders
|
|
9131
|
+
});
|
|
8978
9132
|
}
|
|
9133
|
+
registerOpFactory("reorder", (args) => createReorderOp(args));
|
|
8979
9134
|
function createMoveToIndexOp(args) {
|
|
8980
9135
|
const { ids, parentId, index, label } = args;
|
|
8981
9136
|
const argsForSerial = {
|
|
@@ -9102,7 +9257,7 @@ var reorderBackwardAction = {
|
|
|
9102
9257
|
}
|
|
9103
9258
|
],
|
|
9104
9259
|
eligible: { capability: "edits-page" },
|
|
9105
|
-
requires: ["selection", "scene"],
|
|
9260
|
+
requires: ["selection", "scene", "applyOps"],
|
|
9106
9261
|
invoker: {
|
|
9107
9262
|
timing: "immediate",
|
|
9108
9263
|
run: (deps, params) => {
|
|
@@ -9269,6 +9424,7 @@ function makeAlignAction(edge) {
|
|
|
9269
9424
|
icon: ICON_FOR[edge],
|
|
9270
9425
|
group: "align",
|
|
9271
9426
|
eligible: { capability: "transforms-selection" },
|
|
9427
|
+
requires: ["selection", "scene"],
|
|
9272
9428
|
// No default keybindings — six edges/centers don't fit a clean default
|
|
9273
9429
|
// chord set. Wire bindings explicitly via the actions registry override map.
|
|
9274
9430
|
invoker: {
|
|
@@ -9280,7 +9436,14 @@ function makeAlignAction(edge) {
|
|
|
9280
9436
|
alignSelection(selection, scene, edge);
|
|
9281
9437
|
}
|
|
9282
9438
|
},
|
|
9283
|
-
|
|
9439
|
+
// Deps-aware, matching `alignSelection`'s own `ids.length < 2` guard: a
|
|
9440
|
+
// constant disabled reason greys the entry out forever (see
|
|
9441
|
+
// `requiresSelection`).
|
|
9442
|
+
enabled: (deps) => {
|
|
9443
|
+
const selection = deps?.selection;
|
|
9444
|
+
const count = selection?.get().length ?? 0;
|
|
9445
|
+
return count >= 2 ? true : ActionDisabledReason.SelectionRequired;
|
|
9446
|
+
}
|
|
9284
9447
|
};
|
|
9285
9448
|
}
|
|
9286
9449
|
var alignLeftAction = makeAlignAction("left");
|
|
@@ -9361,6 +9524,7 @@ function makeDistributeAction(axis) {
|
|
|
9361
9524
|
icon: ICON_FOR2[axis],
|
|
9362
9525
|
group: "distribute",
|
|
9363
9526
|
eligible: { capability: "transforms-selection" },
|
|
9527
|
+
requires: ["selection", "scene"],
|
|
9364
9528
|
// No default keybindings. Wire bindings explicitly via the actions registry.
|
|
9365
9529
|
invoker: {
|
|
9366
9530
|
timing: "immediate",
|
|
@@ -9371,7 +9535,14 @@ function makeDistributeAction(axis) {
|
|
|
9371
9535
|
distributeSelection(selection, scene, axis);
|
|
9372
9536
|
}
|
|
9373
9537
|
},
|
|
9374
|
-
|
|
9538
|
+
// Deps-aware, matching `distributeSelection`'s own `ids.length < 3` guard:
|
|
9539
|
+
// a constant disabled reason greys the entry out forever (see
|
|
9540
|
+
// `requiresSelection`).
|
|
9541
|
+
enabled: (deps) => {
|
|
9542
|
+
const selection = deps?.selection;
|
|
9543
|
+
const count = selection?.get().length ?? 0;
|
|
9544
|
+
return count >= 3 ? true : ActionDisabledReason.SelectionRequired;
|
|
9545
|
+
}
|
|
9375
9546
|
};
|
|
9376
9547
|
}
|
|
9377
9548
|
var distributeHorizontalAction = makeDistributeAction("x");
|
|
@@ -9455,7 +9626,91 @@ function pathToMultiPolygon(path, opts = {}) {
|
|
|
9455
9626
|
}
|
|
9456
9627
|
finalizeCurrent();
|
|
9457
9628
|
if (rings.length === 0) return [];
|
|
9458
|
-
return rings.
|
|
9629
|
+
return groupRings(rings, path.fillRule);
|
|
9630
|
+
}
|
|
9631
|
+
function ringSignedArea(ring) {
|
|
9632
|
+
let a = 0;
|
|
9633
|
+
for (let i = 0, n = ring.length - 1; i < n; i++) {
|
|
9634
|
+
a += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
|
|
9635
|
+
}
|
|
9636
|
+
return a * 0.5;
|
|
9637
|
+
}
|
|
9638
|
+
function ringBox(ring) {
|
|
9639
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
9640
|
+
for (const [x, y] of ring) {
|
|
9641
|
+
if (x < minX) minX = x;
|
|
9642
|
+
if (x > maxX) maxX = x;
|
|
9643
|
+
if (y < minY) minY = y;
|
|
9644
|
+
if (y > maxY) maxY = y;
|
|
9645
|
+
}
|
|
9646
|
+
return [minX, minY, maxX, maxY];
|
|
9647
|
+
}
|
|
9648
|
+
function pointInRing(ring, px, py) {
|
|
9649
|
+
let inside = false;
|
|
9650
|
+
for (let i = 0, n = ring.length - 1; i < n; i++) {
|
|
9651
|
+
const [ax, ay] = ring[i];
|
|
9652
|
+
const [bx, by] = ring[i + 1];
|
|
9653
|
+
if (ay > py !== by > py && px < (bx - ax) * (py - ay) / (by - ay) + ax) {
|
|
9654
|
+
inside = !inside;
|
|
9655
|
+
}
|
|
9656
|
+
}
|
|
9657
|
+
return inside;
|
|
9658
|
+
}
|
|
9659
|
+
function ringContains(outer, outerBox, inner, innerBox) {
|
|
9660
|
+
if (innerBox[0] < outerBox[0] || innerBox[1] < outerBox[1] || innerBox[2] > outerBox[2] || innerBox[3] > outerBox[3]) return false;
|
|
9661
|
+
const n = inner.length - 1;
|
|
9662
|
+
let votes = 0;
|
|
9663
|
+
for (const k of [0, Math.floor(n / 3), Math.floor(2 * n / 3)]) {
|
|
9664
|
+
if (pointInRing(outer, inner[k][0], inner[k][1])) votes++;
|
|
9665
|
+
}
|
|
9666
|
+
return votes >= 2;
|
|
9667
|
+
}
|
|
9668
|
+
function groupRings(rings, fillRule) {
|
|
9669
|
+
const n = rings.length;
|
|
9670
|
+
if (n === 1) return [[rings[0]]];
|
|
9671
|
+
const boxes = rings.map(ringBox);
|
|
9672
|
+
const orient = rings.map((r) => ringSignedArea(r) >= 0 ? 1 : -1);
|
|
9673
|
+
const parent = new Array(n).fill(-1);
|
|
9674
|
+
const children = rings.map(() => []);
|
|
9675
|
+
for (let i = 0; i < n; i++) {
|
|
9676
|
+
let best = -1;
|
|
9677
|
+
let bestArea = Infinity;
|
|
9678
|
+
for (let j = 0; j < n; j++) {
|
|
9679
|
+
if (i === j) continue;
|
|
9680
|
+
if (!ringContains(rings[j], boxes[j], rings[i], boxes[i])) continue;
|
|
9681
|
+
const area = (boxes[j][2] - boxes[j][0]) * (boxes[j][3] - boxes[j][1]);
|
|
9682
|
+
if (area < bestArea) {
|
|
9683
|
+
bestArea = area;
|
|
9684
|
+
best = j;
|
|
9685
|
+
}
|
|
9686
|
+
}
|
|
9687
|
+
parent[i] = best;
|
|
9688
|
+
}
|
|
9689
|
+
for (let i = 0; i < n; i++) if (parent[i] >= 0) children[parent[i]].push(i);
|
|
9690
|
+
const filled = new Array(n);
|
|
9691
|
+
for (let i = 0; i < n; i++) {
|
|
9692
|
+
let depth = 0;
|
|
9693
|
+
let winding = 0;
|
|
9694
|
+
for (let a = i; a >= 0; a = parent[a]) {
|
|
9695
|
+
winding += orient[a];
|
|
9696
|
+
if (a !== i) depth++;
|
|
9697
|
+
}
|
|
9698
|
+
filled[i] = fillRule === "evenodd" ? depth % 2 === 0 : winding !== 0;
|
|
9699
|
+
}
|
|
9700
|
+
const out = [];
|
|
9701
|
+
for (let i = 0; i < n; i++) {
|
|
9702
|
+
if (!filled[i]) continue;
|
|
9703
|
+
if (parent[i] >= 0 && filled[parent[i]]) continue;
|
|
9704
|
+
const polygon = [rings[i]];
|
|
9705
|
+
const queue = [...children[i]];
|
|
9706
|
+
while (queue.length > 0) {
|
|
9707
|
+
const c = queue.pop();
|
|
9708
|
+
if (filled[c]) queue.push(...children[c]);
|
|
9709
|
+
else polygon.push(rings[c]);
|
|
9710
|
+
}
|
|
9711
|
+
out.push(polygon);
|
|
9712
|
+
}
|
|
9713
|
+
return out;
|
|
9459
9714
|
}
|
|
9460
9715
|
function multiPolygonToPath(mp) {
|
|
9461
9716
|
let nCmds = 0;
|
|
@@ -9772,6 +10027,7 @@ function makePathfinderAction(op) {
|
|
|
9772
10027
|
icon: ICON_FOR3[op],
|
|
9773
10028
|
group: "pathfinder",
|
|
9774
10029
|
eligible: { capability: "creates-shapes" },
|
|
10030
|
+
requires: ["selection", "booleansAdapter"],
|
|
9775
10031
|
// No default keybindings — there's no industry-standard chord set for
|
|
9776
10032
|
// boolean ops. Wire bindings explicitly via the actions registry override map.
|
|
9777
10033
|
invoker: {
|
|
@@ -12169,10 +12425,11 @@ function createDispatcher(opts) {
|
|
|
12169
12425
|
uiOngoingByAction.set(actionId, gestureId);
|
|
12170
12426
|
notify2();
|
|
12171
12427
|
let ended = false;
|
|
12428
|
+
const live = () => !ended && inFlightHandles.get(gestureId) === handle;
|
|
12172
12429
|
return {
|
|
12173
12430
|
gestureId,
|
|
12174
12431
|
update(nextParams) {
|
|
12175
|
-
if (
|
|
12432
|
+
if (!live()) return;
|
|
12176
12433
|
if (!handle.onMove) return;
|
|
12177
12434
|
try {
|
|
12178
12435
|
handle.onMove(buildUiInvocationCtx(deps, nextParams));
|
|
@@ -12182,7 +12439,10 @@ function createDispatcher(opts) {
|
|
|
12182
12439
|
notify2();
|
|
12183
12440
|
},
|
|
12184
12441
|
end(reason) {
|
|
12185
|
-
if (
|
|
12442
|
+
if (!live()) {
|
|
12443
|
+
ended = true;
|
|
12444
|
+
return;
|
|
12445
|
+
}
|
|
12186
12446
|
ended = true;
|
|
12187
12447
|
if (handle.onEnd) {
|
|
12188
12448
|
try {
|
|
@@ -13509,6 +13769,7 @@ function useTools(opts) {
|
|
|
13509
13769
|
);
|
|
13510
13770
|
const active = contributions.focused;
|
|
13511
13771
|
const getActiveOverlays = contributions.overlays;
|
|
13772
|
+
const registryKey = Object.keys(slotted.registry).sort().join("|") + "#" + slotted.ambient.map((t) => t.id).join("|");
|
|
13512
13773
|
return useMemo(
|
|
13513
13774
|
() => ({
|
|
13514
13775
|
active,
|
|
@@ -13521,7 +13782,7 @@ function useTools(opts) {
|
|
|
13521
13782
|
has,
|
|
13522
13783
|
getActiveOverlays
|
|
13523
13784
|
}),
|
|
13524
|
-
[active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays]
|
|
13785
|
+
[active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays, registryKey]
|
|
13525
13786
|
);
|
|
13526
13787
|
}
|
|
13527
13788
|
|
|
@@ -15128,8 +15389,17 @@ function composeAffordanceLayer(id, label, affordances) {
|
|
|
15128
15389
|
// `buildAffordanceAt` share one implementation rather than two that can
|
|
15129
15390
|
// drift. This wrapper lifts the region's declared cursor and claim onto
|
|
15130
15391
|
// the binding, the way `buildAffordanceAt` lifts them onto `AffordanceHit`.
|
|
15131
|
-
hitTest: (wx, wy,
|
|
15132
|
-
const
|
|
15392
|
+
hitTest: (wx, wy, data, view, _dims, isVisible) => {
|
|
15393
|
+
const state = asChromeState(data);
|
|
15394
|
+
if (!state) return null;
|
|
15395
|
+
const hit = hitAffordanceRegions(
|
|
15396
|
+
affordances,
|
|
15397
|
+
wx,
|
|
15398
|
+
wy,
|
|
15399
|
+
state,
|
|
15400
|
+
view,
|
|
15401
|
+
isVisible ?? asIsVisible(data) ?? void 0
|
|
15402
|
+
);
|
|
15133
15403
|
if (!hit) return null;
|
|
15134
15404
|
const { cursor, strength, claimedKinds } = hit.region;
|
|
15135
15405
|
return {
|
|
@@ -15489,16 +15759,6 @@ function useRotateTool(adapter, options) {
|
|
|
15489
15759
|
[overlay, handleHitRadius, rotationHandleDistance]
|
|
15490
15760
|
);
|
|
15491
15761
|
}
|
|
15492
|
-
|
|
15493
|
-
// src/tools/builtin/hitExistingGate.ts
|
|
15494
|
-
function applyHitExistingGate(ctx, hitExisting) {
|
|
15495
|
-
if (!hitExisting) return false;
|
|
15496
|
-
const hit = hitExisting({ x: ctx.worldX, y: ctx.worldY });
|
|
15497
|
-
if (!hit) return false;
|
|
15498
|
-
const ids = Array.isArray(hit) ? hit : [hit];
|
|
15499
|
-
ctx.selection.set(ids);
|
|
15500
|
-
return true;
|
|
15501
|
-
}
|
|
15502
15762
|
function useHandTool(opts = {}) {
|
|
15503
15763
|
const inertia = opts.inertia === false ? false : opts.inertia;
|
|
15504
15764
|
const axis = opts.axis ?? "both";
|
|
@@ -15750,6 +16010,10 @@ function usePenTool(options) {
|
|
|
15750
16010
|
onEnd: (endCtx, reason) => {
|
|
15751
16011
|
const se = s();
|
|
15752
16012
|
if (reason === "cancel") {
|
|
16013
|
+
if (se.current && se.draggingHandleAt === se.current.anchors.length - 1) {
|
|
16014
|
+
se.current.anchors.pop();
|
|
16015
|
+
if (se.current.anchors.length === 0) se.current = null;
|
|
16016
|
+
}
|
|
15753
16017
|
se.draggingHandleAt = null;
|
|
15754
16018
|
forceRenderRef.current();
|
|
15755
16019
|
return;
|
|
@@ -16699,7 +16963,7 @@ function composeSelectionPose(opts) {
|
|
|
16699
16963
|
const visit = (nid) => {
|
|
16700
16964
|
const kids = getChildren(nid);
|
|
16701
16965
|
if (kids.length === 0) {
|
|
16702
|
-
out.push(nid);
|
|
16966
|
+
if (!isContainer(nid)) out.push(nid);
|
|
16703
16967
|
return;
|
|
16704
16968
|
}
|
|
16705
16969
|
for (const k of kids) visit(k);
|
|
@@ -16752,7 +17016,7 @@ function makeContainerAwareBoundsResolver(getPose, getBounds, getChildren, isCon
|
|
|
16752
17016
|
const visit = (nid) => {
|
|
16753
17017
|
const kids = getChildren(nid);
|
|
16754
17018
|
if (kids.length === 0) {
|
|
16755
|
-
out.push(nid);
|
|
17019
|
+
if (!isContainer(nid)) out.push(nid);
|
|
16756
17020
|
return;
|
|
16757
17021
|
}
|
|
16758
17022
|
for (const k of kids) visit(k);
|
|
@@ -17541,7 +17805,6 @@ function CanvasInner(props, ref) {
|
|
|
17541
17805
|
shaders,
|
|
17542
17806
|
previewIdsExtra,
|
|
17543
17807
|
previewPoseExtra,
|
|
17544
|
-
previewBoundsExtra,
|
|
17545
17808
|
gestureSource,
|
|
17546
17809
|
viewport,
|
|
17547
17810
|
backgroundFill,
|
|
@@ -17567,6 +17830,7 @@ function CanvasInner(props, ref) {
|
|
|
17567
17830
|
debugSinkRefForCtx.current = debugSink;
|
|
17568
17831
|
const adapter = adapterProp;
|
|
17569
17832
|
const canvasRef = useRef(null);
|
|
17833
|
+
const helpersForLayersRef = useRef(null);
|
|
17570
17834
|
const dimsRef = useRef({ width, height });
|
|
17571
17835
|
dimsRef.current = { width, height };
|
|
17572
17836
|
const getIsVisibleRef = useRef(getIsVisible);
|
|
@@ -17589,7 +17853,7 @@ function CanvasInner(props, ref) {
|
|
|
17589
17853
|
const isVisible = getIsVisibleRef.current?.() ?? alwaysVisible;
|
|
17590
17854
|
for (const layer of layers2) {
|
|
17591
17855
|
if (!layer.hitTest) continue;
|
|
17592
|
-
const hit = layer.hitTest(worldX, worldY,
|
|
17856
|
+
const hit = layer.hitTest(worldX, worldY, helpersForLayersRef.current, view, dims, isVisible);
|
|
17593
17857
|
if (hit) return { layerId: layer.id, hit };
|
|
17594
17858
|
}
|
|
17595
17859
|
return null;
|
|
@@ -17744,8 +18008,8 @@ function CanvasInner(props, ref) {
|
|
|
17744
18008
|
};
|
|
17745
18009
|
const selectedIdsForWiring = effectiveSelection.current;
|
|
17746
18010
|
const multiActive2 = selectedIdsForWiring.length > 1;
|
|
17747
|
-
const previewExtraRef = useRef({ previewPoseExtra
|
|
17748
|
-
previewExtraRef.current = { previewPoseExtra
|
|
18011
|
+
const previewExtraRef = useRef({ previewPoseExtra });
|
|
18012
|
+
previewExtraRef.current = { previewPoseExtra };
|
|
17749
18013
|
const effectiveBoundsOf = useMemo(() => {
|
|
17750
18014
|
return boundsOf2 ?? baseBoundsOf;
|
|
17751
18015
|
}, [boundsOf2, baseBoundsOf]);
|
|
@@ -17764,10 +18028,6 @@ function CanvasInner(props, ref) {
|
|
|
17764
18028
|
if (p != null) return geometry.getBounds(p);
|
|
17765
18029
|
}
|
|
17766
18030
|
const extras = previewExtraRef.current;
|
|
17767
|
-
if (extras.previewBoundsExtra) {
|
|
17768
|
-
const b = extras.previewBoundsExtra(id);
|
|
17769
|
-
if (b) return b;
|
|
17770
|
-
}
|
|
17771
18031
|
if (extras.previewPoseExtra) {
|
|
17772
18032
|
const p = extras.previewPoseExtra(id);
|
|
17773
18033
|
if (p != null) return geometry.getBounds(p);
|
|
@@ -17808,10 +18068,6 @@ function CanvasInner(props, ref) {
|
|
|
17808
18068
|
const p = firstPreviewPose(tools, id);
|
|
17809
18069
|
if (p != null) return geometry.getBounds(p);
|
|
17810
18070
|
}
|
|
17811
|
-
if (previewBoundsExtra) {
|
|
17812
|
-
const b = previewBoundsExtra(id);
|
|
17813
|
-
if (b) return b;
|
|
17814
|
-
}
|
|
17815
18071
|
if (previewPoseExtra) {
|
|
17816
18072
|
const p = previewPoseExtra(id);
|
|
17817
18073
|
if (p != null) return geometry.getBounds(p);
|
|
@@ -17847,6 +18103,7 @@ function CanvasInner(props, ref) {
|
|
|
17847
18103
|
getDebug: () => debugSink,
|
|
17848
18104
|
getIsVisible: () => getIsVisibleRef.current?.() ?? alwaysVisible
|
|
17849
18105
|
};
|
|
18106
|
+
helpersForLayersRef.current = helpersForLayers;
|
|
17850
18107
|
if (helpersRef) helpersRef.current = helpersForLayers;
|
|
17851
18108
|
const pointerDownRef = useRef(false);
|
|
17852
18109
|
const handlePointerDown = (e) => {
|
|
@@ -18028,6 +18285,11 @@ function CanvasInner(props, ref) {
|
|
|
18028
18285
|
);
|
|
18029
18286
|
renderer.render(commands, viewToMat3(effectiveView));
|
|
18030
18287
|
}, [layersWithDebug, width, height, effectiveView, debugSink, redrawNonce, dprProp]);
|
|
18288
|
+
useEffect(() => () => {
|
|
18289
|
+
glRendererRef.current?.dispose();
|
|
18290
|
+
glRendererRef.current = null;
|
|
18291
|
+
lastResizeRef.current = null;
|
|
18292
|
+
}, []);
|
|
18031
18293
|
const shaderIdKey = shaders?.map((h) => h.id).join("|") ?? "";
|
|
18032
18294
|
useEffect(() => {
|
|
18033
18295
|
const renderer = glRendererRef.current;
|
|
@@ -19492,6 +19754,89 @@ function poseContainsRotated(pose, wx, wy, tolerance) {
|
|
|
19492
19754
|
}
|
|
19493
19755
|
return poseContains(pose, wx, wy, tolerance);
|
|
19494
19756
|
}
|
|
19757
|
+
function hiddenLayerIds(layers) {
|
|
19758
|
+
const out = /* @__PURE__ */ new Set();
|
|
19759
|
+
for (const layer of layers ?? []) {
|
|
19760
|
+
if (layer.visible === false) out.add(layer.id);
|
|
19761
|
+
}
|
|
19762
|
+
return out;
|
|
19763
|
+
}
|
|
19764
|
+
function hitTestArea(scene, bounds) {
|
|
19765
|
+
const { x, y, width: w, height: h } = bounds;
|
|
19766
|
+
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
19767
|
+
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
19768
|
+
}
|
|
19769
|
+
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
19770
|
+
const ab = areaBounds ?? boundsOf(area);
|
|
19771
|
+
if (!ab) return [];
|
|
19772
|
+
const hits = [];
|
|
19773
|
+
const hidden = hiddenLayerIds(scene.layers);
|
|
19774
|
+
const order = scene.renderOrderNodes();
|
|
19775
|
+
for (let i = 0; i < order.length; i++) {
|
|
19776
|
+
const node = order[i];
|
|
19777
|
+
if (node.kind === "container") continue;
|
|
19778
|
+
if (hidden.size > 0 && hidden.has(node.layer)) continue;
|
|
19779
|
+
const pose = node.pose;
|
|
19780
|
+
const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
|
|
19781
|
+
const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
|
|
19782
|
+
if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
|
|
19783
|
+
continue;
|
|
19784
|
+
}
|
|
19785
|
+
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) {
|
|
19786
|
+
continue;
|
|
19787
|
+
}
|
|
19788
|
+
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) {
|
|
19789
|
+
hits.push(node.id);
|
|
19790
|
+
continue;
|
|
19791
|
+
}
|
|
19792
|
+
if (silhouette) {
|
|
19793
|
+
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
19794
|
+
hits.push(node.id);
|
|
19795
|
+
}
|
|
19796
|
+
continue;
|
|
19797
|
+
}
|
|
19798
|
+
const drawn = findShapeSilhouette(node, pose);
|
|
19799
|
+
if (drawn?.kind === "polygon") {
|
|
19800
|
+
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
19801
|
+
continue;
|
|
19802
|
+
}
|
|
19803
|
+
hits.push(node.id);
|
|
19804
|
+
}
|
|
19805
|
+
return hits;
|
|
19806
|
+
}
|
|
19807
|
+
function silhouetteOverlapsArea(sil, area) {
|
|
19808
|
+
const ns = sil.length >> 1;
|
|
19809
|
+
const na = area.length >> 1;
|
|
19810
|
+
if (ns < 1 || na < 3) return false;
|
|
19811
|
+
for (let i = 0; i < ns; i++) {
|
|
19812
|
+
if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
|
|
19813
|
+
}
|
|
19814
|
+
for (let i = 0; i < na; i++) {
|
|
19815
|
+
if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
|
|
19816
|
+
}
|
|
19817
|
+
for (let i = 0, j = ns - 1; i < ns; j = i++) {
|
|
19818
|
+
const ax = sil[j * 2], ay = sil[j * 2 + 1];
|
|
19819
|
+
const bx = sil[i * 2], by = sil[i * 2 + 1];
|
|
19820
|
+
for (let k = 0, l = na - 1; k < na; l = k++) {
|
|
19821
|
+
const cx = area[l * 2], cy = area[l * 2 + 1];
|
|
19822
|
+
const dx = area[k * 2], dy = area[k * 2 + 1];
|
|
19823
|
+
if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
|
|
19824
|
+
}
|
|
19825
|
+
}
|
|
19826
|
+
return false;
|
|
19827
|
+
}
|
|
19828
|
+
function boundsOf(coords) {
|
|
19829
|
+
if (coords.length < 2) return null;
|
|
19830
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
19831
|
+
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
19832
|
+
const x = coords[i], y = coords[i + 1];
|
|
19833
|
+
if (x < minX) minX = x;
|
|
19834
|
+
if (y < minY) minY = y;
|
|
19835
|
+
if (x > maxX) maxX = x;
|
|
19836
|
+
if (y > maxY) maxY = y;
|
|
19837
|
+
}
|
|
19838
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
19839
|
+
}
|
|
19495
19840
|
|
|
19496
19841
|
// src/canvas/SceneCanvas/useSceneSelectTool.ts
|
|
19497
19842
|
var DEFAULT_PICK_TOLERANCE_PX = 4;
|
|
@@ -19591,7 +19936,9 @@ function useSceneSelectTool(args) {
|
|
|
19591
19936
|
}
|
|
19592
19937
|
const tolerance = pickTolerancePx / meanScale(getView?.()?.scale ?? { x: 1, y: 1 });
|
|
19593
19938
|
const out = [];
|
|
19939
|
+
const hidden = hiddenLayerIds(scene.layers);
|
|
19594
19940
|
for (const n of scene.renderOrderNodes()) {
|
|
19941
|
+
if (hidden.size > 0 && hidden.has(n.layer)) continue;
|
|
19595
19942
|
if (!poseContainsRotated(n.pose, wx, wy, tolerance)) continue;
|
|
19596
19943
|
if (shapePicking && !shapeCoversPoint(n, n.pose, wx, wy, { tolerance })) continue;
|
|
19597
19944
|
out.push(n.id);
|
|
@@ -20494,8 +20841,10 @@ function createPathAnchorAffordances(getAnchorState, opts = {}) {
|
|
|
20494
20841
|
const anchors = {
|
|
20495
20842
|
id: PATH_ANCHOR_CHROME_ID,
|
|
20496
20843
|
regions(state) {
|
|
20844
|
+
const editingId = getAnchorState()?.editingId ?? null;
|
|
20497
20845
|
const out = [];
|
|
20498
20846
|
for (const { id, path } of editablePaths(state)) {
|
|
20847
|
+
if (editingId !== null && id !== editingId) continue;
|
|
20499
20848
|
for (const a of enumerateAnchors(path)) {
|
|
20500
20849
|
out.push(point(
|
|
20501
20850
|
`${id}:anchor:${a.anchorIndex}`,
|
|
@@ -20758,80 +21107,6 @@ function DepRegistryProviderIfRoot({ children }) {
|
|
|
20758
21107
|
if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
20759
21108
|
return /* @__PURE__ */ jsx(DepRegistryProvider, { children });
|
|
20760
21109
|
}
|
|
20761
|
-
function hitTestArea(scene, bounds) {
|
|
20762
|
-
const { x, y, width: w, height: h } = bounds;
|
|
20763
|
-
const area = [x, y, x + w, y, x + w, y + h, x, y + h];
|
|
20764
|
-
return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
|
|
20765
|
-
}
|
|
20766
|
-
function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
|
|
20767
|
-
const ab = areaBounds ?? boundsOf(area);
|
|
20768
|
-
if (!ab) return [];
|
|
20769
|
-
const hits = [];
|
|
20770
|
-
const order = scene.renderOrderNodes();
|
|
20771
|
-
for (let i = 0; i < order.length; i++) {
|
|
20772
|
-
const node = order[i];
|
|
20773
|
-
if (node.kind === "container") continue;
|
|
20774
|
-
const pose = node.pose;
|
|
20775
|
-
const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
|
|
20776
|
-
const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
|
|
20777
|
-
if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
|
|
20778
|
-
continue;
|
|
20779
|
-
}
|
|
20780
|
-
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) {
|
|
20781
|
-
continue;
|
|
20782
|
-
}
|
|
20783
|
-
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) {
|
|
20784
|
-
hits.push(node.id);
|
|
20785
|
-
continue;
|
|
20786
|
-
}
|
|
20787
|
-
if (silhouette) {
|
|
20788
|
-
if (silhouetteOverlapsArea(pose.coords, area)) {
|
|
20789
|
-
hits.push(node.id);
|
|
20790
|
-
}
|
|
20791
|
-
continue;
|
|
20792
|
-
}
|
|
20793
|
-
const drawn = findShapeSilhouette(node, pose);
|
|
20794
|
-
if (drawn?.kind === "polygon") {
|
|
20795
|
-
if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
|
|
20796
|
-
continue;
|
|
20797
|
-
}
|
|
20798
|
-
hits.push(node.id);
|
|
20799
|
-
}
|
|
20800
|
-
return hits;
|
|
20801
|
-
}
|
|
20802
|
-
function silhouetteOverlapsArea(sil, area) {
|
|
20803
|
-
const ns = sil.length >> 1;
|
|
20804
|
-
const na = area.length >> 1;
|
|
20805
|
-
if (ns < 1 || na < 3) return false;
|
|
20806
|
-
for (let i = 0; i < ns; i++) {
|
|
20807
|
-
if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
|
|
20808
|
-
}
|
|
20809
|
-
for (let i = 0; i < na; i++) {
|
|
20810
|
-
if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
|
|
20811
|
-
}
|
|
20812
|
-
for (let i = 0, j = ns - 1; i < ns; j = i++) {
|
|
20813
|
-
const ax = sil[j * 2], ay = sil[j * 2 + 1];
|
|
20814
|
-
const bx = sil[i * 2], by = sil[i * 2 + 1];
|
|
20815
|
-
for (let k = 0, l = na - 1; k < na; l = k++) {
|
|
20816
|
-
const cx = area[l * 2], cy = area[l * 2 + 1];
|
|
20817
|
-
const dx = area[k * 2], dy = area[k * 2 + 1];
|
|
20818
|
-
if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
|
|
20819
|
-
}
|
|
20820
|
-
}
|
|
20821
|
-
return false;
|
|
20822
|
-
}
|
|
20823
|
-
function boundsOf(coords) {
|
|
20824
|
-
if (coords.length < 2) return null;
|
|
20825
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
20826
|
-
for (let i = 0; i + 1 < coords.length; i += 2) {
|
|
20827
|
-
const x = coords[i], y = coords[i + 1];
|
|
20828
|
-
if (x < minX) minX = x;
|
|
20829
|
-
if (y < minY) minY = y;
|
|
20830
|
-
if (x > maxX) maxX = x;
|
|
20831
|
-
if (y > maxY) maxY = y;
|
|
20832
|
-
}
|
|
20833
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
20834
|
-
}
|
|
20835
21110
|
function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize) {
|
|
20836
21111
|
const viewApiRef = useRef({
|
|
20837
21112
|
get: () => currentViewRef.current,
|
|
@@ -21634,6 +21909,7 @@ function useActionsPropResolver(actions) {
|
|
|
21634
21909
|
}
|
|
21635
21910
|
if (!prop) return;
|
|
21636
21911
|
const unregisters = [];
|
|
21912
|
+
const restores = [];
|
|
21637
21913
|
const warnedIds = /* @__PURE__ */ new Set();
|
|
21638
21914
|
for (const [slotId, entry] of Object.entries(prop)) {
|
|
21639
21915
|
if (entry === null) {
|
|
@@ -21661,10 +21937,14 @@ function useActionsPropResolver(actions) {
|
|
|
21661
21937
|
}
|
|
21662
21938
|
const { id: _drop, ...rest } = entry;
|
|
21663
21939
|
const merged = { ...existing, ...rest };
|
|
21940
|
+
restores.push(() => {
|
|
21941
|
+
reg.register(existing);
|
|
21942
|
+
});
|
|
21664
21943
|
unregisters.push(reg.register(merged));
|
|
21665
21944
|
}
|
|
21666
21945
|
return () => {
|
|
21667
21946
|
for (const u of unregisters) u();
|
|
21947
|
+
for (const r of restores) r();
|
|
21668
21948
|
};
|
|
21669
21949
|
}, [reg, actions]);
|
|
21670
21950
|
}
|
|
@@ -24894,23 +25174,7 @@ function nestedHitTester(adapter, opts) {
|
|
|
24894
25174
|
};
|
|
24895
25175
|
return { pickOutermost, pickBest };
|
|
24896
25176
|
}
|
|
24897
|
-
|
|
24898
|
-
// src/features/paths/pathDistance.ts
|
|
24899
25177
|
var BEZIER_SAMPLES = 16;
|
|
24900
|
-
function pointSegDist2(px, py, ax, ay, bx, by) {
|
|
24901
|
-
const dx = bx - ax, dy = by - ay;
|
|
24902
|
-
const len2 = dx * dx + dy * dy;
|
|
24903
|
-
if (len2 === 0) {
|
|
24904
|
-
const ex2 = px - ax, ey2 = py - ay;
|
|
24905
|
-
return ex2 * ex2 + ey2 * ey2;
|
|
24906
|
-
}
|
|
24907
|
-
let t = ((px - ax) * dx + (py - ay) * dy) / len2;
|
|
24908
|
-
if (t < 0) t = 0;
|
|
24909
|
-
else if (t > 1) t = 1;
|
|
24910
|
-
const cx = ax + dx * t, cy = ay + dy * t;
|
|
24911
|
-
const ex = px - cx, ey = py - cy;
|
|
24912
|
-
return ex * ex + ey * ey;
|
|
24913
|
-
}
|
|
24914
25178
|
function pointCubicDist2(px, py, x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
24915
25179
|
let prevX = x0, prevY = y0;
|
|
24916
25180
|
let best = Infinity;
|
|
@@ -24919,7 +25183,7 @@ function pointCubicDist2(px, py, x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
|
24919
25183
|
const u = 1 - t;
|
|
24920
25184
|
const sx = u * u * u * x0 + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * x3;
|
|
24921
25185
|
const sy = u * u * u * y0 + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * y3;
|
|
24922
|
-
const d2 =
|
|
25186
|
+
const d2 = pointSegmentDist2(px, py, prevX, prevY, sx, sy);
|
|
24923
25187
|
if (d2 < best) best = d2;
|
|
24924
25188
|
prevX = sx;
|
|
24925
25189
|
prevY = sy;
|
|
@@ -24956,7 +25220,7 @@ function polygonPathDistance(path, px, py) {
|
|
|
24956
25220
|
ci += 2;
|
|
24957
25221
|
} else if (cmd === PATH_L) {
|
|
24958
25222
|
const x = coords[ci], y = coords[ci + 1];
|
|
24959
|
-
const d2 =
|
|
25223
|
+
const d2 = pointSegmentDist2(px, py, curX, curY, x, y);
|
|
24960
25224
|
if (d2 < best) best = d2;
|
|
24961
25225
|
curX = x;
|
|
24962
25226
|
curY = y;
|
|
@@ -24983,7 +25247,7 @@ function polygonPathDistance(path, px, py) {
|
|
|
24983
25247
|
curY = y;
|
|
24984
25248
|
ci += 4;
|
|
24985
25249
|
} else if (cmd === PATH_Z) {
|
|
24986
|
-
const d2 =
|
|
25250
|
+
const d2 = pointSegmentDist2(px, py, curX, curY, startX, startY);
|
|
24987
25251
|
if (d2 < best) best = d2;
|
|
24988
25252
|
curX = startX;
|
|
24989
25253
|
curY = startY;
|
|
@@ -25836,7 +26100,10 @@ function arrayAdapter(config) {
|
|
|
25836
26100
|
},
|
|
25837
26101
|
getParent,
|
|
25838
26102
|
setParent,
|
|
25839
|
-
insertNode: (obj) => setItems((items) =>
|
|
26103
|
+
insertNode: (obj, index) => setItems((items) => {
|
|
26104
|
+
if (index === void 0 || index < 0 || index >= items.length) return [...items, obj];
|
|
26105
|
+
return [...items.slice(0, index), obj, ...items.slice(index)];
|
|
26106
|
+
}),
|
|
25840
26107
|
removeNode: (id) => setItems((items) => items.filter((o) => o.id !== id)),
|
|
25841
26108
|
getSelection,
|
|
25842
26109
|
setSelection,
|
|
@@ -27122,6 +27389,225 @@ function makeBuilder(animator, timers, createSupervisor, watchCompletion, items,
|
|
|
27122
27389
|
};
|
|
27123
27390
|
}
|
|
27124
27391
|
|
|
27392
|
+
// src/animation/timeline/sampleTrack.ts
|
|
27393
|
+
function floorKeyIndex(keys, t) {
|
|
27394
|
+
let lo = 0;
|
|
27395
|
+
let hi = keys.length - 1;
|
|
27396
|
+
let found = -1;
|
|
27397
|
+
while (lo <= hi) {
|
|
27398
|
+
const mid2 = lo + hi >> 1;
|
|
27399
|
+
if (keys[mid2].t <= t) {
|
|
27400
|
+
found = mid2;
|
|
27401
|
+
lo = mid2 + 1;
|
|
27402
|
+
} else {
|
|
27403
|
+
hi = mid2 - 1;
|
|
27404
|
+
}
|
|
27405
|
+
}
|
|
27406
|
+
return found;
|
|
27407
|
+
}
|
|
27408
|
+
function sampleTrack(track, t, segmentCache) {
|
|
27409
|
+
const { keys } = track;
|
|
27410
|
+
if (keys.length === 0) return void 0;
|
|
27411
|
+
const i = floorKeyIndex(keys, t);
|
|
27412
|
+
if (i < 0) return keys[0].value;
|
|
27413
|
+
if (i >= keys.length - 1) return keys[keys.length - 1].value;
|
|
27414
|
+
const a = keys[i];
|
|
27415
|
+
const b = keys[i + 1];
|
|
27416
|
+
const span = b.t - a.t;
|
|
27417
|
+
const raw = span <= 0 ? 1 : (t - a.t) / span;
|
|
27418
|
+
const u = b.easing ? b.easing(raw) : raw;
|
|
27419
|
+
if (track.interpolator) {
|
|
27420
|
+
let fn = segmentCache?.get(i + 1);
|
|
27421
|
+
if (!fn) {
|
|
27422
|
+
fn = track.interpolator(a.value, b.value);
|
|
27423
|
+
segmentCache?.set(i + 1, fn);
|
|
27424
|
+
}
|
|
27425
|
+
return fn(u);
|
|
27426
|
+
}
|
|
27427
|
+
if (track.interpolate) return track.interpolate(a.value, b.value, u);
|
|
27428
|
+
if (typeof a.value === "number" && typeof b.value === "number") {
|
|
27429
|
+
return a.value + (b.value - a.value) * u;
|
|
27430
|
+
}
|
|
27431
|
+
throw new Error("sampleTrack: interpolate or interpolator is required for non-numeric keyframe values");
|
|
27432
|
+
}
|
|
27433
|
+
|
|
27434
|
+
// src/animation/timeline/createTimeline.ts
|
|
27435
|
+
function firstAfter(events, t) {
|
|
27436
|
+
let lo = 0;
|
|
27437
|
+
let hi = events.length;
|
|
27438
|
+
while (lo < hi) {
|
|
27439
|
+
const mid2 = lo + hi >> 1;
|
|
27440
|
+
if (events[mid2].t > t) hi = mid2;
|
|
27441
|
+
else lo = mid2 + 1;
|
|
27442
|
+
}
|
|
27443
|
+
return lo;
|
|
27444
|
+
}
|
|
27445
|
+
function trackEnd(track) {
|
|
27446
|
+
switch (track.kind) {
|
|
27447
|
+
case "sampled":
|
|
27448
|
+
return track.keys.length ? track.keys[track.keys.length - 1].t : 0;
|
|
27449
|
+
case "event":
|
|
27450
|
+
return track.events.length ? track.events[track.events.length - 1].t : 0;
|
|
27451
|
+
case "timeline":
|
|
27452
|
+
return track.at + tracksEnd(track.timeline.tracks, track.timeline.duration);
|
|
27453
|
+
}
|
|
27454
|
+
}
|
|
27455
|
+
function tracksEnd(tracks, explicit) {
|
|
27456
|
+
if (explicit != null) return explicit;
|
|
27457
|
+
let max = 0;
|
|
27458
|
+
for (const t of tracks) max = Math.max(max, trackEnd(t));
|
|
27459
|
+
return max;
|
|
27460
|
+
}
|
|
27461
|
+
function createTimeline(register, id, opts) {
|
|
27462
|
+
let duration = tracksEnd(opts.tracks, opts.duration);
|
|
27463
|
+
let offset = 0;
|
|
27464
|
+
let lastVirtual = 0;
|
|
27465
|
+
let playhead = 0;
|
|
27466
|
+
let prevPlayhead = -Infinity;
|
|
27467
|
+
let done = false;
|
|
27468
|
+
let live = true;
|
|
27469
|
+
let cancelled = false;
|
|
27470
|
+
let wantPaused = opts.autoplay === false;
|
|
27471
|
+
let wantScale = 1;
|
|
27472
|
+
const subscribers2 = /* @__PURE__ */ new Set();
|
|
27473
|
+
const loopOpt = opts.loop ?? false;
|
|
27474
|
+
let loopsLeft = loopOpt === true ? Infinity : loopOpt === false ? 0 : loopOpt;
|
|
27475
|
+
let caches = /* @__PURE__ */ new WeakMap();
|
|
27476
|
+
const cacheFor = (track) => {
|
|
27477
|
+
let c = caches.get(track);
|
|
27478
|
+
if (!c) {
|
|
27479
|
+
c = /* @__PURE__ */ new Map();
|
|
27480
|
+
caches.set(track, c);
|
|
27481
|
+
}
|
|
27482
|
+
return c;
|
|
27483
|
+
};
|
|
27484
|
+
const applySampled = (tracks, t) => {
|
|
27485
|
+
for (const track of tracks) {
|
|
27486
|
+
if (track.kind === "sampled") {
|
|
27487
|
+
const st = track;
|
|
27488
|
+
const v = sampleTrack(st, t, cacheFor(st));
|
|
27489
|
+
if (v !== void 0) st.onTick(v);
|
|
27490
|
+
} else if (track.kind === "timeline") {
|
|
27491
|
+
applySampled(track.timeline.tracks, t - track.at);
|
|
27492
|
+
}
|
|
27493
|
+
}
|
|
27494
|
+
};
|
|
27495
|
+
const fireEvents = (tracks, from, to) => {
|
|
27496
|
+
for (const track of tracks) {
|
|
27497
|
+
if (track.kind === "event") {
|
|
27498
|
+
const end = firstAfter(track.events, to);
|
|
27499
|
+
for (let i = firstAfter(track.events, from); i < end; i += 1) track.events[i].fire();
|
|
27500
|
+
} else if (track.kind === "timeline") {
|
|
27501
|
+
fireEvents(track.timeline.tracks, from - track.at, to - track.at);
|
|
27502
|
+
}
|
|
27503
|
+
}
|
|
27504
|
+
};
|
|
27505
|
+
const onWrap = () => {
|
|
27506
|
+
fireEvents(opts.tracks, prevPlayhead, duration);
|
|
27507
|
+
prevPlayhead = -Infinity;
|
|
27508
|
+
};
|
|
27509
|
+
const tick = (virtualNow) => {
|
|
27510
|
+
lastVirtual = virtualNow;
|
|
27511
|
+
playhead = virtualNow + offset;
|
|
27512
|
+
if (duration > 0 && playhead >= duration) {
|
|
27513
|
+
if (loopsLeft === Infinity) {
|
|
27514
|
+
const laps = Math.floor(playhead / duration);
|
|
27515
|
+
offset -= laps * duration;
|
|
27516
|
+
playhead -= laps * duration;
|
|
27517
|
+
onWrap();
|
|
27518
|
+
} else {
|
|
27519
|
+
while (playhead >= duration && loopsLeft > 0) {
|
|
27520
|
+
loopsLeft -= 1;
|
|
27521
|
+
offset -= duration;
|
|
27522
|
+
playhead -= duration;
|
|
27523
|
+
onWrap();
|
|
27524
|
+
}
|
|
27525
|
+
}
|
|
27526
|
+
}
|
|
27527
|
+
const finished = playhead >= duration;
|
|
27528
|
+
playhead = Math.min(playhead, duration);
|
|
27529
|
+
applySampled(opts.tracks, playhead);
|
|
27530
|
+
fireEvents(opts.tracks, prevPlayhead, playhead);
|
|
27531
|
+
prevPlayhead = playhead;
|
|
27532
|
+
if (finished && !done) {
|
|
27533
|
+
done = true;
|
|
27534
|
+
opts.onDone?.();
|
|
27535
|
+
}
|
|
27536
|
+
if (playhead < duration) return false;
|
|
27537
|
+
live = false;
|
|
27538
|
+
return true;
|
|
27539
|
+
};
|
|
27540
|
+
const onCancel = () => {
|
|
27541
|
+
cancelled = true;
|
|
27542
|
+
live = false;
|
|
27543
|
+
};
|
|
27544
|
+
const base = register({ id, cancelKey: opts.cancelKey, tick, onCancel });
|
|
27545
|
+
if (opts.autoplay === false) base.pause();
|
|
27546
|
+
const rearm = () => {
|
|
27547
|
+
if (cancelled || playhead >= duration) return;
|
|
27548
|
+
done = false;
|
|
27549
|
+
if (live) return;
|
|
27550
|
+
lastVirtual = 0;
|
|
27551
|
+
offset = playhead;
|
|
27552
|
+
live = true;
|
|
27553
|
+
register({ id, cancelKey: opts.cancelKey, keepExisting: true, tick, onCancel });
|
|
27554
|
+
if (wantPaused) base.pause();
|
|
27555
|
+
base.setTimeScale(wantScale);
|
|
27556
|
+
};
|
|
27557
|
+
return {
|
|
27558
|
+
...base,
|
|
27559
|
+
// Not just `onCancel`: cancelling a finished timeline never reaches the
|
|
27560
|
+
// animator, and must still keep a later seek from reviving it.
|
|
27561
|
+
cancel() {
|
|
27562
|
+
onCancel();
|
|
27563
|
+
base.cancel();
|
|
27564
|
+
},
|
|
27565
|
+
// Playback intent is tracked here, not read back from the entry: a revived
|
|
27566
|
+
// one is a fresh registration, and defaults to running at scale 1.
|
|
27567
|
+
pause() {
|
|
27568
|
+
wantPaused = true;
|
|
27569
|
+
base.pause();
|
|
27570
|
+
},
|
|
27571
|
+
resume() {
|
|
27572
|
+
wantPaused = false;
|
|
27573
|
+
base.resume();
|
|
27574
|
+
},
|
|
27575
|
+
setTimeScale(s) {
|
|
27576
|
+
wantScale = s;
|
|
27577
|
+
base.setTimeScale(s);
|
|
27578
|
+
},
|
|
27579
|
+
isPaused: () => live ? base.isPaused() : wantPaused,
|
|
27580
|
+
seek(t) {
|
|
27581
|
+
offset = t - lastVirtual;
|
|
27582
|
+
playhead = t;
|
|
27583
|
+
prevPlayhead = t;
|
|
27584
|
+
rearm();
|
|
27585
|
+
},
|
|
27586
|
+
time: () => playhead,
|
|
27587
|
+
duration: () => duration,
|
|
27588
|
+
tracks: () => opts.tracks,
|
|
27589
|
+
edit(fn) {
|
|
27590
|
+
fn();
|
|
27591
|
+
caches = /* @__PURE__ */ new WeakMap();
|
|
27592
|
+
duration = tracksEnd(opts.tracks, opts.duration);
|
|
27593
|
+
rearm();
|
|
27594
|
+
for (const cb of subscribers2) {
|
|
27595
|
+
try {
|
|
27596
|
+
cb();
|
|
27597
|
+
} catch (err) {
|
|
27598
|
+
console.error("timeline: subscriber threw", err);
|
|
27599
|
+
}
|
|
27600
|
+
}
|
|
27601
|
+
},
|
|
27602
|
+
subscribe(cb) {
|
|
27603
|
+
subscribers2.add(cb);
|
|
27604
|
+
return () => {
|
|
27605
|
+
subscribers2.delete(cb);
|
|
27606
|
+
};
|
|
27607
|
+
}
|
|
27608
|
+
};
|
|
27609
|
+
}
|
|
27610
|
+
|
|
27125
27611
|
// src/animation/colorRegistry.ts
|
|
27126
27612
|
var ColorOverrideRegistry = class {
|
|
27127
27613
|
map = /* @__PURE__ */ new Map();
|
|
@@ -27276,7 +27762,7 @@ function useAnimator(opts = {}) {
|
|
|
27276
27762
|
}
|
|
27277
27763
|
};
|
|
27278
27764
|
const register = (seed) => {
|
|
27279
|
-
if (seed.cancelKey != null) cancelByKey(seed.cancelKey);
|
|
27765
|
+
if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey);
|
|
27280
27766
|
const anim = seed;
|
|
27281
27767
|
anim.paused = false;
|
|
27282
27768
|
anim.timeScale = 1;
|
|
@@ -27515,6 +28001,7 @@ function useAnimator(opts = {}) {
|
|
|
27515
28001
|
factory,
|
|
27516
28002
|
staggerOpts
|
|
27517
28003
|
)),
|
|
28004
|
+
timeline: (o) => createTimeline(register, nextId.current++, o),
|
|
27518
28005
|
colorOverrides: colorOverrides.current,
|
|
27519
28006
|
onTick: (cb) => {
|
|
27520
28007
|
tickSubscribers.current.add(cb);
|
|
@@ -27586,8 +28073,8 @@ function animateLifecycle(adapter, animator, opts) {
|
|
|
27586
28073
|
const ms = opts.ms ?? 200;
|
|
27587
28074
|
return {
|
|
27588
28075
|
...adapter,
|
|
27589
|
-
insertNode(object) {
|
|
27590
|
-
adapter.insertNode(object);
|
|
28076
|
+
insertNode(object, index) {
|
|
28077
|
+
adapter.insertNode(object, index);
|
|
27591
28078
|
if (!opts.enterFrom) return;
|
|
27592
28079
|
const final = adapter.getPose(object.id);
|
|
27593
28080
|
const start = opts.enterFrom(final);
|
|
@@ -28050,6 +28537,101 @@ function hslToRgbUnit(h, s, l) {
|
|
|
28050
28537
|
return hslToRgb(h, s, l);
|
|
28051
28538
|
}
|
|
28052
28539
|
|
|
28540
|
+
// src/animation/rig/types.ts
|
|
28541
|
+
var IDENTITY_JOINT = {
|
|
28542
|
+
x: 0,
|
|
28543
|
+
y: 0,
|
|
28544
|
+
rotation: 0,
|
|
28545
|
+
scaleX: 1,
|
|
28546
|
+
scaleY: 1
|
|
28547
|
+
};
|
|
28548
|
+
|
|
28549
|
+
// src/animation/rig/blendPoses.ts
|
|
28550
|
+
var TAU = Math.PI * 2;
|
|
28551
|
+
function shortestDelta(a, b) {
|
|
28552
|
+
let d = (b - a) % TAU;
|
|
28553
|
+
if (d > Math.PI) d -= TAU;
|
|
28554
|
+
if (d < -Math.PI) d += TAU;
|
|
28555
|
+
return d;
|
|
28556
|
+
}
|
|
28557
|
+
var FIELDS = ["x", "y", "scaleX", "scaleY"];
|
|
28558
|
+
function blendPoses(poses, weights) {
|
|
28559
|
+
if (poses.length !== weights.length) {
|
|
28560
|
+
throw new Error("blendPoses: poses and weights must have the same length");
|
|
28561
|
+
}
|
|
28562
|
+
const total = weights.reduce((s, w) => s + w, 0);
|
|
28563
|
+
if (total === 0) return {};
|
|
28564
|
+
const norm = weights.map((w) => w / total);
|
|
28565
|
+
const names = /* @__PURE__ */ new Set();
|
|
28566
|
+
for (const p of poses) for (const k of Object.keys(p)) names.add(k);
|
|
28567
|
+
const out = {};
|
|
28568
|
+
for (const name of names) {
|
|
28569
|
+
const joint = {};
|
|
28570
|
+
for (const field of FIELDS) {
|
|
28571
|
+
let acc = 0;
|
|
28572
|
+
for (let i = 0; i < poses.length; i += 1) {
|
|
28573
|
+
const v = poses[i][name]?.[field];
|
|
28574
|
+
acc += (v ?? IDENTITY_JOINT[field]) * norm[i];
|
|
28575
|
+
}
|
|
28576
|
+
joint[field] = acc;
|
|
28577
|
+
}
|
|
28578
|
+
const base = poses[0][name]?.rotation ?? 0;
|
|
28579
|
+
let rot = 0;
|
|
28580
|
+
for (let i = 0; i < poses.length; i += 1) {
|
|
28581
|
+
const v = poses[i][name]?.rotation ?? 0;
|
|
28582
|
+
rot += shortestDelta(base, v) * norm[i];
|
|
28583
|
+
}
|
|
28584
|
+
joint.rotation = base + rot;
|
|
28585
|
+
out[name] = joint;
|
|
28586
|
+
}
|
|
28587
|
+
return out;
|
|
28588
|
+
}
|
|
28589
|
+
|
|
28590
|
+
// src/animation/rig/resolveSkeleton.ts
|
|
28591
|
+
function compose(bind, delta3) {
|
|
28592
|
+
if (!delta3) return bind;
|
|
28593
|
+
return {
|
|
28594
|
+
x: bind.x + (delta3.x ?? 0),
|
|
28595
|
+
y: bind.y + (delta3.y ?? 0),
|
|
28596
|
+
rotation: bind.rotation + (delta3.rotation ?? 0),
|
|
28597
|
+
scaleX: bind.scaleX * (delta3.scaleX ?? 1),
|
|
28598
|
+
scaleY: bind.scaleY * (delta3.scaleY ?? 1)
|
|
28599
|
+
};
|
|
28600
|
+
}
|
|
28601
|
+
function toMat3(t) {
|
|
28602
|
+
const c = Math.cos(t.rotation);
|
|
28603
|
+
const s = Math.sin(t.rotation);
|
|
28604
|
+
const m = new Float32Array(9);
|
|
28605
|
+
m[0] = c * t.scaleX;
|
|
28606
|
+
m[1] = s * t.scaleX;
|
|
28607
|
+
m[2] = 0;
|
|
28608
|
+
m[3] = -s * t.scaleY;
|
|
28609
|
+
m[4] = c * t.scaleY;
|
|
28610
|
+
m[5] = 0;
|
|
28611
|
+
m[6] = t.x;
|
|
28612
|
+
m[7] = t.y;
|
|
28613
|
+
m[8] = 1;
|
|
28614
|
+
return m;
|
|
28615
|
+
}
|
|
28616
|
+
function resolveSkeleton(skeleton, pose) {
|
|
28617
|
+
const out = /* @__PURE__ */ new Map();
|
|
28618
|
+
for (const joint of skeleton.joints) {
|
|
28619
|
+
const local = toMat3(compose(joint.bind, pose[joint.name]));
|
|
28620
|
+
if (joint.parent == null) {
|
|
28621
|
+
out.set(joint.name, local);
|
|
28622
|
+
continue;
|
|
28623
|
+
}
|
|
28624
|
+
const parent = out.get(joint.parent);
|
|
28625
|
+
if (!parent) {
|
|
28626
|
+
throw new Error(
|
|
28627
|
+
`resolveSkeleton: joint "${joint.name}" names parent "${joint.parent}", which has not been resolved. Skeleton.joints must be in topological order.`
|
|
28628
|
+
);
|
|
28629
|
+
}
|
|
28630
|
+
out.set(joint.name, mat3.multiply(parent, local));
|
|
28631
|
+
}
|
|
28632
|
+
return out;
|
|
28633
|
+
}
|
|
28634
|
+
|
|
28053
28635
|
// src/layout/snaps.ts
|
|
28054
28636
|
function dist22(a, b) {
|
|
28055
28637
|
const dx = a.x - b.x;
|
|
@@ -28338,6 +28920,6 @@ function mergeContributions(...bundles) {
|
|
|
28338
28920
|
return out;
|
|
28339
28921
|
}
|
|
28340
28922
|
|
|
28341
|
-
export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp,
|
|
28342
|
-
//# sourceMappingURL=chunk-
|
|
28343
|
-
//# sourceMappingURL=chunk-
|
|
28923
|
+
export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, blendPoses, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveSkeleton, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, viewportsAt, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
|
|
28924
|
+
//# sourceMappingURL=chunk-3B4QEB2G.js.map
|
|
28925
|
+
//# sourceMappingURL=chunk-3B4QEB2G.js.map
|