@expofp/renderer 0.0.0-experimental.2d6c681

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 (280) hide show
  1. package/README.md +145 -0
  2. package/dist/Renderer.d.ts +102 -0
  3. package/dist/Renderer.js +96 -0
  4. package/dist/controls/CameraControls.d.ts +108 -0
  5. package/dist/controls/CameraControls.js +203 -0
  6. package/dist/controls/Controls.d.ts +53 -0
  7. package/dist/controls/Controls.js +54 -0
  8. package/dist/controls/camera-api.d.ts +226 -0
  9. package/dist/controls/camera-api.js +179 -0
  10. package/dist/controls/gestures.d.ts +71 -0
  11. package/dist/controls/gestures.js +98 -0
  12. package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
  13. package/dist/controls/handlers/DoubleTapZoom.js +82 -0
  14. package/dist/controls/handlers/Inertia.d.ts +60 -0
  15. package/dist/controls/handlers/Inertia.js +222 -0
  16. package/dist/controls/handlers/Pan.d.ts +23 -0
  17. package/dist/controls/handlers/Pan.js +212 -0
  18. package/dist/controls/handlers/Pitch.d.ts +19 -0
  19. package/dist/controls/handlers/Pitch.js +200 -0
  20. package/dist/controls/handlers/Roll.d.ts +30 -0
  21. package/dist/controls/handlers/Roll.js +196 -0
  22. package/dist/controls/handlers/Zoom.d.ts +28 -0
  23. package/dist/controls/handlers/Zoom.js +34 -0
  24. package/dist/controls/hooks.d.ts +136 -0
  25. package/dist/controls/hooks.js +200 -0
  26. package/dist/controls/index.d.ts +9 -0
  27. package/dist/controls/index.js +7 -0
  28. package/dist/controls/zoom-to.d.ts +89 -0
  29. package/dist/controls/zoom-to.js +91 -0
  30. package/dist/core/InstanceUniforms.d.ts +108 -0
  31. package/dist/core/InstanceUniforms.js +298 -0
  32. package/dist/core/SquareDataTexture.d.ts +277 -0
  33. package/dist/core/SquareDataTexture.js +476 -0
  34. package/dist/core/batch/BatchedMesh.d.ts +74 -0
  35. package/dist/core/batch/BatchedMesh.js +155 -0
  36. package/dist/core/instanced/FrustumCulling.d.ts +141 -0
  37. package/dist/core/instanced/FrustumCulling.js +246 -0
  38. package/dist/core/instanced/InstancedMesh.d.ts +302 -0
  39. package/dist/core/instanced/InstancedMesh.js +497 -0
  40. package/dist/core/instanced/Instances.d.ts +119 -0
  41. package/dist/core/instanced/Instances.js +230 -0
  42. package/dist/core/instanced/Raycasting.d.ts +118 -0
  43. package/dist/core/instanced/Raycasting.js +134 -0
  44. package/dist/core/instanced/instanced-material.d.ts +77 -0
  45. package/dist/core/instanced/instanced-material.js +228 -0
  46. package/dist/core/lines/BatchedLines.d.ts +137 -0
  47. package/dist/core/lines/BatchedLines.js +215 -0
  48. package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
  49. package/dist/core/lines/FrustumCullingLines.js +85 -0
  50. package/dist/core/lines/RaycastingLines.d.ts +45 -0
  51. package/dist/core/lines/RaycastingLines.js +91 -0
  52. package/dist/core/lines/lines-material.d.ts +47 -0
  53. package/dist/core/lines/lines-material.js +124 -0
  54. package/dist/core/shader-source.d.ts +70 -0
  55. package/dist/core/shader-source.js +110 -0
  56. package/dist/core/sprites/BatchedSprites.d.ts +115 -0
  57. package/dist/core/sprites/BatchedSprites.js +173 -0
  58. package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
  59. package/dist/core/sprites/RaycastingSprites.js +71 -0
  60. package/dist/core/sprites/sprite-material.d.ts +59 -0
  61. package/dist/core/sprites/sprite-material.js +192 -0
  62. package/dist/core/text/BatchedText.d.ts +233 -0
  63. package/dist/core/text/BatchedText.js +473 -0
  64. package/dist/core/text/MemberTexture.d.ts +90 -0
  65. package/dist/core/text/MemberTexture.js +289 -0
  66. package/dist/core/text/Text.d.ts +88 -0
  67. package/dist/core/text/Text.js +230 -0
  68. package/dist/core/text/batched-text-material.d.ts +56 -0
  69. package/dist/core/text/batched-text-material.js +123 -0
  70. package/dist/core/text/glyph-packing.d.ts +17 -0
  71. package/dist/core/text/glyph-packing.js +61 -0
  72. package/dist/core/typed-array.d.ts +16 -0
  73. package/dist/core/typed-array.js +20 -0
  74. package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
  75. package/dist/debug/components/DebugPanelProvider.js +50 -0
  76. package/dist/debug/components/DebugTools.d.ts +22 -0
  77. package/dist/debug/components/DebugTools.js +28 -0
  78. package/dist/debug/components/Gui.d.ts +22 -0
  79. package/dist/debug/components/Gui.js +42 -0
  80. package/dist/debug/components/MemoryInfo.d.ts +22 -0
  81. package/dist/debug/components/MemoryInfo.js +42 -0
  82. package/dist/debug/components/Panel.d.ts +31 -0
  83. package/dist/debug/components/Panel.js +104 -0
  84. package/dist/debug/components/Stats.d.ts +9 -0
  85. package/dist/debug/components/Stats.js +24 -0
  86. package/dist/debug/debug-chunk.d.ts +26 -0
  87. package/dist/debug/debug-chunk.js +32 -0
  88. package/dist/debug/debug-context.d.ts +41 -0
  89. package/dist/debug/debug-context.js +59 -0
  90. package/dist/debug/index.d.ts +41 -0
  91. package/dist/debug/index.js +43 -0
  92. package/dist/events/LayoutDriver.d.ts +36 -0
  93. package/dist/events/LayoutDriver.js +192 -0
  94. package/dist/events/PointerMissed.d.ts +23 -0
  95. package/dist/events/PointerMissed.js +58 -0
  96. package/dist/events/def-culling.d.ts +46 -0
  97. package/dist/events/def-culling.js +69 -0
  98. package/dist/events/layout-targets.d.ts +151 -0
  99. package/dist/events/layout-targets.js +397 -0
  100. package/dist/events/placement/box.d.ts +59 -0
  101. package/dist/events/placement/box.js +96 -0
  102. package/dist/events/placement/grid.d.ts +161 -0
  103. package/dist/events/placement/grid.js +235 -0
  104. package/dist/events/placement/index.d.ts +2 -0
  105. package/dist/events/placement/index.js +1 -0
  106. package/dist/events/placement/pass.d.ts +70 -0
  107. package/dist/events/placement/pass.js +617 -0
  108. package/dist/events/pointer.d.ts +194 -0
  109. package/dist/events/pointer.js +356 -0
  110. package/dist/external/ExternalCanvas.d.ts +56 -0
  111. package/dist/external/ExternalCanvas.js +177 -0
  112. package/dist/external/external-camera.d.ts +18 -0
  113. package/dist/external/external-camera.js +103 -0
  114. package/dist/external/external-events.d.ts +14 -0
  115. package/dist/external/external-events.js +33 -0
  116. package/dist/index.d.ts +13 -0
  117. package/dist/index.js +14 -0
  118. package/dist/objects/Background.d.ts +31 -0
  119. package/dist/objects/Background.js +37 -0
  120. package/dist/objects/containers/SlotContainer.d.ts +312 -0
  121. package/dist/objects/containers/SlotContainer.js +311 -0
  122. package/dist/objects/containers/hooks.d.ts +61 -0
  123. package/dist/objects/containers/hooks.js +196 -0
  124. package/dist/objects/containers/index.d.ts +6 -0
  125. package/dist/objects/containers/index.js +4 -0
  126. package/dist/objects/containers/keys.d.ts +10 -0
  127. package/dist/objects/containers/keys.js +18 -0
  128. package/dist/objects/containers/mesh-writes.d.ts +106 -0
  129. package/dist/objects/containers/mesh-writes.js +97 -0
  130. package/dist/objects/containers/props.d.ts +33 -0
  131. package/dist/objects/containers/props.js +1 -0
  132. package/dist/objects/images/ImageInstance.d.ts +29 -0
  133. package/dist/objects/images/ImageInstance.js +152 -0
  134. package/dist/objects/images/Images.d.ts +49 -0
  135. package/dist/objects/images/Images.js +394 -0
  136. package/dist/objects/images/atlas-packer.d.ts +129 -0
  137. package/dist/objects/images/atlas-packer.js +287 -0
  138. package/dist/objects/images/image-container.d.ts +74 -0
  139. package/dist/objects/images/image-container.js +100 -0
  140. package/dist/objects/images/index.d.ts +2 -0
  141. package/dist/objects/images/index.js +1 -0
  142. package/dist/objects/images/pack-sources.d.ts +190 -0
  143. package/dist/objects/images/pack-sources.js +272 -0
  144. package/dist/objects/images/texture-budget.d.ts +120 -0
  145. package/dist/objects/images/texture-budget.js +215 -0
  146. package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
  147. package/dist/objects/images/worker/atlas-worker.js +123 -0
  148. package/dist/objects/images/worker/client.d.ts +62 -0
  149. package/dist/objects/images/worker/client.js +249 -0
  150. package/dist/objects/images/worker/index.d.ts +10 -0
  151. package/dist/objects/images/worker/index.js +1 -0
  152. package/dist/objects/layer/Layer.d.ts +28 -0
  153. package/dist/objects/layer/Layer.js +134 -0
  154. package/dist/objects/layer/index.d.ts +2 -0
  155. package/dist/objects/layer/index.js +1 -0
  156. package/dist/objects/layer/layer-context.d.ts +74 -0
  157. package/dist/objects/layer/layer-context.js +82 -0
  158. package/dist/objects/layer/mount-scheduler.d.ts +131 -0
  159. package/dist/objects/layer/mount-scheduler.js +289 -0
  160. package/dist/objects/layer/visibility-gui.d.ts +19 -0
  161. package/dist/objects/layer/visibility-gui.js +93 -0
  162. package/dist/objects/lighting/Lighting.d.ts +92 -0
  163. package/dist/objects/lighting/Lighting.js +384 -0
  164. package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
  165. package/dist/objects/lighting/OcclusionPasses.js +271 -0
  166. package/dist/objects/lighting/index.d.ts +11 -0
  167. package/dist/objects/lighting/index.js +10 -0
  168. package/dist/objects/lighting/lighting-gui.d.ts +24 -0
  169. package/dist/objects/lighting/lighting-gui.js +114 -0
  170. package/dist/objects/lighting/occluder.d.ts +34 -0
  171. package/dist/objects/lighting/occluder.js +62 -0
  172. package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
  173. package/dist/objects/lighting/occlusion-targets.js +276 -0
  174. package/dist/objects/lines/LineInstance.d.ts +21 -0
  175. package/dist/objects/lines/LineInstance.js +73 -0
  176. package/dist/objects/lines/Lines.d.ts +17 -0
  177. package/dist/objects/lines/Lines.js +31 -0
  178. package/dist/objects/lines/index.d.ts +2 -0
  179. package/dist/objects/lines/index.js +1 -0
  180. package/dist/objects/lines/line-container.d.ts +35 -0
  181. package/dist/objects/lines/line-container.js +40 -0
  182. package/dist/objects/materials/ColorMaterial.d.ts +92 -0
  183. package/dist/objects/materials/ColorMaterial.js +124 -0
  184. package/dist/objects/materials/LineMaterial.d.ts +44 -0
  185. package/dist/objects/materials/LineMaterial.js +74 -0
  186. package/dist/objects/materials/ModelMaterial.d.ts +53 -0
  187. package/dist/objects/materials/ModelMaterial.js +111 -0
  188. package/dist/objects/materials/TextureMaterial.d.ts +33 -0
  189. package/dist/objects/materials/TextureMaterial.js +48 -0
  190. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
  191. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
  192. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
  193. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
  194. package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
  195. package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
  196. package/dist/objects/materials/effects/ao/index.d.ts +129 -0
  197. package/dist/objects/materials/effects/ao/index.js +72 -0
  198. package/dist/objects/materials/effects/color-dim.d.ts +115 -0
  199. package/dist/objects/materials/effects/color-dim.js +206 -0
  200. package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
  201. package/dist/objects/materials/effects/contact-shadow.js +167 -0
  202. package/dist/objects/materials/effects/effects-context.d.ts +53 -0
  203. package/dist/objects/materials/effects/effects-context.js +24 -0
  204. package/dist/objects/materials/shared.d.ts +24 -0
  205. package/dist/objects/materials/shared.js +72 -0
  206. package/dist/objects/models/ModelInstance.d.ts +22 -0
  207. package/dist/objects/models/ModelInstance.js +33 -0
  208. package/dist/objects/models/Models.d.ts +22 -0
  209. package/dist/objects/models/Models.js +186 -0
  210. package/dist/objects/models/gltf-cache.d.ts +17 -0
  211. package/dist/objects/models/gltf-cache.js +50 -0
  212. package/dist/objects/models/index.d.ts +2 -0
  213. package/dist/objects/models/index.js +1 -0
  214. package/dist/objects/models/model-asset.d.ts +48 -0
  215. package/dist/objects/models/model-asset.js +77 -0
  216. package/dist/objects/models/model-container.d.ts +123 -0
  217. package/dist/objects/models/model-container.js +207 -0
  218. package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
  219. package/dist/objects/shapes/ShapeInstance.js +39 -0
  220. package/dist/objects/shapes/Shapes.d.ts +21 -0
  221. package/dist/objects/shapes/Shapes.js +131 -0
  222. package/dist/objects/shapes/index.d.ts +2 -0
  223. package/dist/objects/shapes/index.js +1 -0
  224. package/dist/objects/shapes/shape-container.d.ts +141 -0
  225. package/dist/objects/shapes/shape-container.js +242 -0
  226. package/dist/objects/shapes/shape-geometry.d.ts +27 -0
  227. package/dist/objects/shapes/shape-geometry.js +94 -0
  228. package/dist/objects/text/TextInstance.d.ts +22 -0
  229. package/dist/objects/text/TextInstance.js +95 -0
  230. package/dist/objects/text/Texts.d.ts +30 -0
  231. package/dist/objects/text/Texts.js +43 -0
  232. package/dist/objects/text/index.d.ts +2 -0
  233. package/dist/objects/text/index.js +1 -0
  234. package/dist/objects/text/text-container.d.ts +121 -0
  235. package/dist/objects/text/text-container.js +229 -0
  236. package/dist/objects/text/text-layout.d.ts +92 -0
  237. package/dist/objects/text/text-layout.js +180 -0
  238. package/dist/space/Camera.d.ts +103 -0
  239. package/dist/space/Camera.js +321 -0
  240. package/dist/space/Scene.d.ts +47 -0
  241. package/dist/space/Scene.js +127 -0
  242. package/dist/space/Space.d.ts +325 -0
  243. package/dist/space/Space.js +359 -0
  244. package/dist/space/VisibleRect.d.ts +45 -0
  245. package/dist/space/VisibleRect.js +129 -0
  246. package/dist/space/camera-handle.d.ts +67 -0
  247. package/dist/space/camera-handle.js +63 -0
  248. package/dist/space/camera-kind.d.ts +26 -0
  249. package/dist/space/camera-kind.js +30 -0
  250. package/dist/space/canvas-size.d.ts +24 -0
  251. package/dist/space/canvas-size.js +20 -0
  252. package/dist/space/clipping.d.ts +69 -0
  253. package/dist/space/clipping.js +130 -0
  254. package/dist/space/layout-measure.d.ts +67 -0
  255. package/dist/space/layout-measure.js +175 -0
  256. package/dist/space/viewport.d.ts +112 -0
  257. package/dist/space/viewport.js +67 -0
  258. package/dist/types/defs.d.ts +234 -0
  259. package/dist/types/defs.js +1 -0
  260. package/dist/types/events.d.ts +198 -0
  261. package/dist/types/events.js +1 -0
  262. package/dist/types/guards.d.ts +21 -0
  263. package/dist/types/guards.js +52 -0
  264. package/dist/update/index.d.ts +3 -0
  265. package/dist/update/index.js +2 -0
  266. package/dist/update/slot-writers.d.ts +30 -0
  267. package/dist/update/slot-writers.js +76 -0
  268. package/dist/update/store.d.ts +41 -0
  269. package/dist/update/store.js +104 -0
  270. package/dist/utils/arrays.d.ts +13 -0
  271. package/dist/utils/arrays.js +20 -0
  272. package/dist/utils/colors.d.ts +19 -0
  273. package/dist/utils/colors.js +27 -0
  274. package/dist/utils/hot-state.d.ts +41 -0
  275. package/dist/utils/hot-state.js +37 -0
  276. package/dist/utils/logging.d.ts +29 -0
  277. package/dist/utils/logging.js +55 -0
  278. package/dist/utils/math.d.ts +52 -0
  279. package/dist/utils/math.js +76 -0
  280. package/package.json +79 -0
@@ -0,0 +1,61 @@
1
+ import { type RefObject } from 'react';
2
+ import { type RenderableDef } from '../../types/defs';
3
+ import { type SlotContainer, type SlotMesh } from './SlotContainer';
4
+ /**
5
+ * Creates one layer's container and keeps it wired to its layer for as long as the component is
6
+ * mounted: the membership's paint order, how hits on its mesh resolve back to defs, and what runs
7
+ * when a member no longer belongs in this container's kind.
8
+ *
9
+ * The container is built once, from the first render's defs, and outlives every render after —
10
+ * membership changes grow it in place rather than rebuilding it, which is what lets the mesh R3F
11
+ * built from its args stand too. `create` is therefore called once and its later identities ignored.
12
+ * @param create - Builds the container. Called on the first render only.
13
+ * @param defs - The layer's defs, in authored paint order.
14
+ * @param onKindMismatch - Re-splits the layer, for a container that finds a member of the wrong
15
+ * kind. Registered as {@link SlotContainer.setKindMismatchHandler} and cleared on unmount or
16
+ * before a new identity replaces it. A container with no kind of its own to leave omits it, and
17
+ * nothing is registered.
18
+ * @returns The container.
19
+ */
20
+ export declare function useContainer<T extends RenderableDef, C extends SlotContainer<unknown, SlotMesh, T>>(create: () => C, defs: readonly T[], onKindMismatch?: () => void): C;
21
+ /**
22
+ * Claims a slot for one instance, releasing on unmount. Paint order is not this hook's concern —
23
+ * the container writes it for the whole sibling list in one pass (see {@link useContainer}).
24
+ * @param container - The container holding the slot.
25
+ * @param def - The def this instance renders.
26
+ * @param resource - The per-instance resource backing the slot; a new identity re-claims.
27
+ * @returns A ref holding the current slot, or `-1` while unclaimed. Read it at call time — a
28
+ * re-claim changes the slot without re-rendering.
29
+ */
30
+ export declare function useSlot<R>(container: SlotContainer<R>, def: RenderableDef, resource: R): RefObject<number>;
31
+ /**
32
+ * Claims a slot for one instance and keeps it in sync with its def: the slot is released on
33
+ * unmount, and on mount and after every `update(def)` the def's visibility and dim are written and —
34
+ * only while it is visible — `write` fills in the rest of the instance's state.
35
+ *
36
+ * The sync is registered as the def's slot writer, so it is also what the camera re-layout frame
37
+ * loop pulls out of `slotWriters` and calls directly — the React path and the per-frame path are
38
+ * one implementation, and both therefore agree about visibility.
39
+ *
40
+ * `hidden` and `dim` live on every def type, so their writes — and the short-circuit `hidden` gates
41
+ * — belong here rather than repeated in each instance component's callback. A hidden instance is
42
+ * never drawn, so computing its transform, color, or glyphs would be wasted; the update that shows
43
+ * it re-runs this sync, which writes the then-current state. Everything type-specific stays in
44
+ * `write`, which is what keeps this hook generic across every def type.
45
+ *
46
+ * **An `update(def)` renders nothing unless the slot's resource moved.** The only thing a render of
47
+ * an instance can do that a sync cannot is re-claim its slot, and that is needed only when the
48
+ * resource backing it changed — a text's line count, a shape's geometry. So the store's snapshot is
49
+ * the resource itself, not a version counter: an update that leaves it alone (a dim, a color, a
50
+ * move) is written by the subscription's own listener, straight into the slot, and React bails out
51
+ * of the render because the snapshot is unchanged. An update that moves it renders, and the render's
52
+ * re-claim writes the def into the new slot.
53
+ * @param container - The container holding the slot.
54
+ * @param def - The def this instance renders.
55
+ * @param resourceOf - Reads the per-instance resource backing the slot off the def; a new identity
56
+ * re-claims. Called on every update, so it must be cheap and return the same identity for an
57
+ * unchanged def. `null` for a container whose slots need no resource, which never re-claims.
58
+ * @param write - Writes the def's type-specific state into its slot. Called only while visible.
59
+ */
60
+ export declare function useDefSync<R>(container: SlotContainer<R>, def: RenderableDef, resourceOf: (() => R) | null, write: (slot: number) => void): void;
61
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1,196 @@
1
+ import { useStore } from '@react-three/fiber';
2
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
3
+ import { registerHitSource } from '../../events/pointer';
4
+ import { registerSlotWriter, subscribeDef, syncNow } from '../../update';
5
+ import { resolveDim, subscribeDim, useDimChain } from '../layer/layer-context';
6
+ /**
7
+ * Creates one layer's container and keeps it wired to its layer for as long as the component is
8
+ * mounted: the membership's paint order, how hits on its mesh resolve back to defs, and what runs
9
+ * when a member no longer belongs in this container's kind.
10
+ *
11
+ * The container is built once, from the first render's defs, and outlives every render after —
12
+ * membership changes grow it in place rather than rebuilding it, which is what lets the mesh R3F
13
+ * built from its args stand too. `create` is therefore called once and its later identities ignored.
14
+ * @param create - Builds the container. Called on the first render only.
15
+ * @param defs - The layer's defs, in authored paint order.
16
+ * @param onKindMismatch - Re-splits the layer, for a container that finds a member of the wrong
17
+ * kind. Registered as {@link SlotContainer.setKindMismatchHandler} and cleared on unmount or
18
+ * before a new identity replaces it. A container with no kind of its own to leave omits it, and
19
+ * nothing is registered.
20
+ * @returns The container.
21
+ */
22
+ export function useContainer(create, defs, onKindMismatch) {
23
+ const [container] = useState(create);
24
+ const chain = useDimChain();
25
+ const invalidate = useInvalidate();
26
+ // The membership the dim subscription below re-orders, current as of the last commit.
27
+ const defsRef = useRef(defs);
28
+ useEffect(() => {
29
+ defsRef.current = defs;
30
+ });
31
+ // Paint order for the whole membership, one pass per structural change. Child effects run before
32
+ // this one, so every instance the change mounted has already claimed its slot; defs whose
33
+ // instances bailed out (memo) never re-ran, which is fine — order lives in the container, not in
34
+ // their props. Running inside the same effect flush as the claims is what keeps a frame from
35
+ // drawing between a claim's default order and this pass.
36
+ useEffect(() => {
37
+ container.setPaintOrder(defs, resolveDim(chain) > 0);
38
+ invalidate();
39
+ }, [container, defs, chain, invalidate]);
40
+ // And again as the layer's dim crosses zero — only whether it is dimmed, not by how much, since the
41
+ // order pass splits two ways. Followed by subscription, not render: an eased dim crosses zero once
42
+ // for every container in the plan in the same frame, and rendering each of them to get here cost
43
+ // that frame more than the passes themselves.
44
+ useEffect(() => {
45
+ let dimmed = resolveDim(chain) > 0;
46
+ return subscribeDim(chain, () => {
47
+ const next = resolveDim(chain) > 0;
48
+ if (next === dimmed)
49
+ return;
50
+ dimmed = next;
51
+ container.setPaintOrder(defsRef.current, next);
52
+ invalidate();
53
+ });
54
+ }, [container, chain, invalidate]);
55
+ // What a hit on this container's mesh means: which def drew it, and where that def sits in the
56
+ // draw order. From an effect, so the mesh is bound by now; the container answers both, so nothing
57
+ // here has to know one mesh kind from another.
58
+ useEffect(() => registerHitSource(container.mesh, container), [container]);
59
+ // Assigned after mount rather than passed to `create`, because the container is built once and
60
+ // outlives every render, so it cannot close over a fresh callback. A caller with nothing to run on
61
+ // a mismatch passes no handler, and this then does nothing: no call into the container and no
62
+ // cleanup closure either.
63
+ useEffect(() => {
64
+ if (!onKindMismatch)
65
+ return;
66
+ container.setKindMismatchHandler(onKindMismatch);
67
+ return () => container.setKindMismatchHandler(undefined);
68
+ }, [container, onKindMismatch]);
69
+ return container;
70
+ }
71
+ /**
72
+ * Claims a slot for one instance, releasing on unmount. Paint order is not this hook's concern —
73
+ * the container writes it for the whole sibling list in one pass (see {@link useContainer}).
74
+ * @param container - The container holding the slot.
75
+ * @param def - The def this instance renders.
76
+ * @param resource - The per-instance resource backing the slot; a new identity re-claims.
77
+ * @returns A ref holding the current slot, or `-1` while unclaimed. Read it at call time — a
78
+ * re-claim changes the slot without re-rendering.
79
+ */
80
+ export function useSlot(container, def, resource) {
81
+ const slotRef = useRef(-1);
82
+ const invalidate = useInvalidate();
83
+ useEffect(() => {
84
+ const slot = container.claim(def, resource);
85
+ slotRef.current = slot;
86
+ // On a re-claim the instance is already registered, so its state must be rewritten into the new
87
+ // slot. On first mount nothing is registered yet and `useDefSync` performs the initial write.
88
+ syncNow(def);
89
+ invalidate();
90
+ return () => {
91
+ container.release(slot);
92
+ slotRef.current = -1;
93
+ invalidate();
94
+ };
95
+ }, [container, def, resource, invalidate]);
96
+ return slotRef;
97
+ }
98
+ /**
99
+ * Claims a slot for one instance and keeps it in sync with its def: the slot is released on
100
+ * unmount, and on mount and after every `update(def)` the def's visibility and dim are written and —
101
+ * only while it is visible — `write` fills in the rest of the instance's state.
102
+ *
103
+ * The sync is registered as the def's slot writer, so it is also what the camera re-layout frame
104
+ * loop pulls out of `slotWriters` and calls directly — the React path and the per-frame path are
105
+ * one implementation, and both therefore agree about visibility.
106
+ *
107
+ * `hidden` and `dim` live on every def type, so their writes — and the short-circuit `hidden` gates
108
+ * — belong here rather than repeated in each instance component's callback. A hidden instance is
109
+ * never drawn, so computing its transform, color, or glyphs would be wasted; the update that shows
110
+ * it re-runs this sync, which writes the then-current state. Everything type-specific stays in
111
+ * `write`, which is what keeps this hook generic across every def type.
112
+ *
113
+ * **An `update(def)` renders nothing unless the slot's resource moved.** The only thing a render of
114
+ * an instance can do that a sync cannot is re-claim its slot, and that is needed only when the
115
+ * resource backing it changed — a text's line count, a shape's geometry. So the store's snapshot is
116
+ * the resource itself, not a version counter: an update that leaves it alone (a dim, a color, a
117
+ * move) is written by the subscription's own listener, straight into the slot, and React bails out
118
+ * of the render because the snapshot is unchanged. An update that moves it renders, and the render's
119
+ * re-claim writes the def into the new slot.
120
+ * @param container - The container holding the slot.
121
+ * @param def - The def this instance renders.
122
+ * @param resourceOf - Reads the per-instance resource backing the slot off the def; a new identity
123
+ * re-claims. Called on every update, so it must be cheap and return the same identity for an
124
+ * unchanged def. `null` for a container whose slots need no resource, which never re-claims.
125
+ * @param write - Writes the def's type-specific state into its slot. Called only while visible.
126
+ */
127
+ export function useDefSync(container, def, resourceOf, write) {
128
+ // A container without resources is a `SlotContainer<null>`, so `null` is the resource it claims.
129
+ const readResource = (resourceOf ?? readNull);
130
+ const invalidate = useInvalidate();
131
+ // Refreshed after every render below; declared first because the subscription reaches for it.
132
+ const syncRef = useRef(noop);
133
+ const resource = useSyncExternalStore((listener) => subscribeDef(def, () => {
134
+ // This instance's own write rather than `syncNow`, which runs every instance drawing the
135
+ // def: each of them holds a subscription of its own, so `syncNow` here would write a def
136
+ // with N instances N² times.
137
+ syncRef.current();
138
+ invalidate();
139
+ listener();
140
+ }), readResource);
141
+ const slotRef = useSlot(container, def, resource);
142
+ /**
143
+ * Writes the def's current state into its slot: visibility always, the rest only when shown.
144
+ */
145
+ function sync() {
146
+ const slot = slotRef.current;
147
+ if (slot < 0)
148
+ return;
149
+ // The slot was claimed for the resource this render saw. When the def has since moved on to
150
+ // another — more lines than the claimed group holds — the write would overrun it; the render
151
+ // the change triggers re-claims and writes then.
152
+ if (!Object.is(readResource(), resource))
153
+ return;
154
+ const visible = !def.hidden;
155
+ container.setVisibleAt(slot, visible);
156
+ // Written even while hidden, unlike everything in `write`: it is one number, and writing it
157
+ // unconditionally is what makes the slot's dim true of the def at all times rather than only
158
+ // while it draws — including across the re-claim that `useSlot` syncs through.
159
+ container.setDimAt(slot, def.dim);
160
+ if (!visible)
161
+ return;
162
+ write(slot);
163
+ }
164
+ // Held in a ref and refreshed after every render: `sync` closes over a fresh `write` each time,
165
+ // and the effect below must not re-run just because that identity changed — only a new def or a
166
+ // new resource means the slot needs writing from here.
167
+ useEffect(() => {
168
+ syncRef.current = sync;
169
+ });
170
+ useEffect(() => registerSlotWriter(def, () => syncRef.current(), invalidate), [def, invalidate]);
171
+ // The first write, and the one after a re-claim. Declared after the ref refresh above, so it runs
172
+ // the closure that knows the new resource — `useSlot`'s own sync ran the previous render's, which
173
+ // the resource check turned into a no-op. Through the registry, so the frame request that shows it
174
+ // comes with it.
175
+ useEffect(() => {
176
+ syncNow(def);
177
+ }, [def, resource]);
178
+ }
179
+ /**
180
+ * The renderer's frame request, without subscribing to R3F's store for it.
181
+ *
182
+ * `useThree(selector)` subscribes, and every instance holds this hook's result — so with a selector
183
+ * each R3F state change (a canvas re-render re-configures the root) ran one selector per instance,
184
+ * tens of thousands on a large plan, for a value that never changes. The store handle is context
185
+ * alone, and reading `invalidate` at call time also follows a host that replaces it (external mode).
186
+ * @returns A stable function requesting a frame.
187
+ */
188
+ function useInvalidate() {
189
+ const store = useStore();
190
+ const [invalidate] = useState(() => () => store.getState().invalidate());
191
+ return invalidate;
192
+ }
193
+ // The resource of an instance whose container needs none.
194
+ const readNull = () => null;
195
+ // What an instance's sync ref holds before its first render has committed.
196
+ const noop = () => undefined;
@@ -0,0 +1,6 @@
1
+ export { useContainer, useDefSync } from './hooks';
2
+ export { defKey } from './keys';
3
+ export { type ColorWritable, type MatrixWritable, shapeToMatrix, writeColorIfChanged, writeMatrixIfChanged, } from './mesh-writes';
4
+ export { type DefsProps, type InstanceProps } from './props';
5
+ export { type BatchedMeshRenderItem, type RenderListItem, SlotContainer, type SlotContainerOptions, type SlotMesh, } from './SlotContainer';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export { useContainer, useDefSync } from './hooks';
2
+ export { defKey } from './keys';
3
+ export { shapeToMatrix, writeColorIfChanged, writeMatrixIfChanged, } from './mesh-writes';
4
+ export { SlotContainer, } from './SlotContainer';
@@ -0,0 +1,10 @@
1
+ import { type RenderableDef } from '../../types/defs';
2
+ /**
3
+ * A stable React key for a def. Defs carry no id, and their array position shifts as siblings are
4
+ * added or removed, so identity is the only durable key — which is also what lets React mount and
5
+ * unmount exactly the defs that entered or left a layer.
6
+ * @param def - The def to key.
7
+ * @returns The def's key, allocated on first use.
8
+ */
9
+ export declare function defKey(def: RenderableDef): string;
10
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1,18 @@
1
+ /** Keys already handed out, so a def keeps the same key for its whole life. */
2
+ const keys = new WeakMap();
3
+ let nextKey = 0;
4
+ /**
5
+ * A stable React key for a def. Defs carry no id, and their array position shifts as siblings are
6
+ * added or removed, so identity is the only durable key — which is also what lets React mount and
7
+ * unmount exactly the defs that entered or left a layer.
8
+ * @param def - The def to key.
9
+ * @returns The def's key, allocated on first use.
10
+ */
11
+ export function defKey(def) {
12
+ let key = keys.get(def);
13
+ if (key === undefined) {
14
+ key = `def-${nextKey++}`;
15
+ keys.set(def, key);
16
+ }
17
+ return key;
18
+ }
@@ -0,0 +1,106 @@
1
+ import { type Shape } from '@expofp/geometry';
2
+ import { Matrix4, Vector4 } from 'three';
3
+ import { type ColorInput } from '../../types/defs';
4
+ import { type Logger } from '../../utils/logging';
5
+ /**
6
+ * A mesh whose instances carry a transform.
7
+ *
8
+ * Named as a capability rather than listed as a union of our mesh classes, because not every batch
9
+ * has one: `BatchedLinesImpl` places its members by their endpoints and has no per-instance matrix
10
+ * at all, yet still writes colors through the helper below. A class that satisfies only one of
11
+ * these two interfaces is the point — the alternative was no-op matrix methods on a class that has
12
+ * no transforms, which would read as a capability it does not have.
13
+ *
14
+ * Implementations flag their own buffer uploads and invalidate their own bounds inside
15
+ * `setMatrixAt`, so no caller has to.
16
+ */
17
+ export interface MatrixWritable {
18
+ /**
19
+ * Reads an instance's transform.
20
+ * @param slot - The instance to read.
21
+ * @param target - Receives the result.
22
+ */
23
+ getMatrixAt(slot: number, target: Matrix4): unknown;
24
+ /**
25
+ * Writes an instance's transform.
26
+ * @param slot - The instance to write.
27
+ * @param matrix - The transform.
28
+ */
29
+ setMatrixAt(slot: number, matrix: Matrix4): unknown;
30
+ }
31
+ /**
32
+ * A mesh whose instances carry an RGBA color, alpha in the fourth channel.
33
+ *
34
+ * `BatchedMesh` types this pair for `Color | Vector4` and our vendored classes widen it the same
35
+ * way, so a `Vector4` is what every implementation routes to its own opacity storage.
36
+ */
37
+ export interface ColorWritable {
38
+ /**
39
+ * Reads an instance's color.
40
+ * @param slot - The instance to read.
41
+ * @param target - Receives the result, alpha in `.w`.
42
+ */
43
+ getColorAt(slot: number, target: Vector4): unknown;
44
+ /**
45
+ * Writes an instance's color.
46
+ * @param slot - The instance to write.
47
+ * @param color - The color, alpha in `.w`.
48
+ */
49
+ setColorAt(slot: number, color: Vector4): unknown;
50
+ }
51
+ /** A def carrying a color of its own. */
52
+ interface ColoredDef {
53
+ /** The def's color, as a css string or packed number. */
54
+ color: ColorInput;
55
+ }
56
+ /**
57
+ * Composes the instance transform a shape draws with, into the shared scratch matrix.
58
+ *
59
+ * A box or rect is drawn as the shared unit plane, so its transform carries the whole shape: the
60
+ * plane is scaled to the shape's size, spun about z by its rotation, and moved to its center. A
61
+ * mesh already carries absolute coordinates in its own geometry, including a z per vertex, so it
62
+ * draws at identity. A polygon is the one in between: its vertices are 2D and its `elevation` is a
63
+ * single scalar beside them, so that scalar is the only part of its placement the geometry cannot
64
+ * hold and the transform lifts it. An image's `bounds` is a rect, so it composes here too and then
65
+ * post-multiplies its own origin and tile offsets onto the result.
66
+ *
67
+ * The result **is the shared scratch** — valid until the next call, which is all any caller needs:
68
+ * a write composes, optionally adjusts, and uploads within one synchronous statement. Handing it
69
+ * back rather than taking a target keeps every instance type off its own private matrix.
70
+ * @param shape - The shape supplying the transform.
71
+ * @returns The scratch matrix, composed.
72
+ */
73
+ export declare function shapeToMatrix(shape: Shape): Matrix4;
74
+ /**
75
+ * Writes a transform into an instance, skipping the upload when nothing moved.
76
+ *
77
+ * The compare-and-skip is what keeps a def that only changed color from paying for a matrix
78
+ * upload and a bounds invalidation: a color-only `update(def)` re-runs the whole sync, and an
79
+ * unchanged transform costs sixteen comparisons instead.
80
+ * @param mesh - The mesh holding the instance.
81
+ * @param slot - The instance's slot.
82
+ * @param matrix - The transform to write.
83
+ * @returns Whether the matrix was written.
84
+ */
85
+ export declare function writeMatrixIfChanged(mesh: MatrixWritable, slot: number, matrix: Matrix4): boolean;
86
+ /**
87
+ * Parses a def's color and writes it into an instance, skipping the upload when it did not change
88
+ * — the color counterpart to {@link writeMatrixIfChanged}, so a def that only moved does not pay
89
+ * for a color upload either.
90
+ *
91
+ * Alpha rides in the fourth channel, which every mesh routes to its own opacity storage.
92
+ *
93
+ * A color that does not parse warns and leaves the instance untouched rather than throwing: a bad
94
+ * color is a bad def, not a broken renderer, and the sync that found it has other writes to finish.
95
+ * Naming the def in the warning is why this takes the whole def rather than the color alone — it is
96
+ * the only thing that makes the report actionable.
97
+ * @param mesh - The mesh holding the instance.
98
+ * @param slot - The instance's slot.
99
+ * @param def - The def supplying the color, and named in the warning.
100
+ * @param logger - Reports an unparsable color, so the warning names the instance component.
101
+ * @returns Whether the color was written; `false` both for one that did not change and for one that
102
+ * did not parse.
103
+ */
104
+ export declare function writeColorIfChanged(mesh: ColorWritable, slot: number, def: ColoredDef, logger: Logger): boolean;
105
+ export {};
106
+ //# sourceMappingURL=mesh-writes.d.ts.map
@@ -0,0 +1,97 @@
1
+ import { isBox, isRect } from '@expofp/geometry';
2
+ import { Color, Matrix4, Quaternion, SRGBColorSpace, Vector3, Vector4 } from 'three';
3
+ import { normalizeColor } from '../../utils/colors';
4
+ /**
5
+ * Composes the instance transform a shape draws with, into the shared scratch matrix.
6
+ *
7
+ * A box or rect is drawn as the shared unit plane, so its transform carries the whole shape: the
8
+ * plane is scaled to the shape's size, spun about z by its rotation, and moved to its center. A
9
+ * mesh already carries absolute coordinates in its own geometry, including a z per vertex, so it
10
+ * draws at identity. A polygon is the one in between: its vertices are 2D and its `elevation` is a
11
+ * single scalar beside them, so that scalar is the only part of its placement the geometry cannot
12
+ * hold and the transform lifts it. An image's `bounds` is a rect, so it composes here too and then
13
+ * post-multiplies its own origin and tile offsets onto the result.
14
+ *
15
+ * The result **is the shared scratch** — valid until the next call, which is all any caller needs:
16
+ * a write composes, optionally adjusts, and uploads within one synchronous statement. Handing it
17
+ * back rather than taking a target keeps every instance type off its own private matrix.
18
+ * @param shape - The shape supplying the transform.
19
+ * @returns The scratch matrix, composed.
20
+ */
21
+ export function shapeToMatrix(shape) {
22
+ if (!isBox(shape) && !isRect(shape)) {
23
+ // A `Mesh` has no `elevation` at all, so this is identity for one; a `Polygon` is lifted onto
24
+ // its plane. Zero composes to identity either way, so the common case costs nothing.
25
+ return _matrix.makeTranslation(0, 0, 'elevation' in shape ? shape.elevation : 0);
26
+ }
27
+ // A Box is a pure 2D AABB — it carries neither field — so both default to zero.
28
+ const elevation = 'elevation' in shape ? shape.elevation : 0;
29
+ const rotation = 'rotation' in shape ? shape.rotation : 0;
30
+ _position.set(shape.center.x, shape.center.y, elevation);
31
+ _quaternion.setFromAxisAngle(Z_AXIS, rotation);
32
+ _scale.set(shape.size.x, shape.size.y, 1);
33
+ return _matrix.compose(_position, _quaternion, _scale);
34
+ }
35
+ /**
36
+ * Writes a transform into an instance, skipping the upload when nothing moved.
37
+ *
38
+ * The compare-and-skip is what keeps a def that only changed color from paying for a matrix
39
+ * upload and a bounds invalidation: a color-only `update(def)` re-runs the whole sync, and an
40
+ * unchanged transform costs sixteen comparisons instead.
41
+ * @param mesh - The mesh holding the instance.
42
+ * @param slot - The instance's slot.
43
+ * @param matrix - The transform to write.
44
+ * @returns Whether the matrix was written.
45
+ */
46
+ export function writeMatrixIfChanged(mesh, slot, matrix) {
47
+ mesh.getMatrixAt(slot, _previousMatrix);
48
+ if (_previousMatrix.equals(matrix))
49
+ return false;
50
+ mesh.setMatrixAt(slot, matrix);
51
+ return true;
52
+ }
53
+ /**
54
+ * Parses a def's color and writes it into an instance, skipping the upload when it did not change
55
+ * — the color counterpart to {@link writeMatrixIfChanged}, so a def that only moved does not pay
56
+ * for a color upload either.
57
+ *
58
+ * Alpha rides in the fourth channel, which every mesh routes to its own opacity storage.
59
+ *
60
+ * A color that does not parse warns and leaves the instance untouched rather than throwing: a bad
61
+ * color is a bad def, not a broken renderer, and the sync that found it has other writes to finish.
62
+ * Naming the def in the warning is why this takes the whole def rather than the color alone — it is
63
+ * the only thing that makes the report actionable.
64
+ * @param mesh - The mesh holding the instance.
65
+ * @param slot - The instance's slot.
66
+ * @param def - The def supplying the color, and named in the warning.
67
+ * @param logger - Reports an unparsable color, so the warning names the instance component.
68
+ * @returns Whether the color was written; `false` both for one that did not change and for one that
69
+ * did not parse.
70
+ */
71
+ export function writeColorIfChanged(mesh, slot, def, logger) {
72
+ const rgb = normalizeColor(def.color);
73
+ if (!rgb) {
74
+ logger.warn(`Invalid color: ${String(def.color)} %O`, def);
75
+ return false;
76
+ }
77
+ _color.setRGB(rgb.r / 255, rgb.g / 255, rgb.b / 255, SRGBColorSpace);
78
+ _colorWithAlpha.set(_color.r, _color.g, _color.b, rgb.a);
79
+ mesh.getColorAt(slot, _previousColor);
80
+ if (_previousColor.equals(_colorWithAlpha))
81
+ return false;
82
+ mesh.setColorAt(slot, _colorWithAlpha);
83
+ return true;
84
+ }
85
+ // Scratch shared by every write. Each is filled and consumed within a single synchronous
86
+ // statement, so a write costs buffer uploads and no allocation. `_matrix` is the one callers see,
87
+ // through `shapeToMatrix`. The `_previous*` values are only ever read back and compared inside the
88
+ // write helpers, so a caller still holding `_matrix` never collides with them.
89
+ const _position = new Vector3();
90
+ const _quaternion = new Quaternion();
91
+ const _scale = new Vector3();
92
+ const _matrix = new Matrix4();
93
+ const _previousMatrix = new Matrix4();
94
+ const _color = new Color();
95
+ const _colorWithAlpha = new Vector4();
96
+ const _previousColor = new Vector4();
97
+ const Z_AXIS = new Vector3(0, 0, 1);
@@ -0,0 +1,33 @@
1
+ import { type RenderableDef } from '../../types/defs';
2
+ /** Props for a component drawing one layer's defs. */
3
+ export interface DefsProps<T extends RenderableDef> {
4
+ /**
5
+ * The defs to draw, in paint order. Layer-level concerns (`hidden`, `dim`, pointer handlers)
6
+ * belong to `<Layer>`, not here.
7
+ *
8
+ * A **new array identity means the collection may have changed** — that is the whole contract.
9
+ * Callers must not pass a layer's `children` directly: it is mutated in place, so its identity
10
+ * never changes and a memoizing compiler would keep serving a stale instance list. Pass the
11
+ * version-keyed snapshot from `useStagedChildren`, whose identity changes exactly when the layer is
12
+ * updated.
13
+ */
14
+ defs: readonly T[];
15
+ /**
16
+ * How many of `defs` may mount their instances now, counted from the start of the list — the
17
+ * staged-mount slice, from `useStagedChildren`. Defaults to all of them, which is what a caller
18
+ * mounting a layer outside the scheduler wants.
19
+ *
20
+ * Only the instances are sliced. The container is still built, sized and paint-ordered from the
21
+ * whole of `defs`, so a growing prefix claims into buffers that were allocated once, and the
22
+ * order pass that labels them runs per membership change rather than per slice.
23
+ */
24
+ mounted?: number;
25
+ }
26
+ /** Props for a component keeping one def's instance in sync. */
27
+ export interface InstanceProps<T extends RenderableDef, C> {
28
+ /** The def this instance draws. */
29
+ def: T;
30
+ /** The container holding the instance. */
31
+ container: C;
32
+ }
33
+ //# sourceMappingURL=props.d.ts.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { type ImageDef } from '../../types/defs';
2
+ import { type InstanceProps } from '../containers';
3
+ import { type PackedImage } from './atlas-packer';
4
+ import { type ImageContainer } from './image-container';
5
+ /** Props for {@link ImageInstance}. */
6
+ export interface ImageInstanceProps extends InstanceProps<ImageDef, ImageContainer> {
7
+ /**
8
+ * The atlas placement this instance draws — the whole image, or one tile of a split one. A def
9
+ * whose source was cut across atlases has several instances, one per {@link PackedImage}.
10
+ */
11
+ packed: PackedImage;
12
+ }
13
+ /**
14
+ * Keeps one image placement current: claims a slot on mount (where the container writes the fixed
15
+ * uv region), writes the def's transform and visibility on mount and after every `update(def)`, and
16
+ * releases the slot on unmount.
17
+ *
18
+ * Renders no scene node — the mesh belongs to the container. Only the transform lives here:
19
+ * visibility is `useDefSync`'s, the uv region is written once at claim (a def's source cannot
20
+ * change), and color has no analogue for a textured quad. Memoized for the same reason as
21
+ * `ShapeInstance`: a structural change hands the atlas a fresh placement list, rebuilding every
22
+ * instance element, so only prop-equality keeps the unchanged siblings from re-rendering. Both props
23
+ * are identity-stable across a membership's lifetime — a change repacks into a fresh container and
24
+ * fresh placements — so the bailout holds.
25
+ * @param props - The def, its placement, and its container.
26
+ * @returns `null`; the instance lives in the mesh, not the scene graph.
27
+ */
28
+ export declare const ImageInstance: import("react").MemoExoticComponent<(props: ImageInstanceProps) => null>;
29
+ //# sourceMappingURL=ImageInstance.d.ts.map