@oh-just-another/scene 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/a11y.d.ts +23 -0
  6. package/dist/a11y.d.ts.map +1 -0
  7. package/dist/a11y.js +37 -0
  8. package/dist/a11y.js.map +1 -0
  9. package/dist/anchors.d.ts +120 -0
  10. package/dist/anchors.d.ts.map +1 -0
  11. package/dist/anchors.js +322 -0
  12. package/dist/anchors.js.map +1 -0
  13. package/dist/annotation-geometry.d.ts +11 -0
  14. package/dist/annotation-geometry.d.ts.map +1 -0
  15. package/dist/annotation-geometry.js +19 -0
  16. package/dist/annotation-geometry.js.map +1 -0
  17. package/dist/annotation.d.ts +32 -0
  18. package/dist/annotation.d.ts.map +1 -0
  19. package/dist/annotation.js +2 -0
  20. package/dist/annotation.js.map +1 -0
  21. package/dist/constants.d.ts +186 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +186 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/container.d.ts +112 -0
  26. package/dist/container.d.ts.map +1 -0
  27. package/dist/container.js +227 -0
  28. package/dist/container.js.map +1 -0
  29. package/dist/diff.d.ts +22 -0
  30. package/dist/diff.d.ts.map +1 -0
  31. package/dist/diff.js +18 -0
  32. package/dist/diff.js.map +1 -0
  33. package/dist/edge-curve.d.ts +37 -0
  34. package/dist/edge-curve.d.ts.map +1 -0
  35. package/dist/edge-curve.js +72 -0
  36. package/dist/edge-curve.js.map +1 -0
  37. package/dist/edge-geometry.d.ts +96 -0
  38. package/dist/edge-geometry.d.ts.map +1 -0
  39. package/dist/edge-geometry.js +467 -0
  40. package/dist/edge-geometry.js.map +1 -0
  41. package/dist/edge.d.ts +206 -0
  42. package/dist/edge.d.ts.map +1 -0
  43. package/dist/edge.js +2 -0
  44. package/dist/edge.js.map +1 -0
  45. package/dist/elbow-link.d.ts +23 -0
  46. package/dist/elbow-link.d.ts.map +1 -0
  47. package/dist/elbow-link.js +419 -0
  48. package/dist/elbow-link.js.map +1 -0
  49. package/dist/elbow-router.d.ts +20 -0
  50. package/dist/elbow-router.d.ts.map +1 -0
  51. package/dist/elbow-router.js +218 -0
  52. package/dist/elbow-router.js.map +1 -0
  53. package/dist/file.d.ts +41 -0
  54. package/dist/file.d.ts.map +1 -0
  55. package/dist/file.js +13 -0
  56. package/dist/file.js.map +1 -0
  57. package/dist/heading.d.ts +42 -0
  58. package/dist/heading.d.ts.map +1 -0
  59. package/dist/heading.js +77 -0
  60. package/dist/heading.js.map +1 -0
  61. package/dist/index.d.ts +47 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +40 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/layer.d.ts +18 -0
  66. package/dist/layer.d.ts.map +1 -0
  67. package/dist/layer.js +2 -0
  68. package/dist/layer.js.map +1 -0
  69. package/dist/layout-registry.d.ts +35 -0
  70. package/dist/layout-registry.d.ts.map +1 -0
  71. package/dist/layout-registry.js +13 -0
  72. package/dist/layout-registry.js.map +1 -0
  73. package/dist/layout.d.ts +128 -0
  74. package/dist/layout.d.ts.map +1 -0
  75. package/dist/layout.js +453 -0
  76. package/dist/layout.js.map +1 -0
  77. package/dist/operations.d.ts +32 -0
  78. package/dist/operations.d.ts.map +1 -0
  79. package/dist/operations.js +105 -0
  80. package/dist/operations.js.map +1 -0
  81. package/dist/outline.d.ts +36 -0
  82. package/dist/outline.d.ts.map +1 -0
  83. package/dist/outline.js +129 -0
  84. package/dist/outline.js.map +1 -0
  85. package/dist/patch.d.ts +57 -0
  86. package/dist/patch.d.ts.map +1 -0
  87. package/dist/patch.js +47 -0
  88. package/dist/patch.js.map +1 -0
  89. package/dist/queries.d.ts +115 -0
  90. package/dist/queries.d.ts.map +1 -0
  91. package/dist/queries.js +403 -0
  92. package/dist/queries.js.map +1 -0
  93. package/dist/render-bounds.d.ts +36 -0
  94. package/dist/render-bounds.d.ts.map +1 -0
  95. package/dist/render-bounds.js +36 -0
  96. package/dist/render-bounds.js.map +1 -0
  97. package/dist/scene.d.ts +80 -0
  98. package/dist/scene.d.ts.map +1 -0
  99. package/dist/scene.js +129 -0
  100. package/dist/scene.js.map +1 -0
  101. package/dist/shape.d.ts +306 -0
  102. package/dist/shape.d.ts.map +1 -0
  103. package/dist/shape.js +192 -0
  104. package/dist/shape.js.map +1 -0
  105. package/dist/snap.d.ts +88 -0
  106. package/dist/snap.d.ts.map +1 -0
  107. package/dist/snap.js +128 -0
  108. package/dist/snap.js.map +1 -0
  109. package/dist/spatial.d.ts +38 -0
  110. package/dist/spatial.d.ts.map +1 -0
  111. package/dist/spatial.js +95 -0
  112. package/dist/spatial.js.map +1 -0
  113. package/dist/style.d.ts +87 -0
  114. package/dist/style.d.ts.map +1 -0
  115. package/dist/style.js +43 -0
  116. package/dist/style.js.map +1 -0
  117. package/dist/text-measure.d.ts +29 -0
  118. package/dist/text-measure.d.ts.map +1 -0
  119. package/dist/text-measure.js +8 -0
  120. package/dist/text-measure.js.map +1 -0
  121. package/dist/three-way-merge.d.ts +59 -0
  122. package/dist/three-way-merge.d.ts.map +1 -0
  123. package/dist/three-way-merge.js +108 -0
  124. package/dist/three-way-merge.js.map +1 -0
  125. package/dist/viewport.d.ts +78 -0
  126. package/dist/viewport.d.ts.map +1 -0
  127. package/dist/viewport.js +62 -0
  128. package/dist/viewport.js.map +1 -0
  129. package/package.json +54 -0
@@ -0,0 +1,105 @@
1
+ import { apply } from "./scene.js";
2
+ // --- Shapes ---
3
+ export const addElement = (scene, shape) => {
4
+ if (scene.elements.has(shape.id)) {
5
+ throw new Error(`Element already exists: ${shape.id}`);
6
+ }
7
+ const patch = { kind: "element", id: shape.id, before: null, after: shape };
8
+ return { scene: apply(scene, patch), patch };
9
+ };
10
+ export const removeElement = (scene, id) => {
11
+ const before = scene.elements.get(id);
12
+ if (!before)
13
+ throw new Error(`Element not found: ${id}`);
14
+ const patch = { kind: "element", id, before, after: null };
15
+ return { scene: apply(scene, patch), patch };
16
+ };
17
+ export const updateElement = (scene, id, update) => {
18
+ const before = scene.elements.get(id);
19
+ if (!before)
20
+ throw new Error(`Element not found: ${id}`);
21
+ const after = update(before);
22
+ const patch = { kind: "element", id, before, after };
23
+ return { scene: apply(scene, patch), patch };
24
+ };
25
+ export const moveElement = (scene, id, to) => updateElement(scene, id, (s) => ({ ...s, position: to }));
26
+ // --- Links ---
27
+ export const addLink = (scene, edge) => {
28
+ if (scene.links.has(edge.id)) {
29
+ throw new Error(`Link already exists: ${edge.id}`);
30
+ }
31
+ const patch = { kind: "link", id: edge.id, before: null, after: edge };
32
+ return { scene: apply(scene, patch), patch };
33
+ };
34
+ export const removeLink = (scene, id) => {
35
+ const before = scene.links.get(id);
36
+ if (!before)
37
+ throw new Error(`Link not found: ${id}`);
38
+ const patch = { kind: "link", id, before, after: null };
39
+ return { scene: apply(scene, patch), patch };
40
+ };
41
+ export const updateLink = (scene, id, update) => {
42
+ const before = scene.links.get(id);
43
+ if (!before)
44
+ throw new Error(`Link not found: ${id}`);
45
+ const after = update(before);
46
+ const patch = { kind: "link", id, before, after };
47
+ return { scene: apply(scene, patch), patch };
48
+ };
49
+ // --- Layers ---
50
+ export const addLayer = (scene, layer) => {
51
+ if (scene.layers.has(layer.id)) {
52
+ throw new Error(`Layer already exists: ${layer.id}`);
53
+ }
54
+ const patch = { kind: "layer", id: layer.id, before: null, after: layer };
55
+ return { scene: apply(scene, patch), patch };
56
+ };
57
+ export const removeLayer = (scene, id) => {
58
+ const before = scene.layers.get(id);
59
+ if (!before)
60
+ throw new Error(`Layer not found: ${id}`);
61
+ const patch = { kind: "layer", id, before, after: null };
62
+ return { scene: apply(scene, patch), patch };
63
+ };
64
+ export const updateLayer = (scene, id, update) => {
65
+ const before = scene.layers.get(id);
66
+ if (!before)
67
+ throw new Error(`Layer not found: ${id}`);
68
+ const after = update(before);
69
+ const patch = { kind: "layer", id, before, after };
70
+ return { scene: apply(scene, patch), patch };
71
+ };
72
+ // --- Annotations ---
73
+ export const addAnnotation = (scene, annotation) => {
74
+ if (scene.annotations.has(annotation.id)) {
75
+ throw new Error(`Annotation already exists: ${annotation.id}`);
76
+ }
77
+ const patch = {
78
+ kind: "annotation",
79
+ id: annotation.id,
80
+ before: null,
81
+ after: annotation,
82
+ };
83
+ return { scene: apply(scene, patch), patch };
84
+ };
85
+ export const removeAnnotation = (scene, id) => {
86
+ const before = scene.annotations.get(id);
87
+ if (!before)
88
+ throw new Error(`Annotation not found: ${id}`);
89
+ const patch = { kind: "annotation", id, before, after: null };
90
+ return { scene: apply(scene, patch), patch };
91
+ };
92
+ export const updateAnnotation = (scene, id, update) => {
93
+ const before = scene.annotations.get(id);
94
+ if (!before)
95
+ throw new Error(`Annotation not found: ${id}`);
96
+ const after = update(before);
97
+ const patch = { kind: "annotation", id, before, after };
98
+ return { scene: apply(scene, patch), patch };
99
+ };
100
+ // --- Viewport ---
101
+ export const setViewport = (scene, viewport) => {
102
+ const patch = { kind: "viewport", before: scene.viewport, after: viewport };
103
+ return { scene: apply(scene, patch), patch };
104
+ };
105
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Vec2 } from "@oh-just-another/types";
2
+ import { type ElementBase } from "./shape.js";
3
+ /**
4
+ * Built-in outline samplers — one per shape `type` that the kernel ships.
5
+ * Plugins can register their own via `registerOutlineSampler` so custom
6
+ * shapes can participate in outline snap + outline-bound edge endpoints.
7
+ *
8
+ * The samplers return points in *local* coordinates; world transform is
9
+ * applied by `getOutlinePoint` / `findNearestOutlinePoint` below.
10
+ */
11
+ export type OutlineSampler<S extends ElementBase = ElementBase> = (shape: S, ratio: number) => Vec2;
12
+ export declare const registerOutlineSampler: <S extends ElementBase>(type: S["type"], sampler: OutlineSampler<S>) => void;
13
+ export declare const getOutlineSampler: (type: string) => OutlineSampler | undefined;
14
+ /**
15
+ * Resolve an outline ratio (0..1, clockwise from top-left) to a *world*
16
+ * point on `shape`. Applies the shape's translation / rotation / scale.
17
+ *
18
+ * Throws when the shape's type has no registered outline sampler.
19
+ */
20
+ export declare const getOutlinePoint: (shape: ElementBase, ratio: number) => Vec2;
21
+ /**
22
+ * Find the point on the shape's outline closest to `worldPoint`. Returns
23
+ * the ratio (for persistence as an `LinkEndpoint.outline`), the resolved
24
+ * world position, and the distance — so callers can decide whether to use
25
+ * the snap (e.g. only if it's within their threshold).
26
+ *
27
+ * Samples the outline at a fixed density (`samples`, default 64). The
28
+ * default density is good enough for visual snap; bump it if you need
29
+ * sub-pixel accuracy at high zoom.
30
+ */
31
+ export declare const findNearestOutlinePoint: (shape: ElementBase, worldPoint: Vec2, samples?: number) => {
32
+ ratio: number;
33
+ world: Vec2;
34
+ distance: number;
35
+ } | null;
36
+ //# sourceMappingURL=outline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.d.ts","sourceRoot":"","sources":["../src/outline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAyB,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AAO1F;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAIpG,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,WAAW,EAC1D,MAAM,CAAC,CAAC,MAAM,CAAC,EACf,SAAS,cAAc,CAAC,CAAC,CAAC,KACzB,IAEF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,cAAc,GAAG,SACvC,CAAC;AAE5B;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,WAAW,EAAE,OAAO,MAAM,KAAG,IAOnE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,WAAW,EAClB,YAAY,IAAI,EAChB,gBAAY,KACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAiBrD,CAAC"}
@@ -0,0 +1,129 @@
1
+ import { getElementLocalBounds } from "./shape.js";
2
+ const req = (v) => {
3
+ if (v === undefined)
4
+ throw new Error("packages/scene: index out of range");
5
+ return v;
6
+ };
7
+ const outlineSamplers = new Map();
8
+ export const registerOutlineSampler = (type, sampler) => {
9
+ outlineSamplers.set(type, sampler);
10
+ };
11
+ export const getOutlineSampler = (type) => outlineSamplers.get(type);
12
+ /**
13
+ * Resolve an outline ratio (0..1, clockwise from top-left) to a *world*
14
+ * point on `shape`. Applies the shape's translation / rotation / scale.
15
+ *
16
+ * Throws when the shape's type has no registered outline sampler.
17
+ */
18
+ export const getOutlinePoint = (shape, ratio) => {
19
+ const sampler = outlineSamplers.get(shape.type);
20
+ if (!sampler) {
21
+ throw new Error(`No outline sampler registered for shape type: ${shape.type}`);
22
+ }
23
+ const local = sampler(shape, clamp01(ratio));
24
+ return localToWorld(shape, local);
25
+ };
26
+ /**
27
+ * Find the point on the shape's outline closest to `worldPoint`. Returns
28
+ * the ratio (for persistence as an `LinkEndpoint.outline`), the resolved
29
+ * world position, and the distance — so callers can decide whether to use
30
+ * the snap (e.g. only if it's within their threshold).
31
+ *
32
+ * Samples the outline at a fixed density (`samples`, default 64). The
33
+ * default density is good enough for visual snap; bump it if you need
34
+ * sub-pixel accuracy at high zoom.
35
+ */
36
+ export const findNearestOutlinePoint = (shape, worldPoint, samples = 64) => {
37
+ const sampler = outlineSamplers.get(shape.type);
38
+ if (!sampler)
39
+ return null;
40
+ let bestRatio = 0;
41
+ let bestPoint = localToWorld(shape, sampler(shape, 0));
42
+ let bestDistance = distance(worldPoint, bestPoint);
43
+ for (let i = 1; i < samples; i++) {
44
+ const ratio = i / samples;
45
+ const world = localToWorld(shape, sampler(shape, ratio));
46
+ const d = distance(worldPoint, world);
47
+ if (d < bestDistance) {
48
+ bestDistance = d;
49
+ bestRatio = ratio;
50
+ bestPoint = world;
51
+ }
52
+ }
53
+ return { ratio: bestRatio, world: bestPoint, distance: bestDistance };
54
+ };
55
+ // --- helpers ---
56
+ const clamp01 = (n) => (n < 0 ? 0 : n > 1 ? 1 : n);
57
+ const distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
58
+ const localToWorld = (shape, local) => {
59
+ const sx = local.x * shape.scale.x;
60
+ const sy = local.y * shape.scale.y;
61
+ const cos = Math.cos(shape.rotation);
62
+ const sin = Math.sin(shape.rotation);
63
+ return {
64
+ x: shape.position.x + (sx * cos - sy * sin),
65
+ y: shape.position.y + (sx * sin + sy * cos),
66
+ };
67
+ };
68
+ /**
69
+ * Sample a closed polyline (sequence of points, implicit wrap-around) at
70
+ * `ratio` along its total perimeter. Used by rectangle / polygon
71
+ * samplers — both reduce to "trace these edges in order".
72
+ */
73
+ const samplePolyline = (points, ratio) => {
74
+ if (points.length === 0)
75
+ return { x: 0, y: 0 };
76
+ if (points.length === 1)
77
+ return req(points[0]);
78
+ // Walk segments by cumulative length until we land on `ratio * total`.
79
+ let total = 0;
80
+ const lengths = [];
81
+ for (let i = 0; i < points.length; i++) {
82
+ const a = req(points[i]);
83
+ const b = req(points[(i + 1) % points.length]);
84
+ const len = distance(a, b);
85
+ lengths.push(len);
86
+ total += len;
87
+ }
88
+ if (total === 0)
89
+ return req(points[0]);
90
+ let target = total * ratio;
91
+ for (let i = 0; i < points.length; i++) {
92
+ const len = req(lengths[i]);
93
+ if (target <= len) {
94
+ const a = req(points[i]);
95
+ const b = req(points[(i + 1) % points.length]);
96
+ const t = len === 0 ? 0 : target / len;
97
+ return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
98
+ }
99
+ target -= len;
100
+ }
101
+ return req(points[0]);
102
+ };
103
+ // --- built-in samplers ---
104
+ registerOutlineSampler("rectangle", (shape, ratio) => {
105
+ const b = getElementLocalBounds(shape);
106
+ const corners = [
107
+ { x: b.x, y: b.y },
108
+ { x: b.x + b.width, y: b.y },
109
+ { x: b.x + b.width, y: b.y + b.height },
110
+ { x: b.x, y: b.y + b.height },
111
+ ];
112
+ return samplePolyline(corners, ratio);
113
+ });
114
+ registerOutlineSampler("ellipse", (shape, ratio) => {
115
+ // Parameterised by angle from positive-x (3 o'clock), but visually the
116
+ // user expects ratio 0 at the top-left "corner" of the bounding box —
117
+ // i.e. ratio 0 == top of the ellipse (12 o'clock). Offset accordingly.
118
+ const b = getElementLocalBounds(shape);
119
+ const cx = b.x + b.width / 2;
120
+ const cy = b.y + b.height / 2;
121
+ const rx = b.width / 2;
122
+ const ry = b.height / 2;
123
+ const angle = ratio * Math.PI * 2 - Math.PI / 2; // start at the top
124
+ return { x: cx + rx * Math.cos(angle), y: cy + ry * Math.sin(angle) };
125
+ });
126
+ registerOutlineSampler("polygon", (shape, ratio) => {
127
+ return samplePolyline(shape.points, ratio);
128
+ });
129
+ //# sourceMappingURL=outline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.js","sourceRoot":"","sources":["../src/outline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { AnnotationId, LinkId, FileId, LayerId, ElementId } from "@oh-just-another/types";
2
+ import type { Annotation } from "./annotation.js";
3
+ import type { Link } from "./edge.js";
4
+ import type { BinaryFile } from "./file.js";
5
+ import type { Layer } from "./layer.js";
6
+ import type { Element } from "./shape.js";
7
+ import type { Viewport } from "./viewport.js";
8
+ /**
9
+ * Unified patch model: each entity update carries `before` and `after`.
10
+ * `before === null && after !== null` → add
11
+ * `before !== null && after === null` → remove
12
+ * `before !== null && after !== null` → update (or move)
13
+ *
14
+ * `before` is stored so `invert` is a pure swap with no need to read the
15
+ * surrounding scene. This is what makes history and offline CRDT replay
16
+ * symmetric and cheap.
17
+ */
18
+ export type Patch = {
19
+ readonly kind: "element";
20
+ readonly id: ElementId;
21
+ readonly before: Element | null;
22
+ readonly after: Element | null;
23
+ } | {
24
+ readonly kind: "link";
25
+ readonly id: LinkId;
26
+ readonly before: Link | null;
27
+ readonly after: Link | null;
28
+ } | {
29
+ readonly kind: "layer";
30
+ readonly id: LayerId;
31
+ readonly before: Layer | null;
32
+ readonly after: Layer | null;
33
+ } | {
34
+ readonly kind: "annotation";
35
+ readonly id: AnnotationId;
36
+ readonly before: Annotation | null;
37
+ readonly after: Annotation | null;
38
+ } | {
39
+ readonly kind: "viewport";
40
+ readonly before: Viewport;
41
+ readonly after: Viewport;
42
+ } | {
43
+ readonly kind: "file";
44
+ readonly id: FileId;
45
+ readonly before: BinaryFile | null;
46
+ readonly after: BinaryFile | null;
47
+ } | {
48
+ readonly kind: "batch";
49
+ readonly patches: readonly Patch[];
50
+ };
51
+ /** Swap `before` and `after` recursively. */
52
+ export declare const invert: (patch: Patch) => Patch;
53
+ /** Compose patches into a single atomic unit. Flattens nested batches. */
54
+ export declare const batch: (patches: readonly Patch[]) => Patch;
55
+ /** True when applying the patch would not change the scene. */
56
+ export declare const isNoop: (patch: Patch) => boolean;
57
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,GACb;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAChC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACnC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAClF;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACnC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAA;CAAE,CAAC;AAEnE,6CAA6C;AAC7C,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,KAkBrC,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,KAAK,GAAI,SAAS,SAAS,KAAK,EAAE,KAAG,KAOjD,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,OAarC,CAAC"}
package/dist/patch.js ADDED
@@ -0,0 +1,47 @@
1
+ /** Swap `before` and `after` recursively. */
2
+ export const invert = (patch) => {
3
+ switch (patch.kind) {
4
+ case "element":
5
+ return { kind: "element", id: patch.id, before: patch.after, after: patch.before };
6
+ case "link":
7
+ return { kind: "link", id: patch.id, before: patch.after, after: patch.before };
8
+ case "layer":
9
+ return { kind: "layer", id: patch.id, before: patch.after, after: patch.before };
10
+ case "annotation":
11
+ return { kind: "annotation", id: patch.id, before: patch.after, after: patch.before };
12
+ case "viewport":
13
+ return { kind: "viewport", before: patch.after, after: patch.before };
14
+ case "file":
15
+ return { kind: "file", id: patch.id, before: patch.after, after: patch.before };
16
+ case "batch":
17
+ // Reverse order so undoing a batch unwinds it in LIFO.
18
+ return { kind: "batch", patches: patch.patches.map(invert).reverse() };
19
+ }
20
+ };
21
+ /** Compose patches into a single atomic unit. Flattens nested batches. */
22
+ export const batch = (patches) => {
23
+ const flat = [];
24
+ for (const p of patches) {
25
+ if (p.kind === "batch")
26
+ flat.push(...p.patches);
27
+ else
28
+ flat.push(p);
29
+ }
30
+ return { kind: "batch", patches: flat };
31
+ };
32
+ /** True when applying the patch would not change the scene. */
33
+ export const isNoop = (patch) => {
34
+ switch (patch.kind) {
35
+ case "element":
36
+ case "link":
37
+ case "layer":
38
+ case "annotation":
39
+ case "file":
40
+ return patch.before === patch.after;
41
+ case "viewport":
42
+ return patch.before === patch.after;
43
+ case "batch":
44
+ return patch.patches.length === 0 || patch.patches.every(isNoop);
45
+ }
46
+ };
47
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAoDA,6CAA6C;AAC7C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAY,EAAS,EAAE;IAC5C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACrF,KAAK,MAAM;YACT,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAClF,KAAK,OAAO;YACV,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACnF,KAAK,YAAY;YACf,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACxF,KAAK,UAAU;YACb,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACxE,KAAK,MAAM;YACT,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAClF,KAAK,OAAO;YACV,uDAAuD;YACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;IAC3E,CAAC;AACH,CAAC,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAAyB,EAAS,EAAE;IACxD,MAAM,IAAI,GAAY,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;;YAC3C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAY,EAAW,EAAE;IAC9C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,YAAY,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK,CAAC;QACtC,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK,CAAC;QACtC,KAAK,OAAO;YACV,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrE,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,115 @@
1
+ import type { Bounds, LinkId, LayerId, ElementId, Vec2 } from "@oh-just-another/types";
2
+ import type { Link } from "./edge.js";
3
+ import type { Layer } from "./layer.js";
4
+ import type { Scene } from "./scene.js";
5
+ import { type Element } from "./shape.js";
6
+ import { SpatialGrid } from "./spatial.js";
7
+ export declare const getElement: (scene: Scene, id: ElementId) => Element | undefined;
8
+ export declare const getLink: (scene: Scene, id: LinkId) => Link | undefined;
9
+ export declare const getLayer: (scene: Scene, id: LayerId) => Layer | undefined;
10
+ /**
11
+ * Layers sorted bottom-to-top by their `order` field. Stable for equal orders
12
+ * (which should not happen in practice with fractional indices).
13
+ */
14
+ export declare const getLayersInOrder: (scene: Scene) => readonly Layer[];
15
+ /** Shapes in `layerId`, sorted bottom-to-top by `order`. */
16
+ export declare const getElementsInLayer: (scene: Scene, layerId: LayerId) => readonly Element[];
17
+ export declare const getLinksInLayer: (scene: Scene, layerId: LayerId) => readonly Link[];
18
+ /**
19
+ * Outline provider for a custom / composite element type — returns the
20
+ * shape's contour as one or more LOCAL-space loops (pre transform). Lets a
21
+ * plugin element made of several visually-disconnected figures (e.g. two
22
+ * unconnected ellipses, no background) supply a multi-loop selection halo
23
+ * instead of falling back to its bounding box. Registered by `shape.type`.
24
+ */
25
+ export type ElementOutlineProvider = (shape: Element) => Vec2[][];
26
+ /** Register a multi-loop outline provider for a custom element `type`. */
27
+ export declare const registerElementOutline: (type: string, provider: ElementOutlineProvider) => void;
28
+ /**
29
+ * World-space outline loop(s) tracing a shape's actual contour, for the
30
+ * selection halo. polygon (star / diamond / hexagon) is exact; ellipse and
31
+ * path are sampled; a group returns one loop per descendant (handles
32
+ * visually-disconnected figures). Shapes without known geometry (composite
33
+ * template, brush, custom) fall back to their world bounding box. Cheap
34
+ * enough to recompute every frame — no baking needed.
35
+ */
36
+ export declare const getElementOutline: (scene: Scene, shape: Element) => Vec2[][];
37
+ /**
38
+ * Direct children of `parentId` — every shape whose `parentId` equals
39
+ * the argument, in z-order. Linear in scene size; for groups inside the
40
+ * editor's hot path, cache the result by `(scene, parentId)`.
41
+ */
42
+ export declare const getChildrenOf: (scene: Scene, parentId: ElementId) => readonly Element[];
43
+ /**
44
+ * `true` when the shape (or any of its ancestors via `parentId`) has
45
+ * `locked: true`. Walks the parent chain bounded by
46
+ * `MAX_PARENT_DEPTH` so the answer stays O(depth) for a freshly
47
+ * grouped scene. Independent from `Layer.locked` — callers that need
48
+ * the combined interactivity gate should `||` both flags.
49
+ */
50
+ export declare const isElementLocked: (scene: Scene, shape: Element) => boolean;
51
+ /**
52
+ * `true` when the shape (or any of its ancestors via `parentId`) has
53
+ * `hidden: true`. Same propagation semantics as `isElementLocked`.
54
+ */
55
+ export declare const isElementHidden: (scene: Scene, shape: Element) => boolean;
56
+ /**
57
+ * Walks the `parentId` chain starting from `elementId` and returns the
58
+ * topmost ancestor (the root). Returns the shape itself when it has no
59
+ * parent, or `undefined` when the shape (or any ancestor) is missing.
60
+ * Cycle-safe — bails after `MAX_PARENT_DEPTH` hops.
61
+ */
62
+ export declare const getRootSelf: (scene: Scene, elementId: ElementId) => Element | undefined;
63
+ /**
64
+ * Every descendant of `parentId`, recursive, including the root itself.
65
+ * Order: parent first, then a depth-first walk. Cycle-safe via the
66
+ * `visited` set.
67
+ */
68
+ export declare const getDescendantsOf: (scene: Scene, parentId: ElementId) => readonly Element[];
69
+ /**
70
+ * Shapes whose world AABB intersects `range`. Linear in the number of shapes.
71
+ * For large scenes use `buildSpatialIndex` once and query the index.
72
+ */
73
+ export declare const getElementsInBounds: (scene: Scene, range: Bounds) => readonly Element[];
74
+ /**
75
+ * Shapes whose world-AABB is at least `minCoverageRatio` covered by
76
+ * `range`. `1` requires full containment (containment-style lasso);
77
+ * `0.5` selects when at least half of the element sits inside the
78
+ * box — friendlier than pure intersection because brushing past an
79
+ * edge doesn't accidentally grab the shape.
80
+ *
81
+ * Always selects shapes that fully contain the lasso (small lasso
82
+ * inside a big shape) — same affordance as bidirectional containment.
83
+ * Zero-area shapes (groups, brushes-with-one-vertex) fall back to a
84
+ * plain intersection test.
85
+ */
86
+ export declare const getElementsCoveredByBounds: (scene: Scene, range: Bounds, minCoverageRatio?: number) => readonly Element[];
87
+ /**
88
+ * Topmost shape containing `point`. Iterates layers top-to-bottom, then shapes
89
+ * within each layer top-to-bottom; returns the first hit. Hit-test here is the
90
+ * conservative AABB test; renderer-specific shape-precise hit-tests belong
91
+ * with the renderer.
92
+ */
93
+ export declare const getElementAt: (scene: Scene, point: Vec2) => Element | undefined;
94
+ /**
95
+ * Build a `SpatialGrid` from the current scene. Re-build (or update
96
+ * incrementally) when shapes change — the grid is not auto-synced with the
97
+ * scene. The default cell size is tuned for typical editor scenes; pass an
98
+ * explicit value if your shapes are much larger or smaller.
99
+ */
100
+ export declare const buildSpatialIndex: (scene: Scene, cellSize?: number) => SpatialGrid;
101
+ /**
102
+ * Range query backed by the index. Returns shapes (not just ids) whose AABB
103
+ * actually intersects `range`. The grid pre-filters by cell overlap; this
104
+ * function does the precise AABB filter.
105
+ */
106
+ export declare const queryByIndex: (scene: Scene, grid: SpatialGrid, range: Bounds) => readonly Element[];
107
+ /**
108
+ * Point hit-test backed by a SpatialGrid. Equivalent to `getElementAt` but
109
+ * pre-filters candidates through `grid.query` — O(k) where k is the
110
+ * shapes overlapping the point's cell. Walks layers top-to-bottom for
111
+ * stable z-order; within a layer picks the highest-`order` shape that
112
+ * actually contains the point.
113
+ */
114
+ export declare const getElementAtIndexed: (scene: Scene, grid: SpatialGrid, point: Vec2) => Element | undefined;
115
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}