@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,194 @@
1
+ /**
2
+ * Picking: which def the pointer is on, and where on the plan.
3
+ *
4
+ * Two hooks are the way in — {@link usePointerProps} makes a layer's group interactive, and
5
+ * {@link usePickFilter} makes the canvas pick the way the renderer draws. Nothing outside this module
6
+ * calls anything else here; the rest is exported so the sibling spec can drive each piece on its own
7
+ * rather than through a mounted canvas.
8
+ */
9
+ import { type RootState, type ThreeEvent } from '@react-three/fiber';
10
+ import { type Intersection, type Object3D, type Ray, type Raycaster } from 'three';
11
+ import { type PlanPoint, type Space } from '../space/Space';
12
+ import { type LayerDef, type RenderableDef } from '../types/defs';
13
+ import { type PointerHandlerName } from '../types/events';
14
+ /**
15
+ * What picking asks of a mesh's owner, in the owner's own terms — instances and defs, never
16
+ * intersections. Pulling the instance out of an intersection is this module's work, because the
17
+ * intersection is three's; a container only answers about what it allocated.
18
+ *
19
+ * One interface rather than two registrations, because both answers have one owner and arrive at one
20
+ * moment. `SlotContainer` satisfies it as it stands, so a container registers itself.
21
+ */
22
+ export interface HitSource {
23
+ /**
24
+ * The def a mesh instance belongs to, or `undefined` when none does — an instance released between
25
+ * the raycast and the dispatch, or a mesh whose picking is not implemented.
26
+ * @param instance - The instance the mesh reported.
27
+ * @returns The def.
28
+ */
29
+ defAt(instance: number | undefined): RenderableDef | undefined;
30
+ /**
31
+ * Where a def sits in this mesh's draw order, counted from the one drawn first.
32
+ * @param def - The def to place.
33
+ * @returns Its place in the draw order.
34
+ */
35
+ paintOrderOf(def: RenderableDef): number;
36
+ }
37
+ /** The picking props {@link usePointerProps} builds for a layer's `<group>`. */
38
+ export interface PointerGroupProps {
39
+ /** Refuses the raycast traversal into the subtree while the layer is off screen. */
40
+ raycast(): false | undefined;
41
+ /** Dispatches a click in the layer's subtree; attached only when the def declares one. */
42
+ onClick?: (event: ThreeEvent<MouseEvent>) => void;
43
+ /** Dispatches a move over the layer's subtree; attached only when the def declares one. */
44
+ onPointerMove?: (event: ThreeEvent<MouseEvent>) => void;
45
+ /** Dispatches the pointer leaving a def; attached only when the def declares one. */
46
+ onPointerOut?: (event: ThreeEvent<MouseEvent>) => void;
47
+ }
48
+ /**
49
+ * The props that make a layer's group interactive: one dispatching handler per handler the def
50
+ * declares, and the raycast gate that un-picks the layer while it is off screen.
51
+ *
52
+ * Presence is the contract, in both directions. R3F raycasts only objects carrying handlers — and
53
+ * raycasts them with their whole subtree — so a handler is attached exactly when the def declares
54
+ * one and the layer is on screen: a layer wanting clicks alone must not be raycast on every pointer
55
+ * move, and a layer that is hidden, or under something hidden, must not be raycast at all. Which
56
+ * function a handler currently is stays out of it: dispatch looks the handler up by name on the def
57
+ * at event time, so swapping one takes effect with no render.
58
+ *
59
+ * Handler presence is read here as values (`layer.onClick !== undefined`) rather than through
60
+ * `layer` inside a callback: memoization compares what a render *read*, and a property reached only
61
+ * from inside a function is not that — a compiled cache keyed on the def object, whose identity
62
+ * never changes, would keep serving a group with no handler attached.
63
+ *
64
+ * The `raycast` gate covers the direction handler absence cannot: an interactive *ancestor* is
65
+ * raycast with everything beneath it, and `false` is three's own signal to stop descending —
66
+ * `Raycaster`'s `intersect()` keeps walking a subtree only while each object's `raycast` returns
67
+ * something other than `false`. Visibility is read at raycast time rather than swapped per render,
68
+ * since R3F ignores a prop set to `undefined` and a blocking version would stick.
69
+ * @param layer - The layer the group renders.
70
+ * @param visible - Whether the layer is on screen, ancestors included.
71
+ * @returns Props to spread onto the layer's `<group>`.
72
+ */
73
+ export declare function usePointerProps(layer: LayerDef, visible: boolean): PointerGroupProps;
74
+ /**
75
+ * Makes the canvas pick the way the renderer draws, for as long as the component is mounted.
76
+ *
77
+ * R3F's `events.filter` is the one hook into picking that sees the whole hit list, which is why both
78
+ * corrections live there: {@link occludeHits} drops what hidden geometry stands in front of, and
79
+ * {@link orderCoplanarHits} settles what the depth buffer never ordered. Both are the renderer's own
80
+ * reading of its scene, so the whole arrangement is here rather than at whichever component mounts
81
+ * it — a caller wires picking by calling this, and nothing outside this module has to know that there
82
+ * are two halves or which order they run in.
83
+ */
84
+ export declare function usePickFilter(): void;
85
+ /**
86
+ * Hands one of R3F's intersection events to a layer's handler, as the narrow event the public API
87
+ * describes: the def that was hit, and where the pointer sits on the plan plane.
88
+ *
89
+ * Two hits are dropped rather than reported. One the renderer cannot trace to a def — a released
90
+ * slot, or a mesh whose picking is not implemented — because a handler has nothing to act on without
91
+ * it. And a click that ends a drag: R3F fires `onClick` on release whenever the press landed on the
92
+ * same object, which on a pannable plan would select whatever the pan started over.
93
+ *
94
+ * The handler is called with no receiver, so a handler shared across layers reads `event.def` rather
95
+ * than the layer it was declared on.
96
+ * @param layer - The layer whose handler should run.
97
+ * @param name - Which handler to run.
98
+ * @param event - R3F's intersection event.
99
+ * @param space - The scene's space, which the event's world coordinates are reported through.
100
+ */
101
+ export declare function dispatchPointer(layer: LayerDef, name: PointerHandlerName, event: ThreeEvent<MouseEvent>, space: Space): void;
102
+ /**
103
+ * Registers what speaks for a mesh's intersections, for as long as the mesh is mounted — so the
104
+ * dispatch and ordering paths stay free of per-mesh-type knowledge.
105
+ * @param mesh - The mesh whose hits the source speaks for.
106
+ * @param source - What answers for them.
107
+ * @returns An unregister function, safe to call after another source replaced this one.
108
+ */
109
+ export declare function registerHitSource(mesh: Object3D, source: HitSource): () => void;
110
+ /**
111
+ * The def an intersection hit.
112
+ *
113
+ * A mesh with no registered source reports nothing, which is how a primitive whose picking is not
114
+ * implemented behaves as if its layer were not interactive: the hit is dropped and no handler runs.
115
+ * @param hit - The intersection to trace.
116
+ * @returns The def, or `undefined` when the hit cannot be traced to one.
117
+ */
118
+ export declare function resolveHit(hit: Intersection): RenderableDef | undefined;
119
+ /**
120
+ * Where a missed click landed, in plan units.
121
+ *
122
+ * **A miss is not an empty canvas.** It means no handler took the click: a layer declaring no
123
+ * pointer handlers is never raycast by R3F in the first place, so the pointer may be sitting
124
+ * squarely on a wall, a model or a booth that simply does not listen. Searching the scene here is
125
+ * what makes the reported position agree with what the user is looking at — the same answer a hit
126
+ * event carries, arrived at a moment later and with no def to name.
127
+ *
128
+ * The search prunes hidden content for free: {@link usePointerProps} refuses the traversal into a
129
+ * hidden layer's subtree, which is three's own signal to stop descending.
130
+ * @param raycaster - The scene's raycaster, holding the ray R3F cast for this click.
131
+ * @param scene - The scene to search.
132
+ * @param space - The scene's space.
133
+ * @returns The nearest geometry under the pointer, or — where there is none — the floor beneath it.
134
+ */
135
+ export declare function missedPoint(raycaster: Raycaster, scene: Object3D, space: Space): PlanPoint;
136
+ /**
137
+ * Drops the hits that geometry with no handlers hides, so a booth behind a wall or a model, opaque
138
+ * or translucent, is not picked. The first half of R3F's `events.filter`, which runs on its
139
+ * nearest-first hits; {@link orderCoplanarHits} is the second, over what survives.
140
+ *
141
+ * R3F raycasts only interactive layers, so it cannot see what stands in front of them. This casts
142
+ * the same ray at the whole scene and cuts the hits at the nearest object no interactive layer
143
+ * holds. A wall and a logo on a booth's roof differ only in distance: the logo lies on the roof, and
144
+ * the wall stands apart from it. So the cut keeps a hit up to {@link depthSeparation} behind the
145
+ * occluder, the least separation at which the depth buffer draws two surfaces in order. Nearer
146
+ * than that, the occluder is drawn on the hit, not in front of it. A click with every hit dropped is
147
+ * a miss, as a click on the occluder was before.
148
+ * @param hits - R3F's hits, nearest first.
149
+ * @param state - The root's state, holding the ray already aimed at the pointer.
150
+ * @param space - The scene's space, whose scale turns the plan-unit separation into a world one.
151
+ * @returns The hits in front of the nearest occluder.
152
+ */
153
+ export declare function occludeHits(hits: Intersection[], state: RootState, space: Space): Intersection[];
154
+ /**
155
+ * Reorders the hits the depth buffer never separated, so the one drawn last is picked first. R3F's
156
+ * `events.filter`, after {@link occludeHits} has cut away what is hidden.
157
+ *
158
+ * Three sorts its intersections by distance alone, and two coplanar polygons are not at two
159
+ * distances — they are at one distance the ray-triangle math resolves a little differently for each
160
+ * of them. Which one R3F dispatches first is that float noise, so a click on the overlap selects
161
+ * one shape on one frame and its neighbor on the next. Sorting stably does not help: the distances
162
+ * genuinely differ rather than tying, and the order three produced them in is a mesh's free list,
163
+ * which `SlotContainer` keeps deliberately apart from paint order.
164
+ *
165
+ * So the tie is settled by what the user is already looking at. Hits within
166
+ * {@link depthSeparation} of the nearest of their run are one depth, because that is the separation
167
+ * below which the depth buffer stops ordering surfaces and draw order alone decides what is on top
168
+ * — the same rule {@link occludeHits} cuts by. Within a run they are ordered by where they draw,
169
+ * last first, along the three keys three's own painter sort reads in the same order: the layer
170
+ * group they hang under, the batch's place in that layer, then the instance's place in the batch.
171
+ *
172
+ * A run is measured from its own first hit rather than from the hit before it, so a stack of
173
+ * surfaces each just inside the separation is still cut into runs instead of chaining into one.
174
+ * @param hits - R3F's hits, nearest first.
175
+ * @param space - The scene's space, whose scale turns the plan-unit separation into a world one.
176
+ * @returns The hits in pick order.
177
+ */
178
+ export declare function orderCoplanarHits(hits: Intersection[], space: Space): Intersection[];
179
+ /**
180
+ * Where a ray crosses the plan plane — the floor under the pointer, whatever is drawn above it.
181
+ *
182
+ * The fallback for a click that reached neither a handler nor any geometry at all: past the edge of
183
+ * the plan there is nothing to report but the ground the ray carries on to.
184
+ *
185
+ * Both the crossing and the fallback are taken through the scene's space, because R3F casts its rays
186
+ * in world coordinates and a scene fitted to the canvas draws a plan unit as some other number of
187
+ * those. Without it the reported position is the world one wearing plan units' name.
188
+ * @param ray - The ray the pointer cast, in world space.
189
+ * @param space - The scene's space.
190
+ * @returns The crossing, in plan units, or — for a ray running parallel to the plan, which crosses
191
+ * it nowhere — where the ray started.
192
+ */
193
+ export declare function planePoint(ray: Ray, space: Space): PlanPoint;
194
+ //# sourceMappingURL=pointer.d.ts.map
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Picking: which def the pointer is on, and where on the plan.
3
+ *
4
+ * Two hooks are the way in — {@link usePointerProps} makes a layer's group interactive, and
5
+ * {@link usePickFilter} makes the canvas pick the way the renderer draws. Nothing outside this module
6
+ * calls anything else here; the rest is exported so the sibling spec can drive each piece on its own
7
+ * rather than through a mounted canvas.
8
+ */
9
+ import { useThree } from '@react-three/fiber';
10
+ import { useEffect } from 'react';
11
+ import { depthSeparation } from '../space/clipping';
12
+ import { useSpace } from '../space/Space';
13
+ import { hotState } from '../utils/hot-state';
14
+ /**
15
+ * The props that make a layer's group interactive: one dispatching handler per handler the def
16
+ * declares, and the raycast gate that un-picks the layer while it is off screen.
17
+ *
18
+ * Presence is the contract, in both directions. R3F raycasts only objects carrying handlers — and
19
+ * raycasts them with their whole subtree — so a handler is attached exactly when the def declares
20
+ * one and the layer is on screen: a layer wanting clicks alone must not be raycast on every pointer
21
+ * move, and a layer that is hidden, or under something hidden, must not be raycast at all. Which
22
+ * function a handler currently is stays out of it: dispatch looks the handler up by name on the def
23
+ * at event time, so swapping one takes effect with no render.
24
+ *
25
+ * Handler presence is read here as values (`layer.onClick !== undefined`) rather than through
26
+ * `layer` inside a callback: memoization compares what a render *read*, and a property reached only
27
+ * from inside a function is not that — a compiled cache keyed on the def object, whose identity
28
+ * never changes, would keep serving a group with no handler attached.
29
+ *
30
+ * The `raycast` gate covers the direction handler absence cannot: an interactive *ancestor* is
31
+ * raycast with everything beneath it, and `false` is three's own signal to stop descending —
32
+ * `Raycaster`'s `intersect()` keeps walking a subtree only while each object's `raycast` returns
33
+ * something other than `false`. Visibility is read at raycast time rather than swapped per render,
34
+ * since R3F ignores a prop set to `undefined` and a blocking version would stick.
35
+ * @param layer - The layer the group renders.
36
+ * @param visible - Whether the layer is on screen, ancestors included.
37
+ * @returns Props to spread onto the layer's `<group>`.
38
+ */
39
+ export function usePointerProps(layer, visible) {
40
+ // Pointer positions are reported on the plan, and R3F casts its rays in world coordinates.
41
+ const space = useSpace();
42
+ const handlesClick = visible && layer.onClick !== undefined;
43
+ const handlesPointerMove = visible && layer.onPointerMove !== undefined;
44
+ const handlesPointerOut = visible && layer.onPointerOut !== undefined;
45
+ function handler(name) {
46
+ return (event) => dispatchPointer(layer, name, event, space);
47
+ }
48
+ return {
49
+ raycast: () => (visible ? undefined : false),
50
+ onClick: handlesClick ? handler('onClick') : undefined,
51
+ onPointerMove: handlesPointerMove ? handler('onPointerMove') : undefined,
52
+ onPointerOut: handlesPointerOut ? handler('onPointerOut') : undefined,
53
+ };
54
+ }
55
+ /**
56
+ * Makes the canvas pick the way the renderer draws, for as long as the component is mounted.
57
+ *
58
+ * R3F's `events.filter` is the one hook into picking that sees the whole hit list, which is why both
59
+ * corrections live there: {@link occludeHits} drops what hidden geometry stands in front of, and
60
+ * {@link orderCoplanarHits} settles what the depth buffer never ordered. Both are the renderer's own
61
+ * reading of its scene, so the whole arrangement is here rather than at whichever component mounts
62
+ * it — a caller wires picking by calling this, and nothing outside this module has to know that there
63
+ * are two halves or which order they run in.
64
+ */
65
+ export function usePickFilter() {
66
+ const setEvents = useThree((state) => state.setEvents);
67
+ // Picking is reported on the plan, and R3F casts its rays in world coordinates.
68
+ const space = useSpace();
69
+ useEffect(() => {
70
+ setEvents({
71
+ filter: (hits, state) => {
72
+ // Cut first, then order: dropping what is hidden cannot move the survivors along the ray,
73
+ // and ordering hits that were about to be dropped is work for nothing.
74
+ const visible = occludeHits(hits, state, space);
75
+ return orderCoplanarHits(visible, space);
76
+ },
77
+ });
78
+ }, [setEvents, space]);
79
+ }
80
+ /**
81
+ * Hands one of R3F's intersection events to a layer's handler, as the narrow event the public API
82
+ * describes: the def that was hit, and where the pointer sits on the plan plane.
83
+ *
84
+ * Two hits are dropped rather than reported. One the renderer cannot trace to a def — a released
85
+ * slot, or a mesh whose picking is not implemented — because a handler has nothing to act on without
86
+ * it. And a click that ends a drag: R3F fires `onClick` on release whenever the press landed on the
87
+ * same object, which on a pannable plan would select whatever the pan started over.
88
+ *
89
+ * The handler is called with no receiver, so a handler shared across layers reads `event.def` rather
90
+ * than the layer it was declared on.
91
+ * @param layer - The layer whose handler should run.
92
+ * @param name - Which handler to run.
93
+ * @param event - R3F's intersection event.
94
+ * @param space - The scene's space, which the event's world coordinates are reported through.
95
+ */
96
+ export function dispatchPointer(layer, name, event, space) {
97
+ const handler = layer[name];
98
+ if (!handler)
99
+ return;
100
+ if (name === 'onClick' && isDrag(event))
101
+ return;
102
+ const def = resolveHit(event);
103
+ if (!def)
104
+ return;
105
+ handler({
106
+ def,
107
+ // The ray's own intersection with the geometry, which three computed against the real
108
+ // triangles — so an elevated def reports the point on *it*, elevation and all, rather than the
109
+ // ground the ray carries on to behind it. R3F reports it in world coordinates; the cast is the
110
+ // assertion of which space that is.
111
+ point: space.worldToPlan(event.point),
112
+ stopPropagation: () => event.stopPropagation(),
113
+ nativeEvent: event.nativeEvent,
114
+ });
115
+ }
116
+ /**
117
+ * Registers what speaks for a mesh's intersections, for as long as the mesh is mounted — so the
118
+ * dispatch and ordering paths stay free of per-mesh-type knowledge.
119
+ * @param mesh - The mesh whose hits the source speaks for.
120
+ * @param source - What answers for them.
121
+ * @returns An unregister function, safe to call after another source replaced this one.
122
+ */
123
+ export function registerHitSource(mesh, source) {
124
+ sources.set(mesh, source);
125
+ return () => {
126
+ if (sources.get(mesh) === source)
127
+ sources.delete(mesh);
128
+ };
129
+ }
130
+ /**
131
+ * The def an intersection hit.
132
+ *
133
+ * A mesh with no registered source reports nothing, which is how a primitive whose picking is not
134
+ * implemented behaves as if its layer were not interactive: the hit is dropped and no handler runs.
135
+ * @param hit - The intersection to trace.
136
+ * @returns The def, or `undefined` when the hit cannot be traced to one.
137
+ */
138
+ export function resolveHit(hit) {
139
+ return sources.get(hit.object)?.defAt(hit.instanceId);
140
+ }
141
+ /**
142
+ * Where a missed click landed, in plan units.
143
+ *
144
+ * **A miss is not an empty canvas.** It means no handler took the click: a layer declaring no
145
+ * pointer handlers is never raycast by R3F in the first place, so the pointer may be sitting
146
+ * squarely on a wall, a model or a booth that simply does not listen. Searching the scene here is
147
+ * what makes the reported position agree with what the user is looking at — the same answer a hit
148
+ * event carries, arrived at a moment later and with no def to name.
149
+ *
150
+ * The search prunes hidden content for free: {@link usePointerProps} refuses the traversal into a
151
+ * hidden layer's subtree, which is three's own signal to stop descending.
152
+ * @param raycaster - The scene's raycaster, holding the ray R3F cast for this click.
153
+ * @param scene - The scene to search.
154
+ * @param space - The scene's space.
155
+ * @returns The nearest geometry under the pointer, or — where there is none — the floor beneath it.
156
+ */
157
+ export function missedPoint(raycaster, scene, space) {
158
+ // Sorted nearest-first by three, so the first hit is the one the user is looking at.
159
+ const [nearest] = raycaster.intersectObject(scene, true);
160
+ if (nearest)
161
+ return space.worldToPlan(nearest.point);
162
+ return planePoint(raycaster.ray, space);
163
+ }
164
+ /**
165
+ * Drops the hits that geometry with no handlers hides, so a booth behind a wall or a model, opaque
166
+ * or translucent, is not picked. The first half of R3F's `events.filter`, which runs on its
167
+ * nearest-first hits; {@link orderCoplanarHits} is the second, over what survives.
168
+ *
169
+ * R3F raycasts only interactive layers, so it cannot see what stands in front of them. This casts
170
+ * the same ray at the whole scene and cuts the hits at the nearest object no interactive layer
171
+ * holds. A wall and a logo on a booth's roof differ only in distance: the logo lies on the roof, and
172
+ * the wall stands apart from it. So the cut keeps a hit up to {@link depthSeparation} behind the
173
+ * occluder, the least separation at which the depth buffer draws two surfaces in order. Nearer
174
+ * than that, the occluder is drawn on the hit, not in front of it. A click with every hit dropped is
175
+ * a miss, as a click on the occluder was before.
176
+ * @param hits - R3F's hits, nearest first.
177
+ * @param state - The root's state, holding the ray already aimed at the pointer.
178
+ * @param space - The scene's space, whose scale turns the plan-unit separation into a world one.
179
+ * @returns The hits in front of the nearest occluder.
180
+ */
181
+ export function occludeHits(hits, state, space) {
182
+ if (hits.length === 0)
183
+ return hits;
184
+ const { interaction } = state.internal;
185
+ const occluder = state.raycaster
186
+ .intersectObject(state.scene, true)
187
+ .find((hit) => !isInteractive(hit.object, interaction));
188
+ if (!occluder)
189
+ return hits;
190
+ // Along the ray, a pitched view only lengthens the gap between two surfaces, never shortens it.
191
+ const cut = occluder.distance + depthSeparation(space.scale);
192
+ return hits.filter((hit) => hit.distance <= cut);
193
+ }
194
+ /**
195
+ * Reorders the hits the depth buffer never separated, so the one drawn last is picked first. R3F's
196
+ * `events.filter`, after {@link occludeHits} has cut away what is hidden.
197
+ *
198
+ * Three sorts its intersections by distance alone, and two coplanar polygons are not at two
199
+ * distances — they are at one distance the ray-triangle math resolves a little differently for each
200
+ * of them. Which one R3F dispatches first is that float noise, so a click on the overlap selects
201
+ * one shape on one frame and its neighbor on the next. Sorting stably does not help: the distances
202
+ * genuinely differ rather than tying, and the order three produced them in is a mesh's free list,
203
+ * which `SlotContainer` keeps deliberately apart from paint order.
204
+ *
205
+ * So the tie is settled by what the user is already looking at. Hits within
206
+ * {@link depthSeparation} of the nearest of their run are one depth, because that is the separation
207
+ * below which the depth buffer stops ordering surfaces and draw order alone decides what is on top
208
+ * — the same rule {@link occludeHits} cuts by. Within a run they are ordered by where they draw,
209
+ * last first, along the three keys three's own painter sort reads in the same order: the layer
210
+ * group they hang under, the batch's place in that layer, then the instance's place in the batch.
211
+ *
212
+ * A run is measured from its own first hit rather than from the hit before it, so a stack of
213
+ * surfaces each just inside the separation is still cut into runs instead of chaining into one.
214
+ * @param hits - R3F's hits, nearest first.
215
+ * @param space - The scene's space, whose scale turns the plan-unit separation into a world one.
216
+ * @returns The hits in pick order.
217
+ */
218
+ export function orderCoplanarHits(hits, space) {
219
+ if (hits.length < 2)
220
+ return hits;
221
+ const separation = depthSeparation(space.scale);
222
+ const ordered = [];
223
+ for (let start = 0; start < hits.length;) {
224
+ let end = start + 1;
225
+ while (end < hits.length) {
226
+ const gap = hits[end].distance - hits[start].distance;
227
+ // A step *back* toward the camera ends the run rather than extending it. R3F sorts by event
228
+ // priority first and distance second, so a second root's hits follow the first root's as their
229
+ // own nearest-first list — two scenes, whose hits must not be ordered against each other
230
+ // however near the numbers land.
231
+ if (gap < 0 || gap > separation)
232
+ break;
233
+ end++;
234
+ }
235
+ const run = hits.slice(start, end);
236
+ // Reversed, so the hit drawn last comes first. `sort` is stable, so hits nothing here orders —
237
+ // a primitive whose picking is not implemented, two triangles of one instance — keep the
238
+ // nearest-first order three gave them rather than being shuffled by a comparator with nothing
239
+ // to say about them.
240
+ if (run.length > 1)
241
+ run.sort((a, b) => compareDrawOrder(b, a));
242
+ ordered.push(...run);
243
+ start = end;
244
+ }
245
+ return ordered;
246
+ }
247
+ /**
248
+ * Where a ray crosses the plan plane — the floor under the pointer, whatever is drawn above it.
249
+ *
250
+ * The fallback for a click that reached neither a handler nor any geometry at all: past the edge of
251
+ * the plan there is nothing to report but the ground the ray carries on to.
252
+ *
253
+ * Both the crossing and the fallback are taken through the scene's space, because R3F casts its rays
254
+ * in world coordinates and a scene fitted to the canvas draws a plan unit as some other number of
255
+ * those. Without it the reported position is the world one wearing plan units' name.
256
+ * @param ray - The ray the pointer cast, in world space.
257
+ * @param space - The scene's space.
258
+ * @returns The crossing, in plan units, or — for a ray running parallel to the plan, which crosses
259
+ * it nowhere — where the ray started.
260
+ */
261
+ export function planePoint(ray, space) {
262
+ return space.rayToPlan(ray) ?? space.worldToPlan(ray.origin);
263
+ }
264
+ /**
265
+ * Whether a press-and-release traveled far enough to be a drag rather than a click.
266
+ *
267
+ * R3F's `delta` is the pointer's travel since the press, rounded to whole pixels.
268
+ * @param event - The click event to judge.
269
+ * @returns Whether to treat it as a drag.
270
+ */
271
+ function isDrag(event) {
272
+ return event.delta * event.delta > DRAG_SLOP_SQ_PX;
273
+ }
274
+ /**
275
+ * Compares two hits by where they draw, earliest first.
276
+ *
277
+ * The three keys are three's own, read in three's order: a mesh inherits the `renderOrder` of its
278
+ * nearest ancestor group as its group order, which outranks its own `renderOrder`, which outranks
279
+ * anything within the mesh. Here that reads as the layer, then the batch, then the instance.
280
+ *
281
+ * **The layer is a key of its own because the two numberings are not one scale.** A layer's
282
+ * `<group>` carries its place in the whole tree's draw order, subdivided inside `[0, 2)`, while a
283
+ * batch mesh carries its kind's index among its layer's batches — `0`, `1`, `2`. So a batch is never
284
+ * comparable to a batch of another layer, and the mesh numbers alone would put the second batch of
285
+ * an early layer above the first batch of a late one. Collapsing them would mean giving every batch
286
+ * its layer's place plus a fraction, which is exactly the mantissa `Layer` keeps free for nesting.
287
+ *
288
+ * Three's own comparator is unreachable: `painterSortStable` is private to its WebGL renderer, and
289
+ * `renderer.renderLists` holds only the per-object answer — the instance key, the one this bug is
290
+ * about, exists nowhere in it.
291
+ *
292
+ * Two hits on one mesh share both object keys, and two hits on different meshes are nearly always
293
+ * separated by one of them, since a layer's place is unique in the tree and a batch's is unique in its
294
+ * layer. So the branches are exclusive in practice, which is also what keeps one mesh's places from
295
+ * ever being compared against another's — those count from zero independently and mean nothing
296
+ * against each other. Where two meshes do tie on both — the background quad draws level with the
297
+ * first layer, and three settles that by `material.id` — this reports nothing and the stable sort
298
+ * leaves them nearest-first.
299
+ * @param a - The first hit.
300
+ * @param b - The second hit.
301
+ * @returns Negative when `a` draws first, positive when `b` does, zero when nothing orders them.
302
+ */
303
+ function compareDrawOrder(a, b) {
304
+ if (a.object !== b.object) {
305
+ return (groupOrderOf(a.object) - groupOrderOf(b.object) || a.object.renderOrder - b.object.renderOrder);
306
+ }
307
+ const source = sources.get(a.object);
308
+ if (!source)
309
+ return 0;
310
+ const first = source.defAt(a.instanceId);
311
+ const second = source.defAt(b.instanceId);
312
+ // A hit traced to no def reports the first place rather than the last: nothing is known about
313
+ // where it drew, and that must not outrank a def whose place is known.
314
+ return (first ? source.paintOrderOf(first) : 0) - (second ? source.paintOrderOf(second) : 0);
315
+ }
316
+ /**
317
+ * The group order three would give an object: the `renderOrder` of its nearest ancestor group,
318
+ * which for anything a layer holds is that layer's place in the tree's draw order.
319
+ * @param object - The object to place.
320
+ * @returns The group order, or `0` for an object under no group at all.
321
+ */
322
+ function groupOrderOf(object) {
323
+ for (let node = object.parent; node; node = node.parent) {
324
+ if (node.isGroup)
325
+ return node.renderOrder;
326
+ }
327
+ return 0;
328
+ }
329
+ /**
330
+ * Whether an object sits in an interactive layer: it, or an ancestor, carries R3F handlers.
331
+ * @param object - The object hit.
332
+ * @param interaction - R3F's objects with handlers.
333
+ * @returns Whether R3F raycasts it for events.
334
+ */
335
+ function isInteractive(object, interaction) {
336
+ for (let node = object; node; node = node.parent) {
337
+ if (interaction.includes(node))
338
+ return true;
339
+ }
340
+ return false;
341
+ }
342
+ /**
343
+ * Squared pointer travel, in px², above which a release is a drag and not a click — so ~3.9 px of
344
+ * slop. Squared because that is how the imperative renderer's `dragThreshold` compared it, and this
345
+ * reproduces its feel exactly; a threshold in whole pixels would be four times looser.
346
+ */
347
+ const DRAG_SLOP_SQ_PX = 15;
348
+ /**
349
+ * What speaks for each mounted container's mesh, keyed weakly by mesh identity — so nothing has to
350
+ * be cleaned up per renderer, and two renderers holding the same defs never collide.
351
+ *
352
+ * Kept across hot updates, because a container registers from a mount effect that a re-render does
353
+ * not re-run: a fresh map would leave every mesh in the scene unpickable until a full reload, which
354
+ * is what editing anything in this module's import graph used to do (see {@link hotState}).
355
+ */
356
+ const sources = hotState(import.meta.hot, 'sources', () => new WeakMap());
@@ -0,0 +1,56 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * What a host that owns the canvas hands over — a MapLibre custom layer in practice. `Renderer`
4
+ * carries all three as optional props ({@link RendererProps} extends the partial of this), and
5
+ * `gl` arriving is what selects external mode, exactly as it selected the imperative renderer's.
6
+ */
7
+ export interface ExternalCanvasProps {
8
+ /**
9
+ * The host's WebGL context. Rendering shares the host's context and canvas (`gl.canvas`) inside
10
+ * the host's own frame loop.
11
+ */
12
+ gl: WebGLRenderingContext | WebGL2RenderingContext;
13
+ /**
14
+ * Asks the host for a frame — `map.triggerRepaint()` under MapLibre. The demand loop's other
15
+ * half: the host owns every frame, so anything that would invalidate the renderer's own canvas —
16
+ * an `update()`, a texture upload, a re-layout backlog — asks the host instead.
17
+ */
18
+ requestFrame: () => void;
19
+ /**
20
+ * Subscribes the renderer's per-frame draw to the host's render hook. The host calls the
21
+ * subscribed function once per frame it draws — from a MapLibre custom layer's `render` — with
22
+ * its combined view-projection matrix, sixteen elements column-major.
23
+ * @param draw - Draws one frame with the host's matrix.
24
+ * @returns An unsubscribe function.
25
+ */
26
+ onFrame: (draw: (projectionMatrix: ArrayLike<number>) => void) => () => void;
27
+ }
28
+ /**
29
+ * `<Canvas>`'s stand-in for a host that already owns the canvas: everything the Canvas component
30
+ * does — boot an R3F root, configure the renderer, the events, the size — minus the one thing it
31
+ * cannot share, creating the canvas element. It knows nothing of the scene: `children` render into
32
+ * the root verbatim, so `Renderer` composes one tree and only the mount differs.
33
+ *
34
+ * The bootstrap is imperative because the host's contract is: there is no React tree to mount into
35
+ * until a context exists. R3F's `<Canvas>` cannot serve here — it creates and owns its own canvas
36
+ * element inside a wrapper it renders, and has no way to adopt a foreign one — so this takes R3F's
37
+ * own escape hatch for exactly that, `createRoot(canvas)`, which is all `<Canvas>` wraps anyway.
38
+ * It is configured once per context, while the tree re-renders through props like any other.
39
+ *
40
+ * The camera half is the mount's as well, because the host owns it: one bare camera carries
41
+ * whatever matrix the host handed the frame hook, decomposed into a rigid view and a projection so
42
+ * that everything reading a camera's transform — culling, raycasts, lighting — works unchanged.
43
+ * There is no viewbox fit, no controls and no camera commands; the tree above simply mounts none
44
+ * of them.
45
+ *
46
+ * The demand loop inverts. R3F's own `invalidate` is a no-op under `frameloop: 'never'`, so the
47
+ * store's copy is replaced with the host's `requestFrame` — and every host frame redraws in full,
48
+ * as a shared canvas requires, starting from `resetState()` so three's GL-state tracking never
49
+ * trusts what the host's own drawing left behind.
50
+ * @param props - The host's hooks, and the tree to render into the root.
51
+ * @returns Nothing; the tree renders on the host's canvas, not in this one.
52
+ */
53
+ export declare function ExternalCanvas(props: ExternalCanvasProps & {
54
+ children?: ReactNode;
55
+ }): null;
56
+ //# sourceMappingURL=ExternalCanvas.d.ts.map