@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,93 @@
1
+ import { use, useEffect } from 'react';
2
+ import { useGui } from '../../debug/debug-context';
3
+ import { isLayerDefs } from '../../types/guards';
4
+ import { childrenOf, subscribeDef, update } from '../../update';
5
+ import { MountSchedulerContext } from './mount-scheduler';
6
+ /**
7
+ * Adds a visibility checkbox for one layer: labeled with the layer's name, checked while the
8
+ * layer is visible. Toggling flips `layer.hidden` and calls `update(layer)` — exactly the write an
9
+ * SDK caller would make, so the checkbox exercises the real update path. The controller binds a
10
+ * getter/setter proxy over the def and `listen()`s, so a `hidden` change made elsewhere shows up
11
+ * in the checkbox too.
12
+ * @param folder - The folder to add the checkbox to.
13
+ * @param layer - The layer the checkbox controls.
14
+ */
15
+ function addVisibilityToggle(folder, layer) {
16
+ const proxy = {
17
+ get visible() {
18
+ return !layer.hidden;
19
+ },
20
+ set visible(value) {
21
+ layer.hidden = !value;
22
+ update(layer);
23
+ },
24
+ };
25
+ folder.add(proxy, 'visible').name(layer.name).listen();
26
+ }
27
+ /**
28
+ * Returns a sync function that rebuilds `folder`'s checkboxes when the listed layers changed since
29
+ * the last call, and is a cheap no-op otherwise — safe to subscribe to chatty sources (every
30
+ * `update()` on a layer, every scheduler admission) without churning the GUI.
31
+ * @param folder - The folder holding the checkboxes.
32
+ * @param list - Reads the layers to list.
33
+ * @returns The sync function.
34
+ */
35
+ function makeSyncToggles(folder, list) {
36
+ let shown = [];
37
+ return () => {
38
+ const layers = list();
39
+ if (layers.length === shown.length && layers.every((layer, i) => layer === shown[i]))
40
+ return;
41
+ shown = layers;
42
+ for (const controller of [...folder.controllers])
43
+ controller.destroy();
44
+ for (const layer of layers)
45
+ addVisibilityToggle(folder, layer);
46
+ };
47
+ }
48
+ /**
49
+ * Builds the layer-visibility debug GUI: called by the tree's root `Layer` alone, a no-op without
50
+ * a debug GUI in context. Two folders, one checkbox per layer:
51
+ *
52
+ * - **Main layers** — the root's direct child layers, in child order, kept current through the
53
+ * same store subscription `update()` drives (a root membership change rebuilds the rows).
54
+ * - **Content layers** — every leaf (shape) layer in the tree, in the mount scheduler's
55
+ * registration order, which is the flattened leaf order. No new bookkeeping tracks the leaves:
56
+ * `useStagedMount` already registers each one with the scheduler, so this folder mirrors the
57
+ * scheduler's queue and follows leaves as they mount and unmount. Without a scheduler in
58
+ * context (a plain `<Layer>` mount outside `Scene`) the folder stays empty.
59
+ *
60
+ * The effect runs entirely against the defs and the GUI — toggles never re-render the layer tree
61
+ * beyond what `update()` itself triggers.
62
+ * @param layer - The tree's root layer def.
63
+ */
64
+ export function useVisibilityGui(layer) {
65
+ const gui = useGui();
66
+ const scheduler = use(MountSchedulerContext);
67
+ // This effect runs after every descendant's, so by first sync the scheduler's queue already
68
+ // holds the whole initial tree's leaves.
69
+ useEffect(() => {
70
+ if (!gui)
71
+ return undefined;
72
+ const main = gui.addFolder('Main layers');
73
+ const syncMain = makeSyncToggles(main, () => {
74
+ const children = childrenOf(layer);
75
+ return isLayerDefs(children) ? children : [];
76
+ });
77
+ syncMain();
78
+ const unsubscribeMain = subscribeDef(layer, syncMain);
79
+ const content = gui.addFolder('Content layers');
80
+ let unsubscribeContent;
81
+ if (scheduler) {
82
+ const syncContent = makeSyncToggles(content, () => scheduler.leaves());
83
+ syncContent();
84
+ unsubscribeContent = scheduler.subscribe(syncContent);
85
+ }
86
+ return () => {
87
+ unsubscribeMain();
88
+ unsubscribeContent?.();
89
+ main.destroy();
90
+ content.destroy();
91
+ };
92
+ }, [gui, scheduler, layer]);
93
+ }
@@ -0,0 +1,21 @@
1
+ import { type LineDef } from '../../types/defs';
2
+ import { type InstanceProps } from '../containers';
3
+ import { type LineContainer } from './line-container';
4
+ /** Props for {@link LineInstance}. */
5
+ export type LineInstanceProps = InstanceProps<LineDef, LineContainer>;
6
+ /**
7
+ * Keeps one line's instance current: claims a slot on mount, writes the def's endpoints, color,
8
+ * width and visibility on mount and after every `update(def)`, and releases the slot on unmount.
9
+ *
10
+ * Renders no scene node — the batch belongs to the container. Nothing here can force a re-claim:
11
+ * unlike a shape, whose geometry is its slot's resource, a line is the same segment template
12
+ * wherever it runs, so moving one is an ordinary write and its slot survives.
13
+ *
14
+ * Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
15
+ * `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
16
+ * siblings from re-rendering. Correctness never depends on it.
17
+ * @param props - The def and its container.
18
+ * @returns `null`; the instance lives in the batch, not the scene graph.
19
+ */
20
+ export declare const LineInstance: import("react").MemoExoticComponent<(props: LineInstanceProps) => null>;
21
+ //# sourceMappingURL=LineInstance.d.ts.map
@@ -0,0 +1,73 @@
1
+ import { memo } from 'react';
2
+ import { Vector3 } from 'three';
3
+ import { createLogger } from '../../utils/logging';
4
+ import { useDefSync, writeColorIfChanged } from '../containers';
5
+ const logger = createLogger('line-instance');
6
+ /**
7
+ * Keeps one line's instance current: claims a slot on mount, writes the def's endpoints, color,
8
+ * width and visibility on mount and after every `update(def)`, and releases the slot on unmount.
9
+ *
10
+ * Renders no scene node — the batch belongs to the container. Nothing here can force a re-claim:
11
+ * unlike a shape, whose geometry is its slot's resource, a line is the same segment template
12
+ * wherever it runs, so moving one is an ordinary write and its slot survives.
13
+ *
14
+ * Memoized for the same reason as `ShapeInstance`: a structural change hands the layer a fresh
15
+ * `defs` snapshot, so element-identity bailout never applies and only prop equality keeps unchanged
16
+ * siblings from re-rendering. Correctness never depends on it.
17
+ * @param props - The def and its container.
18
+ * @returns `null`; the instance lives in the batch, not the scene graph.
19
+ */
20
+ export const LineInstance = memo(function LineInstance(props) {
21
+ const { def, container } = props;
22
+ useDefSync(container, def, null, (slot) => {
23
+ writeLinePoints(container, slot, def);
24
+ writeColorIfChanged(container.mesh, slot, def, logger);
25
+ writeLineWidth(container, slot, def);
26
+ });
27
+ return null;
28
+ });
29
+ /**
30
+ * Writes a line's endpoints into its instance.
31
+ *
32
+ * A `LineLike` is 2D plus an optional elevation, which is what places the segment on its layer's
33
+ * plane; the batch compares and skips, so a def that only changed color pays two vector fills.
34
+ * @param container - The container holding the instance.
35
+ * @param slot - The instance's slot.
36
+ * @param def - The line def supplying the endpoints.
37
+ */
38
+ function writeLinePoints(container, slot, def) {
39
+ const { p0, p1, elevation = 0 } = def.points;
40
+ _start.set(p0.x, p0.y, elevation);
41
+ _end.set(p1.x, p1.y, elevation);
42
+ // Compared before writing, like every other instance write: a color-only `update(def)` re-runs the
43
+ // whole sync, and an unchanged segment costs six comparisons instead of two uniform uploads and a
44
+ // bounds invalidation.
45
+ container.mesh.getPointsAt(slot, _heldStart, _heldEnd);
46
+ if (_heldStart.equals(_start) && _heldEnd.equals(_end))
47
+ return;
48
+ container.mesh.setPointsAt(slot, _start, _end);
49
+ }
50
+ /**
51
+ * Writes a line's width into its instance, skipping the upload when it did not change.
52
+ *
53
+ * Clamped at zero, which is where the def's contract and the batch's part ways: the batch reads a
54
+ * negative width as "this member has none of its own" and falls back to the material's, an
55
+ * affordance for callers that never set one. A `LineDef` always carries a width, so a negative one
56
+ * is bad input rather than a request to inherit — and it must not come out thicker than a zero.
57
+ * @param container - The container holding the instance.
58
+ * @param slot - The instance's slot.
59
+ * @param def - The line def supplying the width.
60
+ */
61
+ function writeLineWidth(container, slot, def) {
62
+ const width = Math.max(def.width, 0);
63
+ if (container.mesh.getWidthAt(slot) === width)
64
+ return;
65
+ container.mesh.setWidthAt(slot, width);
66
+ }
67
+ // Scratch shared by every endpoint write: filled and consumed within a single synchronous
68
+ // statement, so a write costs one buffer upload and no allocation. The `_held*` pair is only ever
69
+ // read back and compared, so it never collides with the endpoints being written.
70
+ const _start = new Vector3();
71
+ const _end = new Vector3();
72
+ const _heldStart = new Vector3();
73
+ const _heldEnd = new Vector3();
@@ -0,0 +1,17 @@
1
+ import { type LineDef } from '../../types/defs';
2
+ import { type DefsProps } from '../containers';
3
+ /** Props for {@link Lines}. */
4
+ export type LinesProps = DefsProps<LineDef>;
5
+ /**
6
+ * Draws a collection of lines as members of one `BatchedLines`, so a layer of route segments
7
+ * renders in a single instanced draw.
8
+ *
9
+ * Membership is reconciled by React exactly as it is for shapes: a new `defs` array re-renders this
10
+ * component and React mounts or unmounts only the delta, with no differ of our own. Order is applied
11
+ * as it is for images — the container labels each slot with its def's place in the layer, and the
12
+ * batch's `customSort` orders the draw list by it, so a reorder writes nothing to the GPU.
13
+ * @param props - The lines to draw.
14
+ * @returns The batch and one instance component per line.
15
+ */
16
+ export declare function Lines(props: LinesProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=Lines.d.ts.map
@@ -0,0 +1,29 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { BatchedLines } from '../../core/lines/BatchedLines';
3
+ import { defKey, useContainer } from '../containers';
4
+ import { LineMaterial } from '../materials/LineMaterial';
5
+ import { LineContainer } from './line-container';
6
+ import { LineInstance } from './LineInstance';
7
+ /**
8
+ * Draws a collection of lines as members of one `BatchedLines`, so a layer of route segments
9
+ * renders in a single instanced draw.
10
+ *
11
+ * Membership is reconciled by React exactly as it is for shapes: a new `defs` array re-renders this
12
+ * component and React mounts or unmounts only the delta, with no differ of our own. Order is applied
13
+ * as it is for images — the container labels each slot with its def's place in the layer, and the
14
+ * batch's `customSort` orders the draw list by it, so a reorder writes nothing to the GPU.
15
+ * @param props - The lines to draw.
16
+ * @returns The batch and one instance component per line.
17
+ */
18
+ export function Lines(props) {
19
+ const { defs } = props;
20
+ // Sized from the first render's defs; membership changes grow the batch in place, never rebuild
21
+ // it. The container is bookkeeping only, so the copy StrictMode's double-invoked initializer
22
+ // discards is plain garbage.
23
+ const container = useContainer(() => new LineContainer(defs), defs);
24
+ // `args` is diffed element-wise and a change would dispose and rebuild the batch, orphaning every
25
+ // claimed slot — it is a frozen snapshot, so that never happens. The `ref` arrow is
26
+ // deliberate: `ref={container.bind}` would make the React Compiler infer `container` itself as a
27
+ // ref and reject its render-time reads.
28
+ return (_jsxs(_Fragment, { children: [_jsx(BatchedLines, { args: container.args, ref: (mesh) => container.bind(mesh), customSort: container.customSort, children: _jsx(LineMaterial, {}) }), defs.map((def) => (_jsx(LineInstance, { def: def, container: container }, defKey(def))))] }));
29
+ }
@@ -0,0 +1,2 @@
1
+ export { Lines, type LinesProps } from './Lines';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ export { Lines } from './Lines';
@@ -0,0 +1,35 @@
1
+ import { type InstancedMeshExtParams } from '../../core/instanced/InstancedMesh';
2
+ import { type BatchedLinesExt } from '../../core/lines/BatchedLines';
3
+ import { type LineDef } from '../../types/defs';
4
+ import { SlotContainer } from '../containers';
5
+ /**
6
+ * The constructor tuple R3F builds a line batch from, passed as `<BatchedLines args={…}>`. R3F diffs
7
+ * `args` element-wise and rebuilds the object when any element changes, so this is snapshotted once
8
+ * and never mutated.
9
+ *
10
+ * Only the options: the batch builds its own segment template and takes its material from the
11
+ * `<LineMaterial>` child R3F mounts under it.
12
+ */
13
+ export type LineMeshArgs = [params: InstancedMeshExtParams];
14
+ /**
15
+ * One line layer's bookkeeping.
16
+ *
17
+ * Thinner than the shape container and the image one alike, because a line has no per-instance
18
+ * resource to allocate against — every member draws the same segment template, placed by its own
19
+ * endpoints. That is why the resource type is `null` here: nothing about a `LineDef` can force a
20
+ * re-claim, so a def keeps its slot for as long as it is mounted, `points` included.
21
+ */
22
+ export declare class LineContainer extends SlotContainer<null, BatchedLinesExt, LineDef> {
23
+ /** The constructor args R3F builds the batch from; a frozen snapshot, never mutated. */
24
+ readonly args: LineMeshArgs;
25
+ /**
26
+ * Builds a line layer's bookkeeping, sized to hold `defs` exactly so a layer that mounts full
27
+ * never reallocates. The batch itself is R3F's — the component renders `<BatchedLines>` and hands
28
+ * it over through {@link SlotContainer.bind} — so R3F disposes it on unmount.
29
+ * @param defs - The layer's lines, used only to size the initial capacity.
30
+ */
31
+ constructor(defs: readonly LineDef[]);
32
+ protected addInstance(): number;
33
+ protected removeInstance(slot: number): void;
34
+ }
35
+ //# sourceMappingURL=line-container.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { SlotContainer } from '../containers';
2
+ /**
3
+ * One line layer's bookkeeping.
4
+ *
5
+ * Thinner than the shape container and the image one alike, because a line has no per-instance
6
+ * resource to allocate against — every member draws the same segment template, placed by its own
7
+ * endpoints. That is why the resource type is `null` here: nothing about a `LineDef` can force a
8
+ * re-claim, so a def keeps its slot for as long as it is mounted, `points` included.
9
+ */
10
+ export class LineContainer extends SlotContainer {
11
+ /** The constructor args R3F builds the batch from; a frozen snapshot, never mutated. */
12
+ args;
13
+ /**
14
+ * Builds a line layer's bookkeeping, sized to hold `defs` exactly so a layer that mounts full
15
+ * never reallocates. The batch itself is R3F's — the component renders `<BatchedLines>` and hands
16
+ * it over through {@link SlotContainer.bind} — so R3F disposes it on unmount.
17
+ * @param defs - The layer's lines, used only to size the initial capacity.
18
+ */
19
+ constructor(defs) {
20
+ // Reversed, and that is the material's doing rather than this layer's: a line batch stencils
21
+ // first-fragment-wins so translucent lines do not darken where they overlap, which hands the
22
+ // pixel to whichever member rasterizes *first*. Draw order is therefore the reverse of paint
23
+ // order. Slots are still labeled in paint order, so "last sibling on top" holds here as
24
+ // everywhere else.
25
+ super({ reverse: true });
26
+ this.args = [{ capacity: Math.max(defs.length, 1) }];
27
+ }
28
+ addInstance() {
29
+ // The batch grows its own buffers if the layer ever outgrows the capacity it was sized to, so
30
+ // there is nothing to pre-scan; the id is captured from the creation callback.
31
+ let slot = -1;
32
+ this.mesh.addInstances(1, (id) => {
33
+ slot = id;
34
+ });
35
+ return slot;
36
+ }
37
+ removeInstance(slot) {
38
+ this.mesh.removeInstances(slot);
39
+ }
40
+ }
@@ -0,0 +1,85 @@
1
+ import { type ColorRepresentation, type WebGLProgramParametersWithUniforms } from 'three';
2
+ /** Props for {@link ColorMaterial}. */
3
+ export interface ColorMaterialProps {
4
+ /** Base color. Defaults to white, which is what a per-instance tint multiplies against. */
5
+ color?: ColorRepresentation;
6
+ /**
7
+ * Whether this batch draws **volumes** — meshes carrying real z, which need shading to read as
8
+ * solid and depth testing to occlude themselves and each other. Defaults to `false`, the flat
9
+ * content that is every 2D primitive in a plan.
10
+ */
11
+ solid?: boolean;
12
+ /**
13
+ * Whether those volumes are translucent, which moves them into three's transparent pass. Only
14
+ * meaningful together with {@link solid}; flat content never uses it (see the note below on why 2D
15
+ * stays in the opaque list).
16
+ */
17
+ translucent?: boolean;
18
+ /**
19
+ * Runs on the shader before the dim patch does — the slot for a caller's own shader surgery,
20
+ * like the background quad's clip-space projection and its dim-uniform stand-in. Captured at
21
+ * first render and fixed for the material's lifetime, like every other axis of this component:
22
+ * a changing handler identity would risk a recompile per render.
23
+ */
24
+ onBeforeCompile?: (parameters: WebGLProgramParametersWithUniforms) => void;
25
+ }
26
+ /**
27
+ * The material a shape batch draws with, rendered as a child of the mesh so R3F attaches, updates,
28
+ * and disposes it. Also troika's base material for text (see `Texts`), which never asks for `solid`.
29
+ *
30
+ * One component with a branch rather than two files, because the two are the same thing along one
31
+ * axis — what a batch of shapes draws with — and the three differences are the whole point, best
32
+ * read side by side:
33
+ *
34
+ * | | flat (default) | `solid` |
35
+ * | --- | --- | --- |
36
+ * | base | `MeshBasicMaterial`, unlit | `MeshPhysicalMaterial`, lit |
37
+ * | `depthWrite` | off, from {@link sharedParams} | **on** |
38
+ * | polygon offset | on, from {@link sharedParams} | **off** |
39
+ *
40
+ * Each difference is load-bearing. A volume is the one kind of content that writes depth: that is how
41
+ * its own faces resolve against each other, and what lets 2D content drawn afterwards be occluded by
42
+ * it. It carries no polygon offset because solids are what everything else is biased *against* —
43
+ * offsetting them too would only move the problem.
44
+ *
45
+ * `ior: 1` and `specularIntensity: 0` drive the lit material's `F0` to zero, so no specular lobe sits
46
+ * on top of the diffuse term. That is what makes the lighting identity exact: with the two lights
47
+ * summing to intensity `π`, a face square-on to the camera renders at precisely the authored color
48
+ * (see `Lighting`). A default `MeshPhysicalMaterial` would put a sheen on every wall and push it
49
+ * above its albedo.
50
+ *
51
+ * Per-instance color arrives through `BatchedMesh.setColorAt` either way: three injects the
52
+ * batching-color chunk for whatever material the batch draws with.
53
+ *
54
+ * **`translucent` is the one place the array-order contract bends, and it is deliberate.** It sets
55
+ * `transparent: true`, which moves the batch into three's transparent list — drawn after *every*
56
+ * opaque object in the scene, whatever `renderOrder` says (`WebGLRenderer.renderScene`). That is
57
+ * exactly what makes a translucent volume blend with the opaque content behind it even when that
58
+ * content lives in a later layer, which no position in the ordered stream can achieve: a translucent
59
+ * surface can only blend with what is already in the framebuffer.
60
+ *
61
+ * What it costs is the reverse case. A planar def sitting *on* a translucent volume — a label or logo
62
+ * on a glass roof — draws in the opaque pass and so lands **before** it: the label composites against
63
+ * whatever was under the glass rather than onto it, and the volume then glazes over the label. So a
64
+ * translucent solid is right about what is behind it and wrong about what is on it. Flat content is
65
+ * never given this flag for the same reason, plus one of its own: 2D is coplanar, so it has no depth
66
+ * order to sort by and order is all it has.
67
+ *
68
+ * `forceSinglePass` is switched off here alone. three renders a `DoubleSide` transparent material
69
+ * twice — back faces, then front (`WebGLRenderer.js:1348`) — which is a correctly ordered two-pass
70
+ * for free, and the reason a closed translucent volume does not blend its own far and near surfaces
71
+ * in whatever order the batch happened to pack them.
72
+ *
73
+ * Depth writes stay **on** even when translucent, which is not three's usual convention. Two rules
74
+ * depend on it: a translucent volume must still be occluded by anything nearer, and a farther one
75
+ * drawn later must not paint over it. MapLibre makes the same choice for `fill-extrusion`
76
+ * (`DepthMode.ReadWrite` in both of its passes). The trade and the two rejected alternatives are
77
+ * recorded in the design spec's Decision 2.
78
+ *
79
+ * The branch is stable for a mounted batch — `Shapes` renders a separate element per kind, each with
80
+ * a constant `solid` — so R3F never swaps one base class for the other under a live mesh.
81
+ * @param props - The material's base color, and whether it draws volumes.
82
+ * @returns The material element.
83
+ */
84
+ export declare function ColorMaterial(props: ColorMaterialProps): import("react").JSX.Element;
85
+ //# sourceMappingURL=ColorMaterial.d.ts.map
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useDimPatch } from './dim';
4
+ import { sharedParams } from './shared';
5
+ /**
6
+ * The material a shape batch draws with, rendered as a child of the mesh so R3F attaches, updates,
7
+ * and disposes it. Also troika's base material for text (see `Texts`), which never asks for `solid`.
8
+ *
9
+ * One component with a branch rather than two files, because the two are the same thing along one
10
+ * axis — what a batch of shapes draws with — and the three differences are the whole point, best
11
+ * read side by side:
12
+ *
13
+ * | | flat (default) | `solid` |
14
+ * | --- | --- | --- |
15
+ * | base | `MeshBasicMaterial`, unlit | `MeshPhysicalMaterial`, lit |
16
+ * | `depthWrite` | off, from {@link sharedParams} | **on** |
17
+ * | polygon offset | on, from {@link sharedParams} | **off** |
18
+ *
19
+ * Each difference is load-bearing. A volume is the one kind of content that writes depth: that is how
20
+ * its own faces resolve against each other, and what lets 2D content drawn afterwards be occluded by
21
+ * it. It carries no polygon offset because solids are what everything else is biased *against* —
22
+ * offsetting them too would only move the problem.
23
+ *
24
+ * `ior: 1` and `specularIntensity: 0` drive the lit material's `F0` to zero, so no specular lobe sits
25
+ * on top of the diffuse term. That is what makes the lighting identity exact: with the two lights
26
+ * summing to intensity `π`, a face square-on to the camera renders at precisely the authored color
27
+ * (see `Lighting`). A default `MeshPhysicalMaterial` would put a sheen on every wall and push it
28
+ * above its albedo.
29
+ *
30
+ * Per-instance color arrives through `BatchedMesh.setColorAt` either way: three injects the
31
+ * batching-color chunk for whatever material the batch draws with.
32
+ *
33
+ * **`translucent` is the one place the array-order contract bends, and it is deliberate.** It sets
34
+ * `transparent: true`, which moves the batch into three's transparent list — drawn after *every*
35
+ * opaque object in the scene, whatever `renderOrder` says (`WebGLRenderer.renderScene`). That is
36
+ * exactly what makes a translucent volume blend with the opaque content behind it even when that
37
+ * content lives in a later layer, which no position in the ordered stream can achieve: a translucent
38
+ * surface can only blend with what is already in the framebuffer.
39
+ *
40
+ * What it costs is the reverse case. A planar def sitting *on* a translucent volume — a label or logo
41
+ * on a glass roof — draws in the opaque pass and so lands **before** it: the label composites against
42
+ * whatever was under the glass rather than onto it, and the volume then glazes over the label. So a
43
+ * translucent solid is right about what is behind it and wrong about what is on it. Flat content is
44
+ * never given this flag for the same reason, plus one of its own: 2D is coplanar, so it has no depth
45
+ * order to sort by and order is all it has.
46
+ *
47
+ * `forceSinglePass` is switched off here alone. three renders a `DoubleSide` transparent material
48
+ * twice — back faces, then front (`WebGLRenderer.js:1348`) — which is a correctly ordered two-pass
49
+ * for free, and the reason a closed translucent volume does not blend its own far and near surfaces
50
+ * in whatever order the batch happened to pack them.
51
+ *
52
+ * Depth writes stay **on** even when translucent, which is not three's usual convention. Two rules
53
+ * depend on it: a translucent volume must still be occluded by anything nearer, and a farther one
54
+ * drawn later must not paint over it. MapLibre makes the same choice for `fill-extrusion`
55
+ * (`DepthMode.ReadWrite` in both of its passes). The trade and the two rejected alternatives are
56
+ * recorded in the design spec's Decision 2.
57
+ *
58
+ * The branch is stable for a mounted batch — `Shapes` renders a separate element per kind, each with
59
+ * a constant `solid` — so R3F never swaps one base class for the other under a live mesh.
60
+ * @param props - The material's base color, and whether it draws volumes.
61
+ * @returns The material element.
62
+ */
63
+ export function ColorMaterial(props) {
64
+ // Read-then-default rather than a destructuring default: the React Compiler cannot lower
65
+ // `const { color = 'white' } = props` (BuildHIR::lowerAssignment, AssignmentPattern), and
66
+ // floorplan's dev build runs the compiler with `panicThreshold: 'all_errors'`, which turns that
67
+ // into a hard 500 for the whole app. Storybook's pass defaults to `panicThreshold: 'none'` and
68
+ // silently skips the component instead, so only the running app surfaces it.
69
+ const color = props.color ?? 'white';
70
+ // Before the branch, because it is a hook — and passed to both, because a volume dims exactly as
71
+ // flat content does: the patch runs on whatever `gl_FragColor` the material arrived at, which for
72
+ // a lit material is the shaded color.
73
+ const dimPatch = useDimPatch();
74
+ // The caller's surgery first, then dimming — so a pre-patch that declares the dim uniform's
75
+ // stand-in is already in the shader when the dim patch looks for it. One stable handler, for the
76
+ // same reason `useDimPatch` keeps its own.
77
+ const [onBeforeCompile] = useState(() => {
78
+ const pre = props.onBeforeCompile;
79
+ return (parameters) => {
80
+ pre?.(parameters);
81
+ dimPatch(parameters);
82
+ };
83
+ });
84
+ if (props.solid) {
85
+ const translucent = props.translucent ?? false;
86
+ return (_jsx("meshPhysicalMaterial", { ...sharedParams, color: color, depthWrite: true, polygonOffset: false, ior: 1, specularIntensity: 0, transparent: translucent, forceSinglePass: !translucent, onBeforeCompile: onBeforeCompile }));
87
+ }
88
+ return _jsx("meshBasicMaterial", { ...sharedParams, color: color, onBeforeCompile: onBeforeCompile });
89
+ }
@@ -0,0 +1,44 @@
1
+ import { type ColorRepresentation } from 'three';
2
+ /** Props for {@link LineMaterial}. */
3
+ export interface LineMaterialProps {
4
+ /** Base color. Defaults to white, which is what a per-instance tint multiplies against. */
5
+ color?: ColorRepresentation;
6
+ /**
7
+ * Width every member without one of its own draws at, in screen-space pixels — `BatchedLinesExt`
8
+ * resolves an unset per-member width to this. Defaults to 1.
9
+ */
10
+ width?: number;
11
+ }
12
+ /**
13
+ * The material line batches draw with, rendered as a child of the batch so R3F attaches, updates,
14
+ * and disposes it.
15
+ *
16
+ * Three's own, configured — it knows nothing about batching. What makes one of these draw members
17
+ * that differ in width and tint is a patch the batch installs on whatever material it is handed
18
+ * (`core/lines/lines-material`), so this stays a material a plain `LineSegments2` could draw with.
19
+ *
20
+ * `worldUnits` is left at three's default, so `width` is screen-space and a line keeps its
21
+ * thickness as the floorplan zooms. `depthFunc` and the blending come from the shared params, the
22
+ * same as every other 2D layer: order decides overlap, not depth.
23
+ *
24
+ * **The stencil is what keeps translucent lines from darkening where they overlap.** Alpha blending
25
+ * composites per fragment, so anywhere two line quads cover the same pixel — a route's shared
26
+ * endpoints, where two round caps land on top of each other, or two lines simply crossing — the
27
+ * color is blended twice and reads darker than either line. The three settings below make the first
28
+ * fragment to reach a pixel the only one that draws:
29
+ *
30
+ * - `stencilFunc: NotEqual` against {@link LINE_STENCIL_REF} passes only where the layer has not
31
+ * drawn yet.
32
+ * - `stencilZPass: Replace` stamps the ref as it passes, so the next fragment there fails.
33
+ * - `stencilWrite` turns both on.
34
+ *
35
+ * Two consequences worth knowing. Lines within a layer never blend with *each other* — only the
36
+ * winner shows — so a line that must blend over another belongs in its own layer. And "first
37
+ * fragment wins" is why a line batch is drawn back to front: the one that paints on top has to
38
+ * rasterize first. Between layers nothing changes, because the clear below hands each
39
+ * batch a fresh stencil, so ordinary blending and occlusion still apply across them.
40
+ * @param props - The material's base color and fallback width.
41
+ * @returns The material element.
42
+ */
43
+ export declare function LineMaterial(props: LineMaterialProps): import("react").JSX.Element;
44
+ //# sourceMappingURL=LineMaterial.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { extend } from '@react-three/fiber';
3
+ import { NotEqualStencilFunc, ReplaceStencilOp, } from 'three';
4
+ import { LineMaterial as LineMaterialBase } from 'three/addons/lines/LineMaterial.js';
5
+ import { useDimPatch } from './dim';
6
+ import { sharedParams } from './shared';
7
+ /**
8
+ * The stencil value a line layer stamps on every pixel it covers.
9
+ *
10
+ * `255` is deliberate rather than arbitrary. In the MapLibre embed the SDK renders into the map's
11
+ * own canvas and GL context, so this is *MapLibre's* stencil buffer: it assigns tile clipping ids
12
+ * counting up from zero, and the floorplan draws last, so taking the top of the range keeps the two
13
+ * out of each other's way.
14
+ */
15
+ const LINE_STENCIL_REF = 255;
16
+ /**
17
+ * Registers three's line material as an R3F element, by value so the generated key cannot collide
18
+ * with anything three puts in the catalog when the canvas mounts.
19
+ */
20
+ const LineMaterialElement = extend(LineMaterialBase);
21
+ /**
22
+ * The material line batches draw with, rendered as a child of the batch so R3F attaches, updates,
23
+ * and disposes it.
24
+ *
25
+ * Three's own, configured — it knows nothing about batching. What makes one of these draw members
26
+ * that differ in width and tint is a patch the batch installs on whatever material it is handed
27
+ * (`core/lines/lines-material`), so this stays a material a plain `LineSegments2` could draw with.
28
+ *
29
+ * `worldUnits` is left at three's default, so `width` is screen-space and a line keeps its
30
+ * thickness as the floorplan zooms. `depthFunc` and the blending come from the shared params, the
31
+ * same as every other 2D layer: order decides overlap, not depth.
32
+ *
33
+ * **The stencil is what keeps translucent lines from darkening where they overlap.** Alpha blending
34
+ * composites per fragment, so anywhere two line quads cover the same pixel — a route's shared
35
+ * endpoints, where two round caps land on top of each other, or two lines simply crossing — the
36
+ * color is blended twice and reads darker than either line. The three settings below make the first
37
+ * fragment to reach a pixel the only one that draws:
38
+ *
39
+ * - `stencilFunc: NotEqual` against {@link LINE_STENCIL_REF} passes only where the layer has not
40
+ * drawn yet.
41
+ * - `stencilZPass: Replace` stamps the ref as it passes, so the next fragment there fails.
42
+ * - `stencilWrite` turns both on.
43
+ *
44
+ * Two consequences worth knowing. Lines within a layer never blend with *each other* — only the
45
+ * winner shows — so a line that must blend over another belongs in its own layer. And "first
46
+ * fragment wins" is why a line batch is drawn back to front: the one that paints on top has to
47
+ * rasterize first. Between layers nothing changes, because the clear below hands each
48
+ * batch a fresh stencil, so ordinary blending and occlusion still apply across them.
49
+ * @param props - The material's base color and fallback width.
50
+ * @returns The material element.
51
+ */
52
+ export function LineMaterial(props) {
53
+ // Read-then-default rather than destructuring defaults, which the React Compiler cannot lower —
54
+ // see `ColorMaterial` for what that costs.
55
+ const color = props.color ?? 'white';
56
+ const width = props.width ?? 1;
57
+ const onBeforeCompile = useDimPatch();
58
+ /**
59
+ * Hands the batch about to draw a clean stencil to stamp.
60
+ *
61
+ * The stamps are per *layer*, not per frame: without this, a batch drawn later would be rejected
62
+ * everywhere an earlier one already covered, and a route's animated overlay would not appear over
63
+ * the route beneath it. Clearing here rather than in the batch keeps it beside the settings that
64
+ * make it necessary — this material is what each line layer mounts its own copy of, so "once per
65
+ * batch, before it draws" falls out of where the callback lives.
66
+ * @param renderer - The renderer about to draw the batch.
67
+ */
68
+ function onBeforeRender(renderer) {
69
+ renderer.clearStencil();
70
+ }
71
+ return (_jsx(LineMaterialElement, { ...sharedParams, color: color, linewidth: width, stencilWrite: true, stencilFunc: NotEqualStencilFunc, stencilZPass: ReplaceStencilOp, stencilRef: LINE_STENCIL_REF, onBeforeRender: onBeforeRender, onBeforeCompile: onBeforeCompile }));
72
+ }