@expofp/renderer 3.2.5 → 3.27.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 (228) hide show
  1. package/README.md +111 -0
  2. package/dist/Renderer.d.ts +88 -0
  3. package/dist/Renderer.js +53 -0
  4. package/dist/controls/CameraControls.d.ts +102 -0
  5. package/dist/controls/CameraControls.js +239 -0
  6. package/dist/controls/camera-api.d.ts +206 -0
  7. package/dist/controls/camera-api.js +181 -0
  8. package/dist/controls/camera-move.d.ts +13 -0
  9. package/dist/controls/camera-move.js +18 -0
  10. package/dist/controls/camera-state.d.ts +40 -0
  11. package/dist/controls/camera-state.js +65 -0
  12. package/dist/controls/gestures.d.ts +70 -0
  13. package/dist/controls/gestures.js +84 -0
  14. package/dist/controls/handlers/Inertia.d.ts +111 -0
  15. package/dist/controls/handlers/Inertia.js +215 -0
  16. package/dist/controls/handlers/Pan.d.ts +23 -0
  17. package/dist/controls/handlers/Pan.js +32 -0
  18. package/dist/controls/handlers/Pitch.d.ts +94 -0
  19. package/dist/controls/handlers/Pitch.js +191 -0
  20. package/dist/controls/handlers/Roll.d.ts +103 -0
  21. package/dist/controls/handlers/Roll.js +182 -0
  22. package/dist/controls/handlers/Zoom.d.ts +15 -0
  23. package/dist/controls/handlers/Zoom.js +26 -0
  24. package/dist/controls/index.d.ts +56 -0
  25. package/dist/controls/index.js +58 -0
  26. package/dist/controls/zoom-to.d.ts +89 -0
  27. package/dist/controls/zoom-to.js +91 -0
  28. package/dist/core/InstanceUniforms.d.ts +93 -0
  29. package/dist/core/InstanceUniforms.js +266 -0
  30. package/dist/core/SquareDataTexture.d.ts +277 -0
  31. package/dist/core/SquareDataTexture.js +476 -0
  32. package/dist/core/batch/BatchedMesh.d.ts +74 -0
  33. package/dist/core/batch/BatchedMesh.js +149 -0
  34. package/dist/core/instanced/FrustumCulling.d.ts +141 -0
  35. package/dist/core/instanced/FrustumCulling.js +246 -0
  36. package/dist/core/instanced/InstancedMesh.d.ts +302 -0
  37. package/dist/core/instanced/InstancedMesh.js +497 -0
  38. package/dist/core/instanced/Instances.d.ts +119 -0
  39. package/dist/core/instanced/Instances.js +230 -0
  40. package/dist/core/instanced/Raycasting.d.ts +118 -0
  41. package/dist/core/instanced/Raycasting.js +134 -0
  42. package/dist/core/instanced/instanced-material.d.ts +77 -0
  43. package/dist/core/instanced/instanced-material.js +224 -0
  44. package/dist/core/lines/BatchedLines.d.ts +135 -0
  45. package/dist/core/lines/BatchedLines.js +213 -0
  46. package/dist/core/lines/FrustumCulling.d.ts +43 -0
  47. package/dist/core/lines/FrustumCulling.js +85 -0
  48. package/dist/core/lines/Raycasting.d.ts +44 -0
  49. package/dist/core/lines/Raycasting.js +91 -0
  50. package/dist/core/lines/lines-material.d.ts +47 -0
  51. package/dist/core/lines/lines-material.js +115 -0
  52. package/dist/core/shader-source.d.ts +50 -0
  53. package/dist/core/shader-source.js +84 -0
  54. package/dist/core/text/BatchedText.d.ts +227 -0
  55. package/dist/core/text/BatchedText.js +467 -0
  56. package/dist/core/text/MemberTexture.d.ts +90 -0
  57. package/dist/core/text/MemberTexture.js +194 -0
  58. package/dist/core/text/Text.d.ts +73 -0
  59. package/dist/core/text/Text.js +215 -0
  60. package/dist/core/text/batched-text-material.d.ts +56 -0
  61. package/dist/core/text/batched-text-material.js +121 -0
  62. package/dist/core/text/glyph-packing.d.ts +17 -0
  63. package/dist/core/text/glyph-packing.js +61 -0
  64. package/dist/core/typed-array.d.ts +16 -0
  65. package/dist/core/typed-array.js +20 -0
  66. package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
  67. package/dist/debug/components/DebugPanelProvider.js +38 -0
  68. package/dist/debug/components/DebugTools.d.ts +22 -0
  69. package/dist/debug/components/DebugTools.js +28 -0
  70. package/dist/debug/components/Gui.d.ts +20 -0
  71. package/dist/debug/components/Gui.js +32 -0
  72. package/dist/debug/components/MemoryInfo.d.ts +22 -0
  73. package/dist/debug/components/MemoryInfo.js +42 -0
  74. package/dist/debug/components/Panel.d.ts +31 -0
  75. package/dist/debug/components/Panel.js +98 -0
  76. package/dist/debug/components/Stats.d.ts +9 -0
  77. package/dist/debug/components/Stats.js +24 -0
  78. package/dist/debug/debug-context.d.ts +41 -0
  79. package/dist/debug/debug-context.js +59 -0
  80. package/dist/debug/index.d.ts +51 -0
  81. package/dist/debug/index.js +64 -0
  82. package/dist/events/LayoutDriver.d.ts +31 -0
  83. package/dist/events/LayoutDriver.js +88 -0
  84. package/dist/events/PointerMissed.d.ts +23 -0
  85. package/dist/events/PointerMissed.js +58 -0
  86. package/dist/events/def-culling.d.ts +46 -0
  87. package/dist/events/def-culling.js +69 -0
  88. package/dist/events/layout-targets.d.ts +104 -0
  89. package/dist/events/layout-targets.js +245 -0
  90. package/dist/events/pointer.d.ts +118 -0
  91. package/dist/events/pointer.js +168 -0
  92. package/dist/external/ExternalCanvas.d.ts +56 -0
  93. package/dist/external/ExternalCanvas.js +178 -0
  94. package/dist/external/external-camera.d.ts +17 -0
  95. package/dist/external/external-camera.js +82 -0
  96. package/dist/external/external-events.d.ts +14 -0
  97. package/dist/external/external-events.js +33 -0
  98. package/dist/index.d.ts +11 -783
  99. package/dist/index.js +12 -8031
  100. package/dist/objects/Background.d.ts +31 -0
  101. package/dist/objects/Background.js +37 -0
  102. package/dist/objects/Lighting.d.ts +20 -0
  103. package/dist/objects/Lighting.js +85 -0
  104. package/dist/objects/containers/SlotContainer.d.ts +266 -0
  105. package/dist/objects/containers/SlotContainer.js +251 -0
  106. package/dist/objects/containers/hooks.d.ts +46 -0
  107. package/dist/objects/containers/hooks.js +118 -0
  108. package/dist/objects/containers/index.d.ts +6 -0
  109. package/dist/objects/containers/index.js +4 -0
  110. package/dist/objects/containers/keys.d.ts +10 -0
  111. package/dist/objects/containers/keys.js +18 -0
  112. package/dist/objects/containers/mesh-writes.d.ts +106 -0
  113. package/dist/objects/containers/mesh-writes.js +97 -0
  114. package/dist/objects/containers/props.d.ts +23 -0
  115. package/dist/objects/containers/props.js +1 -0
  116. package/dist/objects/images/ImageInstance.d.ts +29 -0
  117. package/dist/objects/images/ImageInstance.js +83 -0
  118. package/dist/objects/images/Images.d.ts +37 -0
  119. package/dist/objects/images/Images.js +204 -0
  120. package/dist/objects/images/atlas-packer.d.ts +128 -0
  121. package/dist/objects/images/atlas-packer.js +315 -0
  122. package/dist/objects/images/image-container.d.ts +51 -0
  123. package/dist/objects/images/image-container.js +70 -0
  124. package/dist/objects/images/index.d.ts +2 -0
  125. package/dist/objects/images/index.js +1 -0
  126. package/dist/objects/images/texture-budget.d.ts +120 -0
  127. package/dist/objects/images/texture-budget.js +215 -0
  128. package/dist/objects/layer/Layer.d.ts +27 -0
  129. package/dist/objects/layer/Layer.js +114 -0
  130. package/dist/objects/layer/index.d.ts +2 -0
  131. package/dist/objects/layer/index.js +1 -0
  132. package/dist/objects/layer/mount-scheduler.d.ts +102 -0
  133. package/dist/objects/layer/mount-scheduler.js +145 -0
  134. package/dist/objects/layer/visibility-gui.d.ts +19 -0
  135. package/dist/objects/layer/visibility-gui.js +93 -0
  136. package/dist/objects/lines/LineInstance.d.ts +21 -0
  137. package/dist/objects/lines/LineInstance.js +73 -0
  138. package/dist/objects/lines/Lines.d.ts +17 -0
  139. package/dist/objects/lines/Lines.js +29 -0
  140. package/dist/objects/lines/index.d.ts +2 -0
  141. package/dist/objects/lines/index.js +1 -0
  142. package/dist/objects/lines/line-container.d.ts +35 -0
  143. package/dist/objects/lines/line-container.js +40 -0
  144. package/dist/objects/materials/ColorMaterial.d.ts +85 -0
  145. package/dist/objects/materials/ColorMaterial.js +89 -0
  146. package/dist/objects/materials/LineMaterial.d.ts +44 -0
  147. package/dist/objects/materials/LineMaterial.js +72 -0
  148. package/dist/objects/materials/ModelMaterial.d.ts +41 -0
  149. package/dist/objects/materials/ModelMaterial.js +84 -0
  150. package/dist/objects/materials/TextureMaterial.d.ts +30 -0
  151. package/dist/objects/materials/TextureMaterial.js +29 -0
  152. package/dist/objects/materials/dim.d.ts +123 -0
  153. package/dist/objects/materials/dim.js +213 -0
  154. package/dist/objects/materials/shared.d.ts +6 -0
  155. package/dist/objects/materials/shared.js +51 -0
  156. package/dist/objects/models/ModelInstance.d.ts +22 -0
  157. package/dist/objects/models/ModelInstance.js +33 -0
  158. package/dist/objects/models/Models.d.ts +22 -0
  159. package/dist/objects/models/Models.js +177 -0
  160. package/dist/objects/models/gltf-cache.d.ts +17 -0
  161. package/dist/objects/models/gltf-cache.js +50 -0
  162. package/dist/objects/models/index.d.ts +2 -0
  163. package/dist/objects/models/index.js +1 -0
  164. package/dist/objects/models/model-asset.d.ts +48 -0
  165. package/dist/objects/models/model-asset.js +77 -0
  166. package/dist/objects/models/model-container.d.ts +139 -0
  167. package/dist/objects/models/model-container.js +227 -0
  168. package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
  169. package/dist/objects/shapes/ShapeInstance.js +40 -0
  170. package/dist/objects/shapes/Shapes.d.ts +21 -0
  171. package/dist/objects/shapes/Shapes.js +120 -0
  172. package/dist/objects/shapes/index.d.ts +2 -0
  173. package/dist/objects/shapes/index.js +1 -0
  174. package/dist/objects/shapes/shape-container.d.ts +165 -0
  175. package/dist/objects/shapes/shape-container.js +272 -0
  176. package/dist/objects/shapes/shape-geometry.d.ts +27 -0
  177. package/dist/objects/shapes/shape-geometry.js +94 -0
  178. package/dist/objects/text/TextInstance.d.ts +22 -0
  179. package/dist/objects/text/TextInstance.js +95 -0
  180. package/dist/objects/text/Texts.d.ts +30 -0
  181. package/dist/objects/text/Texts.js +41 -0
  182. package/dist/objects/text/index.d.ts +2 -0
  183. package/dist/objects/text/index.js +1 -0
  184. package/dist/objects/text/text-container.d.ts +105 -0
  185. package/dist/objects/text/text-container.js +206 -0
  186. package/dist/objects/text/text-layout.d.ts +83 -0
  187. package/dist/objects/text/text-layout.js +154 -0
  188. package/dist/space/Camera.d.ts +103 -0
  189. package/dist/space/Camera.js +321 -0
  190. package/dist/space/Scene.d.ts +44 -0
  191. package/dist/space/Scene.js +112 -0
  192. package/dist/space/Space.d.ts +288 -0
  193. package/dist/space/Space.js +329 -0
  194. package/dist/space/VisibleRect.d.ts +72 -0
  195. package/dist/space/VisibleRect.js +129 -0
  196. package/dist/space/camera-handle.d.ts +67 -0
  197. package/dist/space/camera-handle.js +63 -0
  198. package/dist/space/camera-kind.d.ts +26 -0
  199. package/dist/space/camera-kind.js +30 -0
  200. package/dist/space/canvas-size.d.ts +24 -0
  201. package/dist/space/canvas-size.js +20 -0
  202. package/dist/space/clipping.d.ts +55 -0
  203. package/dist/space/clipping.js +108 -0
  204. package/dist/space/layout-measure.d.ts +65 -0
  205. package/dist/space/layout-measure.js +173 -0
  206. package/dist/space/viewport.d.ts +112 -0
  207. package/dist/space/viewport.js +67 -0
  208. package/dist/types/defs.d.ts +169 -0
  209. package/dist/types/defs.js +1 -0
  210. package/dist/types/events.d.ts +183 -0
  211. package/dist/types/events.js +1 -0
  212. package/dist/types/guards.d.ts +14 -0
  213. package/dist/types/guards.js +42 -0
  214. package/dist/update/index.d.ts +3 -0
  215. package/dist/update/index.js +2 -0
  216. package/dist/update/slot-writers.d.ts +30 -0
  217. package/dist/update/slot-writers.js +72 -0
  218. package/dist/update/store.d.ts +41 -0
  219. package/dist/update/store.js +98 -0
  220. package/dist/utils/arrays.d.ts +13 -0
  221. package/dist/utils/arrays.js +20 -0
  222. package/dist/utils/colors.d.ts +19 -0
  223. package/dist/utils/colors.js +27 -0
  224. package/dist/utils/logging.d.ts +26 -0
  225. package/dist/utils/logging.js +47 -0
  226. package/dist/utils/math.d.ts +24 -0
  227. package/dist/utils/math.js +27 -0
  228. package/package.json +67 -19
@@ -0,0 +1,183 @@
1
+ import { type PlanPoint } from '../space/Space';
2
+ import { type LayerDef, type RenderableDef } from './defs';
3
+ /**
4
+ * The def one of a layer's {@link PointerHandlers} is handed: a content layer's own child type, and —
5
+ * because a layer whose children are layers stands above every kind — any renderable at all for one
6
+ * of those. Never a {@link LayerDef} in practice: a hit resolves to the primitive under the pointer.
7
+ */
8
+ export type SubtreeDef<T extends RenderableDef> = T extends LayerDef ? RenderableDef : T;
9
+ /** A pointer event delivered to one of a layer's {@link PointerHandlers}. */
10
+ export interface PointerEventData<T extends RenderableDef = RenderableDef> {
11
+ /**
12
+ * The def under the pointer — the nearest hit inside the subtree of the layer whose handler is
13
+ * running. A hit the renderer cannot trace back to a def is dropped rather than reported, so this
14
+ * is never absent.
15
+ */
16
+ def: SubtreeDef<T>;
17
+ /**
18
+ * Where the pointer struck {@link def}, in plan units — the ray's own intersection with the
19
+ * geometry, taken back into plan space, so `z` is the elevation it was drawn at and a click on the
20
+ * roof of a raised model reports the roof.
21
+ *
22
+ * The same kind of answer {@link PointerMissedEventData} carries, which searches the scene for
23
+ * itself. A caller that wants the floor coordinate under an elevated hit instead — "which square
24
+ * metre of the hall is this" rather than "what did I touch" — converts the canvas position through
25
+ * `ViewportAPI.canvasToPlan`, which crosses the plan plane on purpose.
26
+ *
27
+ * A fresh instance per event, so keeping a reference to it is safe.
28
+ */
29
+ point: PlanPoint;
30
+ /** Keeps the event from reaching the ancestors of the layer whose handler is running. */
31
+ stopPropagation(): void;
32
+ /** The DOM event this was dispatched from. */
33
+ nativeEvent: MouseEvent;
34
+ }
35
+ /**
36
+ * A click that reached no layer handler — empty background, or a def whose layer declares no
37
+ * handlers and so is not picked at all. Delivered to the renderer's `onPointerMissed`.
38
+ */
39
+ export interface PointerMissedEventData {
40
+ /**
41
+ * Where the click landed, in plan units — the nearest geometry under the pointer, with the
42
+ * elevation it was struck at, exactly as {@link PointerEventData.point} reports for a hit.
43
+ *
44
+ * **Reaching no handler is not the same as striking nothing.** A layer declaring no pointer
45
+ * handlers is never raycast, so a click arrives here as readily from the middle of a wall as from
46
+ * open background. The scene is searched along the same ray to tell those apart, and only the case
47
+ * that really is empty — past the edge of every piece of geometry — falls back to where the ray
48
+ * crosses the plan plane (`z = 0`).
49
+ */
50
+ point: PlanPoint;
51
+ /** The DOM event this was dispatched from. */
52
+ nativeEvent: MouseEvent;
53
+ }
54
+ /**
55
+ * The pointer handlers a {@link LayerDef} may carry. Kept apart from {@link LayoutHandlers} so that
56
+ * `keyof PointerHandlers` is exactly the dispatchable names.
57
+ *
58
+ * **Declaring one is what makes a layer interactive.** A layer with none is never raycast; a layer
59
+ * with any is raycast together with its whole subtree; a layer that is off screen — hidden, or under
60
+ * something hidden — is not picked at all.
61
+ *
62
+ * **Events travel upward.** The nearest layer's handler runs first, then each ancestor that declares
63
+ * the same handler, until one calls {@link PointerEventData.stopPropagation}; `event.def` stays the
64
+ * def that was hit. Overlapping defs deliver one event apiece — even within one layer — in the order
65
+ * their slots were claimed (mount order), not paint order; `Pointer/Overlap` pins that.
66
+ *
67
+ * **Handlers return nothing.** One that mutates defs calls `update()` afterward, like any other code.
68
+ *
69
+ * **Handlers take no receiver.** They are read off the layer and called as plain functions —
70
+ * `this: void` makes reading `this` a compile error rather than an `undefined` at runtime — so a
71
+ * handler shared across layers keys off `event.def`. Declared as methods rather than function-typed
72
+ * fields on purpose: a method's parameters are checked bivariantly, which is what keeps a
73
+ * `LayerDef<ShapeDef>` assignable to a plain `RenderableDef`.
74
+ */
75
+ export interface PointerHandlers<T extends RenderableDef = RenderableDef> {
76
+ /**
77
+ * Called for a click on a def in this layer — a press and release that stayed within a few pixels,
78
+ * so a pan gesture ending over a def does not click it.
79
+ * @param event - The def that was clicked, and where.
80
+ */
81
+ onClick?(this: void, event: PointerEventData<T>): void;
82
+ /**
83
+ * Called while the pointer is over a def in this layer, on every move it stays over one, including
84
+ * the move that first entered it.
85
+ * @param event - The def under the pointer, and where.
86
+ */
87
+ onPointerMove?(this: void, event: PointerEventData<T>): void;
88
+ /**
89
+ * Called when the pointer leaves the def it was over, naming that def — so a handler mirroring
90
+ * hover state clears it here.
91
+ * @param event - The def the pointer left, and where the pointer was.
92
+ */
93
+ onPointerOut?(this: void, event: PointerEventData<T>): void;
94
+ }
95
+ /** The pointer handlers by name, so dispatch can name one without repeating the list. */
96
+ export type PointerHandlerName = keyof PointerHandlers;
97
+ /** A def's re-layout handler, named so a caller can annotate one apart from the def it serves. */
98
+ export type ScaleHandler = NonNullable<LayoutHandlers['onScale']>;
99
+ /** A def's counter-rotation handler, named like {@link ScaleHandler}. */
100
+ export type RollHandler = NonNullable<LayoutHandlers['onRoll']>;
101
+ /**
102
+ * What the camera currently shows, as a layout rule sees it — every {@link LayoutHandlers} handler
103
+ * receives the same view, whichever axis's move invoked it, so a rule that turns a label can re-fit
104
+ * it at the size it is drawn at without asking anyone.
105
+ *
106
+ * **Valid during the call.** The registry reuses one instance across a sweep, so destructure or
107
+ * copy what you need rather than retaining the object.
108
+ */
109
+ export interface LayoutView {
110
+ /**
111
+ * How much of the plan one device pixel spans, in plan units — the factor anything sized in
112
+ * pixels is laid out against, and the quantity the imperative renderer published as `ptScale`.
113
+ */
114
+ readonly pixelSize: number;
115
+ /**
116
+ * Which way the plan is turned, in **radians** clockwise from north — the same quantity, in the
117
+ * same unit, the imperative renderer's `navigation:roll` event carried, so ported rules keep the
118
+ * quarter-turn thresholds they were written against. (`CameraState.roll` reports the same angle
119
+ * in degrees, for people; rules get the unit their arithmetic wants.)
120
+ */
121
+ readonly roll: number;
122
+ }
123
+ /**
124
+ * The layout handlers a def may carry — camera-driven rules that re-derive a def's geometry, one
125
+ * per axis the camera moves on: `onScale` for the pixel size, `onRoll` for the turn. Both receive
126
+ * the same {@link LayoutView}.
127
+ *
128
+ * Where {@link PointerHandlers} hang off the layer that gets picked — picking is a query over a
129
+ * subtree — a layout handler hangs off the one def it lays out, a layer included (which is how a
130
+ * layer whose *membership* depends on the camera re-derives it), and never propagates: a def's
131
+ * geometry has exactly one owner.
132
+ *
133
+ * A handler takes no def parameter: it is typically a closure built where its def is built, closing
134
+ * over the def or the plan-lived object that owns it. **What such a closure captures is the one
135
+ * thing to watch.** Defs live for the plan's lifetime, so a handler must capture nothing
136
+ * shorter-lived than the def — an owner alive as long as the plan is safe, since the closure then
137
+ * retains nothing the def was not already keeping alive, while a build-time temporary (a parse
138
+ * buffer, a raw config) captured even incidentally would stay pinned in memory for as long as the
139
+ * def exists. Should defs ever stop being built by plan-lived owners, keep that property by moving
140
+ * per-def data onto the defs themselves and capturing only the def.
141
+ */
142
+ export interface LayoutHandlers {
143
+ /**
144
+ * Lays the def this handler is declared on out for a new pixel size — the hook everything sized
145
+ * in pixels rather than plan units hangs off: label font steps, badge and marker rescaling, a
146
+ * bookmark's clamp.
147
+ *
148
+ * Called once when the def is first registered, and again whenever the pixel size moves — hidden
149
+ * defs included, since whether a def draws at all can be exactly what its rule decides (a booth's
150
+ * dot appears only once the plan is far enough out for its label to stop fitting).
151
+ *
152
+ * **Returns what changed, and the renderer writes it** by the cheapest route that is correct for
153
+ * each def: a layer is reconciled, anything else goes straight into its slot with no render — what
154
+ * makes it affordable for every label on a plan to re-lay out while the camera moves. Return
155
+ * nothing when nothing changed and the write — the expensive half — is skipped; the def still
156
+ * counts as laid out either way. Do not call `update()` here: returning *is* this handler's
157
+ * update.
158
+ *
159
+ * Called as a plain function with no receiver, like the pointer handlers.
160
+ * @param view - What the camera shows; multiply pixel-authored sizes by `view.pixelSize`.
161
+ * @returns The defs that changed and need writing, or nothing.
162
+ */
163
+ onScale?(this: void, view: LayoutView): readonly RenderableDef[] | void;
164
+ /**
165
+ * Lays the def out for a new camera roll — the hook everything that stays upright, or snaps to the
166
+ * nearest cardinal direction, hangs off: label and bookmark counter-rotation, a badge walking its
167
+ * anchor corner, a marker turning with the plan.
168
+ *
169
+ * Called once when the def is first registered — so a def added while the plan is turned is
170
+ * correct before its first paint — and again whenever the roll moves at all. There is no epsilon:
171
+ * the rules themselves step in quarter turns, so nearly every call returns nothing, exactly as the
172
+ * imperative renderer's `navigation:roll` handlers did.
173
+ *
174
+ * The same write contract as {@link onScale}: return what changed, return nothing when nothing
175
+ * did, and never call `update()` here. Called as a plain function with no receiver.
176
+ * @param view - What the camera shows — `view.roll` is the angle to lay out against, and
177
+ * `view.pixelSize` rides along because a rule that turns a label re-fits it at the size it is
178
+ * currently drawn at.
179
+ * @returns The defs that changed and need writing, or nothing.
180
+ */
181
+ onRoll?(this: void, view: LayoutView): readonly RenderableDef[] | void;
182
+ }
183
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { type ImageDef, type LayerDef, type LineDef, type ModelDef, type RenderableDef, type RenderableDefCollection, type ShapeDef, type TextDef } from './defs';
2
+ export declare function isShapeDef(def: RenderableDef): def is ShapeDef;
3
+ export declare function isImageDef(def: RenderableDef): def is ImageDef;
4
+ export declare function isTextDef(def: RenderableDef): def is TextDef;
5
+ export declare function isLineDef(def: RenderableDef): def is LineDef;
6
+ export declare function isModelDef(def: RenderableDef): def is ModelDef;
7
+ export declare function isLayerDef(def: RenderableDef): def is LayerDef;
8
+ export declare function isLayerDefs(defs: Readonly<RenderableDefCollection>): defs is readonly LayerDef[];
9
+ export declare function isShapeDefs(defs: Readonly<RenderableDefCollection>): defs is readonly ShapeDef[];
10
+ export declare function isImageDefs(defs: Readonly<RenderableDefCollection>): defs is readonly ImageDef[];
11
+ export declare function isTextDefs(defs: Readonly<RenderableDefCollection>): defs is readonly TextDef[];
12
+ export declare function isLineDefs(defs: Readonly<RenderableDefCollection>): defs is readonly LineDef[];
13
+ export declare function isModelDefs(defs: Readonly<RenderableDefCollection>): defs is readonly ModelDef[];
14
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1,42 @@
1
+ export function isShapeDef(def) {
2
+ return def.shape !== undefined;
3
+ }
4
+ export function isImageDef(def) {
5
+ return def.source !== undefined;
6
+ }
7
+ export function isTextDef(def) {
8
+ return def.lines !== undefined;
9
+ }
10
+ export function isLineDef(def) {
11
+ return def.points !== undefined;
12
+ }
13
+ export function isModelDef(def) {
14
+ return def.url !== undefined;
15
+ }
16
+ export function isLayerDef(def) {
17
+ return def.children !== undefined;
18
+ }
19
+ // The array guards below take the collection type, not `readonly RenderableDef[]`: the collection
20
+ // is a union of single-kind arrays, so a first-element check is sound for the whole array. A plain
21
+ // `RenderableDef[]` parameter would admit mixed arrays the check cannot speak for. The `Readonly`
22
+ // wrapper widens, not narrows: mutable collections are assignable to readonly parameters (and
23
+ // narrowing a mutable union member keeps it mutable), while the reverse would reject the readonly
24
+ // snapshots from `useStagedChildren`.
25
+ export function isLayerDefs(defs) {
26
+ return defs[0] !== undefined && isLayerDef(defs[0]);
27
+ }
28
+ export function isShapeDefs(defs) {
29
+ return defs[0] !== undefined && isShapeDef(defs[0]);
30
+ }
31
+ export function isImageDefs(defs) {
32
+ return defs[0] !== undefined && isImageDef(defs[0]);
33
+ }
34
+ export function isTextDefs(defs) {
35
+ return defs[0] !== undefined && isTextDef(defs[0]);
36
+ }
37
+ export function isLineDefs(defs) {
38
+ return defs[0] !== undefined && isLineDef(defs[0]);
39
+ }
40
+ export function isModelDefs(defs) {
41
+ return defs[0] !== undefined && isModelDef(defs[0]);
42
+ }
@@ -0,0 +1,3 @@
1
+ export { registerSlotWriter, syncNow, writeChanged } from './slot-writers';
2
+ export { childrenOf, subscribeDef, update, versionOf } from './store';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { registerSlotWriter, syncNow, writeChanged } from './slot-writers';
2
+ export { childrenOf, subscribeDef, update, versionOf } from './store';
@@ -0,0 +1,30 @@
1
+ import { type RenderableDef } from '../types/defs';
2
+ /**
3
+ * Registers a def's sync closure, paired with the frame request that shows what it writes.
4
+ *
5
+ * The two are registered together rather than left to each caller because a slot write nobody asked
6
+ * a frame for is invisible: the renderer runs a demand frameloop, so the write lands in GPU memory
7
+ * and waits for a frame that never comes. Pairing them here is what makes every write path — React's
8
+ * and the frame loop's — correct without remembering anything. The frame loop's writes ask for a
9
+ * frame they are already inside, which costs one more after the camera stops.
10
+ * @param def - The def the instance component renders.
11
+ * @param sync - Writes the def's current state into its slot.
12
+ * @param invalidate - Requests the frame that draws it.
13
+ * @returns An unregister function, which removes this writer and leaves the def's others alone.
14
+ */
15
+ export declare function registerSlotWriter(def: RenderableDef, sync: () => void, invalidate: () => void): () => void;
16
+ /**
17
+ * Runs every write registered for a def — each of its slots, and the frame that draws them. A no-op
18
+ * for defs that are not mounted.
19
+ * @param def - The def to re-sync.
20
+ */
21
+ export declare function syncNow(def: RenderableDef): void;
22
+ /**
23
+ * Writes the defs a layout handler reported changed, by the cheapest route that is correct for
24
+ * each: a layer means its membership moved, which only React can reconcile, while anything else is
25
+ * a field write that goes straight into the slot, with no render at all behind the frame that draws
26
+ * it. This is what a layout handler returning defs buys — see `LayoutHandlers.onScale`.
27
+ * @param defs - The defs the handler reported changed, or nothing when it changed nothing.
28
+ */
29
+ export declare function writeChanged(defs: readonly RenderableDef[] | undefined): void;
30
+ //# sourceMappingURL=slot-writers.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { isLayerDef } from '../types/guards';
2
+ import { update } from './store';
3
+ /**
4
+ * The write closures mounted instance components register for their defs — the single write path
5
+ * from a def's fields into its GPU slots, and the frame that draws what they wrote.
6
+ *
7
+ * Two callers run them: the instance's own effect (after `update(def)`), and the camera re-layout
8
+ * frame loop — the latter mutating defs and re-syncing them without going through React at all.
9
+ * Sharing one closure is what keeps the two paths from disagreeing.
10
+ *
11
+ * A **set** per def, because one def can be drawn by more than one mesh: a model merged into a part
12
+ * per source material has an instance in each, and an image whose source is split into tiles has one
13
+ * per placement. Holding a single writer let the last instance to mount overwrite its siblings, so
14
+ * every later write reached exactly one of a def's slots — a two-part model faded half of itself.
15
+ */
16
+ const slotWriters = new WeakMap();
17
+ /**
18
+ * Registers a def's sync closure, paired with the frame request that shows what it writes.
19
+ *
20
+ * The two are registered together rather than left to each caller because a slot write nobody asked
21
+ * a frame for is invisible: the renderer runs a demand frameloop, so the write lands in GPU memory
22
+ * and waits for a frame that never comes. Pairing them here is what makes every write path — React's
23
+ * and the frame loop's — correct without remembering anything. The frame loop's writes ask for a
24
+ * frame they are already inside, which costs one more after the camera stops.
25
+ * @param def - The def the instance component renders.
26
+ * @param sync - Writes the def's current state into its slot.
27
+ * @param invalidate - Requests the frame that draws it.
28
+ * @returns An unregister function, which removes this writer and leaves the def's others alone.
29
+ */
30
+ export function registerSlotWriter(def, sync, invalidate) {
31
+ /** Writes the def's slot and asks for the frame that shows it. */
32
+ function write() {
33
+ sync();
34
+ invalidate();
35
+ }
36
+ const writers = slotWriters.get(def) ?? new Set();
37
+ writers.add(write);
38
+ slotWriters.set(def, writers);
39
+ return () => {
40
+ writers.delete(write);
41
+ };
42
+ }
43
+ /**
44
+ * Runs every write registered for a def — each of its slots, and the frame that draws them. A no-op
45
+ * for defs that are not mounted.
46
+ * @param def - The def to re-sync.
47
+ */
48
+ export function syncNow(def) {
49
+ const writers = slotWriters.get(def);
50
+ if (!writers)
51
+ return;
52
+ // Over a copy: a write can re-claim a slot, and a re-claim mounts and unmounts writers.
53
+ for (const write of [...writers])
54
+ write();
55
+ }
56
+ /**
57
+ * Writes the defs a layout handler reported changed, by the cheapest route that is correct for
58
+ * each: a layer means its membership moved, which only React can reconcile, while anything else is
59
+ * a field write that goes straight into the slot, with no render at all behind the frame that draws
60
+ * it. This is what a layout handler returning defs buys — see `LayoutHandlers.onScale`.
61
+ * @param defs - The defs the handler reported changed, or nothing when it changed nothing.
62
+ */
63
+ export function writeChanged(defs) {
64
+ if (!defs)
65
+ return;
66
+ for (const def of defs) {
67
+ if (isLayerDef(def))
68
+ update(def);
69
+ else
70
+ syncNow(def);
71
+ }
72
+ }
@@ -0,0 +1,41 @@
1
+ import { type LayerDef, type RenderableDef, type RenderableDefCollection } from '../types/defs';
2
+ /**
3
+ * Marks defs as changed after their fields, geometry, or `children` were mutated, so the renderer
4
+ * mirrors the mutation into GPU state.
5
+ *
6
+ * This is the renderer's only update signal. Mutate whatever you like — scalar fields, geometry in
7
+ * place, a layer's `children` array — then pass the defs you touched; for a membership change pass
8
+ * the parent layer, and for a reparent pass both parents. Calling it before the renderer mounts, or
9
+ * after it unmounts, is a safe no-op: defs are the source of truth and mounting performs a full
10
+ * sync. Calling it on an unchanged def is harmless.
11
+ * @param defs - Defs that changed.
12
+ */
13
+ export declare function update(...defs: RenderableDef[]): void;
14
+ /**
15
+ * Subscribes to a def's change notifications. Internal — components reach this through
16
+ * `useDefSync` and `useStagedChildren`.
17
+ * @param def - The def to watch.
18
+ * @param listener - Called after every {@link update} touching the def.
19
+ * @returns An unsubscribe function.
20
+ */
21
+ export declare function subscribeDef(def: RenderableDef, listener: () => void): () => void;
22
+ /**
23
+ * Reads a def's current version — the value `useSyncExternalStore` snapshots.
24
+ * @param def - The def to read.
25
+ * @returns The version, or `0` when nothing is subscribed.
26
+ */
27
+ export declare function versionOf(def: RenderableDef): number;
28
+ /**
29
+ * Reads a layer's children as a version-keyed snapshot. Internal — components reach this through
30
+ * `useStagedChildren`.
31
+ *
32
+ * The layer's `children` array is mutated in place, so its identity can never signal a membership
33
+ * change to memoization. The snapshot restores that signal at the store level: it is rebuilt as a
34
+ * fresh copy exactly when the layer's version has moved, and the cached copy is returned otherwise —
35
+ * the identity stability `useSyncExternalStore` requires between updates, and the identity change
36
+ * downstream memoization invalidates on.
37
+ * @param def - The layer whose children to snapshot.
38
+ * @returns The current snapshot; a new array iff the layer was updated since the last call.
39
+ */
40
+ export declare function childrenOf<T extends RenderableDef>(def: LayerDef<T>): Readonly<RenderableDefCollection<T>>;
41
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Per-def subscription state, created on first subscribe and dropped when the last subscriber
3
+ * leaves. Keyed weakly by def identity: a def the renderer never mounted has no entry, which is
4
+ * exactly why {@link update} on it is a no-op rather than an error.
5
+ */
6
+ const subscriptions = new WeakMap();
7
+ /**
8
+ * Marks defs as changed after their fields, geometry, or `children` were mutated, so the renderer
9
+ * mirrors the mutation into GPU state.
10
+ *
11
+ * This is the renderer's only update signal. Mutate whatever you like — scalar fields, geometry in
12
+ * place, a layer's `children` array — then pass the defs you touched; for a membership change pass
13
+ * the parent layer, and for a reparent pass both parents. Calling it before the renderer mounts, or
14
+ * after it unmounts, is a safe no-op: defs are the source of truth and mounting performs a full
15
+ * sync. Calling it on an unchanged def is harmless.
16
+ * @param defs - Defs that changed.
17
+ */
18
+ export function update(...defs) {
19
+ for (const def of defs)
20
+ notify(def);
21
+ }
22
+ /**
23
+ * Subscribes to a def's change notifications. Internal — components reach this through
24
+ * `useDefSync` and `useStagedChildren`.
25
+ * @param def - The def to watch.
26
+ * @param listener - Called after every {@link update} touching the def.
27
+ * @returns An unsubscribe function.
28
+ */
29
+ export function subscribeDef(def, listener) {
30
+ const entry = entryFor(def);
31
+ entry.listeners.add(listener);
32
+ return () => {
33
+ const current = subscriptions.get(def);
34
+ if (!current)
35
+ return;
36
+ current.listeners.delete(listener);
37
+ // The entry outlives its listeners deliberately. `useSyncExternalStore` re-subscribes whenever
38
+ // its subscribe closure changes identity, which happens on every render unless the closure is
39
+ // memoized; dropping the entry on the last unsubscribe would reset the version to 0 mid-render,
40
+ // costing a spurious re-render and re-sync and breaking the monotonicity the `childrenOf` cache
41
+ // relies on to signal in-place mutation. Retaining it costs nothing: the map is weak, so
42
+ // an entry is collected with its def.
43
+ };
44
+ }
45
+ /**
46
+ * Reads a def's current version — the value `useSyncExternalStore` snapshots.
47
+ * @param def - The def to read.
48
+ * @returns The version, or `0` when nothing is subscribed.
49
+ */
50
+ export function versionOf(def) {
51
+ return subscriptions.get(def)?.version ?? 0;
52
+ }
53
+ /**
54
+ * Reads a layer's children as a version-keyed snapshot. Internal — components reach this through
55
+ * `useStagedChildren`.
56
+ *
57
+ * The layer's `children` array is mutated in place, so its identity can never signal a membership
58
+ * change to memoization. The snapshot restores that signal at the store level: it is rebuilt as a
59
+ * fresh copy exactly when the layer's version has moved, and the cached copy is returned otherwise —
60
+ * the identity stability `useSyncExternalStore` requires between updates, and the identity change
61
+ * downstream memoization invalidates on.
62
+ * @param def - The layer whose children to snapshot.
63
+ * @returns The current snapshot; a new array iff the layer was updated since the last call.
64
+ */
65
+ export function childrenOf(def) {
66
+ const entry = entryFor(def);
67
+ if (entry.childrenSnapshot?.version !== entry.version) {
68
+ entry.childrenSnapshot = { version: entry.version, snapshot: [...def.children] };
69
+ }
70
+ // A layer's children are homogeneous (`RenderableDefCollection<T>`), so the erased cache entry is
71
+ // safely re-narrowed to the layer's own child type.
72
+ return entry.childrenSnapshot.snapshot;
73
+ }
74
+ /**
75
+ * Returns a def's subscription entry, creating it on first contact.
76
+ * @param def - The def to look up.
77
+ * @returns The entry.
78
+ */
79
+ function entryFor(def) {
80
+ let entry = subscriptions.get(def);
81
+ if (!entry) {
82
+ entry = { version: 0, listeners: new Set() };
83
+ subscriptions.set(def, entry);
84
+ }
85
+ return entry;
86
+ }
87
+ /**
88
+ * Bumps one def's version and notifies its listeners.
89
+ * @param def - The def that changed.
90
+ */
91
+ function notify(def) {
92
+ const entry = subscriptions.get(def);
93
+ if (!entry)
94
+ return;
95
+ entry.version++;
96
+ for (const listener of entry.listeners)
97
+ listener();
98
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Whether two arrays hold the same values in the same positions, compared by identity.
3
+ *
4
+ * The batched classes keep an applied order and are handed a desired one on every membership
5
+ * change; this is what lets an unchanged order skip the work of applying it — which matters most
6
+ * where applying it is expensive (repacking glyphs or endpoints) and the caller cannot know whether
7
+ * anything moved.
8
+ * @param a - The first sequence.
9
+ * @param b - The second sequence.
10
+ * @returns Whether they match.
11
+ */
12
+ export declare function sameSequence<T>(a: readonly T[], b: readonly T[]): boolean;
13
+ //# sourceMappingURL=arrays.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Whether two arrays hold the same values in the same positions, compared by identity.
3
+ *
4
+ * The batched classes keep an applied order and are handed a desired one on every membership
5
+ * change; this is what lets an unchanged order skip the work of applying it — which matters most
6
+ * where applying it is expensive (repacking glyphs or endpoints) and the caller cannot know whether
7
+ * anything moved.
8
+ * @param a - The first sequence.
9
+ * @param b - The second sequence.
10
+ * @returns Whether they match.
11
+ */
12
+ export function sameSequence(a, b) {
13
+ if (a.length !== b.length)
14
+ return false;
15
+ for (let i = 0; i < a.length; i++) {
16
+ if (a[i] !== b[i])
17
+ return false;
18
+ }
19
+ return true;
20
+ }
@@ -0,0 +1,19 @@
1
+ import { type RgbaColor } from 'colord';
2
+ import { type ColorInput } from '../types/defs';
3
+ /**
4
+ * Normalizes a {@link ColorInput} into an rgba color object.
5
+ * @param colorInput color as a css color string or a numeric 0xRRGGBB value
6
+ * @returns rgba color (r/g/b 0-255, a 0-1), or undefined if the input is not a valid color
7
+ */
8
+ export declare function normalizeColor(colorInput: ColorInput): RgbaColor | undefined;
9
+ /**
10
+ * Whether a color carries an alpha below 1, and so belongs in a translucent batch.
11
+ *
12
+ * A color that does not parse counts as **opaque**: `ShapeInstance` reports it and leaves the slot's
13
+ * tint alone, so moving a shape into a later render pass on the strength of a value the renderer
14
+ * could not read would compound one fault with another.
15
+ * @param colorInput color as a css color string or a numeric 0xRRGGBB value
16
+ * @returns true when the color is translucent
17
+ */
18
+ export declare function isTranslucentColor(colorInput: ColorInput): boolean;
19
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { colord, extend } from 'colord';
2
+ import namesPlugin from 'colord/plugins/names';
3
+ extend([namesPlugin]);
4
+ /**
5
+ * Normalizes a {@link ColorInput} into an rgba color object.
6
+ * @param colorInput color as a css color string or a numeric 0xRRGGBB value
7
+ * @returns rgba color (r/g/b 0-255, a 0-1), or undefined if the input is not a valid color
8
+ */
9
+ export function normalizeColor(colorInput) {
10
+ const colorString = typeof colorInput === 'string' ? colorInput : `#${colorInput.toString(16).padStart(6, '0')}`;
11
+ const color = colord(colorString);
12
+ if (!color.isValid())
13
+ return undefined;
14
+ return color.toRgb();
15
+ }
16
+ /**
17
+ * Whether a color carries an alpha below 1, and so belongs in a translucent batch.
18
+ *
19
+ * A color that does not parse counts as **opaque**: `ShapeInstance` reports it and leaves the slot's
20
+ * tint alone, so moving a shape into a later render pass on the strength of a value the renderer
21
+ * could not read would compound one fault with another.
22
+ * @param colorInput color as a css color string or a numeric 0xRRGGBB value
23
+ * @returns true when the color is translucent
24
+ */
25
+ export function isTranslucentColor(colorInput) {
26
+ return (normalizeColor(colorInput)?.a ?? 1) < 1;
27
+ }
@@ -0,0 +1,26 @@
1
+ import { type Debugger } from 'debug';
2
+ import { type SceneDef } from '../types/defs';
3
+ /** Logger interface */
4
+ export interface Logger {
5
+ /** Debug level logger */
6
+ debug: Debugger;
7
+ /** Info level logger */
8
+ info: Debugger;
9
+ /** Warn level logger */
10
+ warn: Debugger;
11
+ /** Error level logger */
12
+ error: Debugger;
13
+ }
14
+ /**
15
+ * Creates a logger with the given namespace.
16
+ * @param namespace - The namespace to use for the logger.
17
+ * @returns A logger with the given namespace.
18
+ */
19
+ export declare function createLogger(namespace: string): Logger;
20
+ /**
21
+ * Prints the layer tree of the given scene to the console.
22
+ * @param scene - Scene tree to print
23
+ * @param logger - The logger to use to print the tree.
24
+ */
25
+ export declare function printScene(scene: SceneDef, logger: Debugger): void;
26
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1,47 @@
1
+ /* eslint-disable no-console */
2
+ import createLog from 'debug';
3
+ import { isLayerDefs } from '../types/guards';
4
+ const rootNamespace = 'efp:renderer';
5
+ /**
6
+ * Creates a logger with the given namespace.
7
+ * @param namespace - The namespace to use for the logger.
8
+ * @returns A logger with the given namespace.
9
+ */
10
+ export function createLogger(namespace) {
11
+ const fullNamespace = namespace ? `${rootNamespace}:${namespace}` : rootNamespace;
12
+ const info = createLog(fullNamespace);
13
+ info.log = console.info.bind(console);
14
+ const debug = info.extend('debug');
15
+ debug.log = console.debug.bind(console);
16
+ const warn = info.extend('warn');
17
+ warn.log = console.warn.bind(console);
18
+ const error = info.extend('error');
19
+ error.log = console.error.bind(console);
20
+ return {
21
+ debug,
22
+ info,
23
+ warn,
24
+ error,
25
+ };
26
+ }
27
+ /**
28
+ * Prints the layer tree of the given scene to the console.
29
+ * @param scene - Scene tree to print
30
+ * @param logger - The logger to use to print the tree.
31
+ */
32
+ export function printScene(scene, logger) {
33
+ function printLayer(def) {
34
+ const fullName = def.name;
35
+ if (isLayerDefs(def.children)) {
36
+ console.groupCollapsed(fullName);
37
+ def.children.forEach((c) => printLayer(c));
38
+ console.groupEnd();
39
+ }
40
+ else {
41
+ logger(fullName);
42
+ }
43
+ }
44
+ if (!logger.enabled)
45
+ return;
46
+ printLayer(scene.rootLayer);
47
+ }