@expofp/renderer 3.2.4 → 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.
- package/README.md +111 -0
- package/dist/Renderer.d.ts +88 -0
- package/dist/Renderer.js +53 -0
- package/dist/controls/CameraControls.d.ts +102 -0
- package/dist/controls/CameraControls.js +239 -0
- package/dist/controls/camera-api.d.ts +206 -0
- package/dist/controls/camera-api.js +181 -0
- package/dist/controls/camera-move.d.ts +13 -0
- package/dist/controls/camera-move.js +18 -0
- package/dist/controls/camera-state.d.ts +40 -0
- package/dist/controls/camera-state.js +65 -0
- package/dist/controls/gestures.d.ts +70 -0
- package/dist/controls/gestures.js +84 -0
- package/dist/controls/handlers/Inertia.d.ts +111 -0
- package/dist/controls/handlers/Inertia.js +215 -0
- package/dist/controls/handlers/Pan.d.ts +23 -0
- package/dist/controls/handlers/Pan.js +32 -0
- package/dist/controls/handlers/Pitch.d.ts +94 -0
- package/dist/controls/handlers/Pitch.js +191 -0
- package/dist/controls/handlers/Roll.d.ts +103 -0
- package/dist/controls/handlers/Roll.js +182 -0
- package/dist/controls/handlers/Zoom.d.ts +15 -0
- package/dist/controls/handlers/Zoom.js +26 -0
- package/dist/controls/index.d.ts +56 -0
- package/dist/controls/index.js +58 -0
- package/dist/controls/zoom-to.d.ts +89 -0
- package/dist/controls/zoom-to.js +91 -0
- package/dist/core/InstanceUniforms.d.ts +93 -0
- package/dist/core/InstanceUniforms.js +266 -0
- package/dist/core/SquareDataTexture.d.ts +277 -0
- package/dist/core/SquareDataTexture.js +476 -0
- package/dist/core/batch/BatchedMesh.d.ts +74 -0
- package/dist/core/batch/BatchedMesh.js +149 -0
- package/dist/core/instanced/FrustumCulling.d.ts +141 -0
- package/dist/core/instanced/FrustumCulling.js +246 -0
- package/dist/core/instanced/InstancedMesh.d.ts +302 -0
- package/dist/core/instanced/InstancedMesh.js +497 -0
- package/dist/core/instanced/Instances.d.ts +119 -0
- package/dist/core/instanced/Instances.js +230 -0
- package/dist/core/instanced/Raycasting.d.ts +118 -0
- package/dist/core/instanced/Raycasting.js +134 -0
- package/dist/core/instanced/instanced-material.d.ts +77 -0
- package/dist/core/instanced/instanced-material.js +224 -0
- package/dist/core/lines/BatchedLines.d.ts +135 -0
- package/dist/core/lines/BatchedLines.js +213 -0
- package/dist/core/lines/FrustumCulling.d.ts +43 -0
- package/dist/core/lines/FrustumCulling.js +85 -0
- package/dist/core/lines/Raycasting.d.ts +44 -0
- package/dist/core/lines/Raycasting.js +91 -0
- package/dist/core/lines/lines-material.d.ts +47 -0
- package/dist/core/lines/lines-material.js +115 -0
- package/dist/core/shader-source.d.ts +50 -0
- package/dist/core/shader-source.js +84 -0
- package/dist/core/text/BatchedText.d.ts +227 -0
- package/dist/core/text/BatchedText.js +467 -0
- package/dist/core/text/MemberTexture.d.ts +90 -0
- package/dist/core/text/MemberTexture.js +194 -0
- package/dist/core/text/Text.d.ts +73 -0
- package/dist/core/text/Text.js +215 -0
- package/dist/core/text/batched-text-material.d.ts +56 -0
- package/dist/core/text/batched-text-material.js +121 -0
- package/dist/core/text/glyph-packing.d.ts +17 -0
- package/dist/core/text/glyph-packing.js +61 -0
- package/dist/core/typed-array.d.ts +16 -0
- package/dist/core/typed-array.js +20 -0
- package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
- package/dist/debug/components/DebugPanelProvider.js +38 -0
- package/dist/debug/components/DebugTools.d.ts +22 -0
- package/dist/debug/components/DebugTools.js +28 -0
- package/dist/debug/components/Gui.d.ts +20 -0
- package/dist/debug/components/Gui.js +32 -0
- package/dist/debug/components/MemoryInfo.d.ts +22 -0
- package/dist/debug/components/MemoryInfo.js +42 -0
- package/dist/debug/components/Panel.d.ts +31 -0
- package/dist/debug/components/Panel.js +98 -0
- package/dist/debug/components/Stats.d.ts +9 -0
- package/dist/debug/components/Stats.js +24 -0
- package/dist/debug/debug-context.d.ts +41 -0
- package/dist/debug/debug-context.js +59 -0
- package/dist/debug/index.d.ts +51 -0
- package/dist/debug/index.js +64 -0
- package/dist/events/LayoutDriver.d.ts +31 -0
- package/dist/events/LayoutDriver.js +88 -0
- package/dist/events/PointerMissed.d.ts +23 -0
- package/dist/events/PointerMissed.js +58 -0
- package/dist/events/def-culling.d.ts +46 -0
- package/dist/events/def-culling.js +69 -0
- package/dist/events/layout-targets.d.ts +104 -0
- package/dist/events/layout-targets.js +245 -0
- package/dist/events/pointer.d.ts +118 -0
- package/dist/events/pointer.js +168 -0
- package/dist/external/ExternalCanvas.d.ts +56 -0
- package/dist/external/ExternalCanvas.js +178 -0
- package/dist/external/external-camera.d.ts +17 -0
- package/dist/external/external-camera.js +82 -0
- package/dist/external/external-events.d.ts +14 -0
- package/dist/external/external-events.js +33 -0
- package/dist/index.d.ts +11 -783
- package/dist/index.js +12 -8028
- package/dist/objects/Background.d.ts +31 -0
- package/dist/objects/Background.js +37 -0
- package/dist/objects/Lighting.d.ts +20 -0
- package/dist/objects/Lighting.js +85 -0
- package/dist/objects/containers/SlotContainer.d.ts +266 -0
- package/dist/objects/containers/SlotContainer.js +251 -0
- package/dist/objects/containers/hooks.d.ts +46 -0
- package/dist/objects/containers/hooks.js +118 -0
- package/dist/objects/containers/index.d.ts +6 -0
- package/dist/objects/containers/index.js +4 -0
- package/dist/objects/containers/keys.d.ts +10 -0
- package/dist/objects/containers/keys.js +18 -0
- package/dist/objects/containers/mesh-writes.d.ts +106 -0
- package/dist/objects/containers/mesh-writes.js +97 -0
- package/dist/objects/containers/props.d.ts +23 -0
- package/dist/objects/containers/props.js +1 -0
- package/dist/objects/images/ImageInstance.d.ts +29 -0
- package/dist/objects/images/ImageInstance.js +83 -0
- package/dist/objects/images/Images.d.ts +37 -0
- package/dist/objects/images/Images.js +204 -0
- package/dist/objects/images/atlas-packer.d.ts +128 -0
- package/dist/objects/images/atlas-packer.js +315 -0
- package/dist/objects/images/image-container.d.ts +51 -0
- package/dist/objects/images/image-container.js +70 -0
- package/dist/objects/images/index.d.ts +2 -0
- package/dist/objects/images/index.js +1 -0
- package/dist/objects/images/texture-budget.d.ts +120 -0
- package/dist/objects/images/texture-budget.js +215 -0
- package/dist/objects/layer/Layer.d.ts +27 -0
- package/dist/objects/layer/Layer.js +114 -0
- package/dist/objects/layer/index.d.ts +2 -0
- package/dist/objects/layer/index.js +1 -0
- package/dist/objects/layer/mount-scheduler.d.ts +102 -0
- package/dist/objects/layer/mount-scheduler.js +145 -0
- package/dist/objects/layer/visibility-gui.d.ts +19 -0
- package/dist/objects/layer/visibility-gui.js +93 -0
- package/dist/objects/lines/LineInstance.d.ts +21 -0
- package/dist/objects/lines/LineInstance.js +73 -0
- package/dist/objects/lines/Lines.d.ts +17 -0
- package/dist/objects/lines/Lines.js +29 -0
- package/dist/objects/lines/index.d.ts +2 -0
- package/dist/objects/lines/index.js +1 -0
- package/dist/objects/lines/line-container.d.ts +35 -0
- package/dist/objects/lines/line-container.js +40 -0
- package/dist/objects/materials/ColorMaterial.d.ts +85 -0
- package/dist/objects/materials/ColorMaterial.js +89 -0
- package/dist/objects/materials/LineMaterial.d.ts +44 -0
- package/dist/objects/materials/LineMaterial.js +72 -0
- package/dist/objects/materials/ModelMaterial.d.ts +41 -0
- package/dist/objects/materials/ModelMaterial.js +84 -0
- package/dist/objects/materials/TextureMaterial.d.ts +30 -0
- package/dist/objects/materials/TextureMaterial.js +29 -0
- package/dist/objects/materials/dim.d.ts +123 -0
- package/dist/objects/materials/dim.js +213 -0
- package/dist/objects/materials/shared.d.ts +6 -0
- package/dist/objects/materials/shared.js +51 -0
- package/dist/objects/models/ModelInstance.d.ts +22 -0
- package/dist/objects/models/ModelInstance.js +33 -0
- package/dist/objects/models/Models.d.ts +22 -0
- package/dist/objects/models/Models.js +177 -0
- package/dist/objects/models/gltf-cache.d.ts +17 -0
- package/dist/objects/models/gltf-cache.js +50 -0
- package/dist/objects/models/index.d.ts +2 -0
- package/dist/objects/models/index.js +1 -0
- package/dist/objects/models/model-asset.d.ts +48 -0
- package/dist/objects/models/model-asset.js +77 -0
- package/dist/objects/models/model-container.d.ts +139 -0
- package/dist/objects/models/model-container.js +227 -0
- package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
- package/dist/objects/shapes/ShapeInstance.js +40 -0
- package/dist/objects/shapes/Shapes.d.ts +21 -0
- package/dist/objects/shapes/Shapes.js +120 -0
- package/dist/objects/shapes/index.d.ts +2 -0
- package/dist/objects/shapes/index.js +1 -0
- package/dist/objects/shapes/shape-container.d.ts +165 -0
- package/dist/objects/shapes/shape-container.js +272 -0
- package/dist/objects/shapes/shape-geometry.d.ts +27 -0
- package/dist/objects/shapes/shape-geometry.js +94 -0
- package/dist/objects/text/TextInstance.d.ts +22 -0
- package/dist/objects/text/TextInstance.js +95 -0
- package/dist/objects/text/Texts.d.ts +30 -0
- package/dist/objects/text/Texts.js +41 -0
- package/dist/objects/text/index.d.ts +2 -0
- package/dist/objects/text/index.js +1 -0
- package/dist/objects/text/text-container.d.ts +105 -0
- package/dist/objects/text/text-container.js +206 -0
- package/dist/objects/text/text-layout.d.ts +83 -0
- package/dist/objects/text/text-layout.js +154 -0
- package/dist/space/Camera.d.ts +103 -0
- package/dist/space/Camera.js +321 -0
- package/dist/space/Scene.d.ts +44 -0
- package/dist/space/Scene.js +112 -0
- package/dist/space/Space.d.ts +288 -0
- package/dist/space/Space.js +329 -0
- package/dist/space/VisibleRect.d.ts +72 -0
- package/dist/space/VisibleRect.js +129 -0
- package/dist/space/camera-handle.d.ts +67 -0
- package/dist/space/camera-handle.js +63 -0
- package/dist/space/camera-kind.d.ts +26 -0
- package/dist/space/camera-kind.js +30 -0
- package/dist/space/canvas-size.d.ts +24 -0
- package/dist/space/canvas-size.js +20 -0
- package/dist/space/clipping.d.ts +55 -0
- package/dist/space/clipping.js +108 -0
- package/dist/space/layout-measure.d.ts +65 -0
- package/dist/space/layout-measure.js +173 -0
- package/dist/space/viewport.d.ts +112 -0
- package/dist/space/viewport.js +67 -0
- package/dist/types/defs.d.ts +169 -0
- package/dist/types/defs.js +1 -0
- package/dist/types/events.d.ts +183 -0
- package/dist/types/events.js +1 -0
- package/dist/types/guards.d.ts +14 -0
- package/dist/types/guards.js +42 -0
- package/dist/update/index.d.ts +3 -0
- package/dist/update/index.js +2 -0
- package/dist/update/slot-writers.d.ts +30 -0
- package/dist/update/slot-writers.js +72 -0
- package/dist/update/store.d.ts +41 -0
- package/dist/update/store.js +98 -0
- package/dist/utils/arrays.d.ts +13 -0
- package/dist/utils/arrays.js +20 -0
- package/dist/utils/colors.d.ts +19 -0
- package/dist/utils/colors.js +27 -0
- package/dist/utils/logging.d.ts +26 -0
- package/dist/utils/logging.js +47 -0
- package/dist/utils/math.d.ts +24 -0
- package/dist/utils/math.js +27 -0
- package/package.json +67 -19
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { createContext, use, useEffect } from 'react';
|
|
2
|
+
import { isLayerDef } from '../types/guards';
|
|
3
|
+
import { writeChanged } from '../update';
|
|
4
|
+
import { createLogger } from '../utils/logging';
|
|
5
|
+
import { culledBy } from './def-culling';
|
|
6
|
+
const logger = createLogger('layout');
|
|
7
|
+
/**
|
|
8
|
+
* The renderer's re-layout registry. Absent outside a renderer, where a layer simply registers
|
|
9
|
+
* nothing — which is what lets `Layer` be rendered bare in a spec.
|
|
10
|
+
*/
|
|
11
|
+
export const LayoutTargetsContext = createContext(undefined);
|
|
12
|
+
/**
|
|
13
|
+
* Registers a layer's re-layout targets for as long as the layer is mounted: the defs it holds that
|
|
14
|
+
* declare an `onScale`, and the layer itself when it declares one.
|
|
15
|
+
*
|
|
16
|
+
* A child *layer* is never registered here even when it declares a handler — it registers itself, and
|
|
17
|
+
* it has to: a handler assigned to a layer after mount re-renders that layer alone, so a registration
|
|
18
|
+
* held by its parent would never learn of it. Self-registration also covers the root, which has no
|
|
19
|
+
* parent layer to be registered by.
|
|
20
|
+
*
|
|
21
|
+
* Called from the layer rather than from its containers or their instances, because this is where
|
|
22
|
+
* both halves are already at hand. It also means a def is laid out whether or not its instance has
|
|
23
|
+
* mounted — defs are the source of truth, and a staged mount picks up whatever they hold when it
|
|
24
|
+
* admits them.
|
|
25
|
+
*
|
|
26
|
+
* Outside a renderer there is no registry and this does nothing, which is what lets a `Layer` be
|
|
27
|
+
* rendered bare in a spec.
|
|
28
|
+
* @param layer - The layer registering; itself a target when it declares a handler, which is how a
|
|
29
|
+
* layer whose *membership* depends on the camera re-derives it.
|
|
30
|
+
* @param children - The layer's children snapshot.
|
|
31
|
+
*/
|
|
32
|
+
export function useLayoutTargets(layer, children) {
|
|
33
|
+
const targets = use(LayoutTargetsContext);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!targets)
|
|
36
|
+
return undefined;
|
|
37
|
+
const defs = children.filter((def) => (def.onScale !== undefined || def.onRoll !== undefined) && !isLayerDef(def));
|
|
38
|
+
if (layer.onScale || layer.onRoll)
|
|
39
|
+
defs.push(layer);
|
|
40
|
+
if (!defs.length)
|
|
41
|
+
return undefined;
|
|
42
|
+
return targets.register(defs);
|
|
43
|
+
}, [targets, layer, children]);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Builds a renderer's re-layout registry.
|
|
47
|
+
* @param invalidate - Requests a frame, for layouts that happen outside the frame loop.
|
|
48
|
+
* @param pixelSize - The pixel size defs are laid out at until the first frame moves it.
|
|
49
|
+
* @param roll - The roll defs are laid out at until the first frame moves it, in radians.
|
|
50
|
+
* @returns The registry.
|
|
51
|
+
*/
|
|
52
|
+
export function createLayoutTargets(invalidate, pixelSize, roll = 0) {
|
|
53
|
+
const groups = new Set();
|
|
54
|
+
/**
|
|
55
|
+
* The pixel size each def was last laid out at — the whole of the registry's state, because the
|
|
56
|
+
* rule that lays a def out is the def's own. Keyed by def rather than by group, so a membership
|
|
57
|
+
* change re-lays out only the defs that joined.
|
|
58
|
+
*
|
|
59
|
+
* A def whose `onScale` is *replaced* after it was laid out is not noticed: the record says only
|
|
60
|
+
* "laid out at this size". Reassigning a handler is a thing nothing does — a def is built with its
|
|
61
|
+
* rule — and a caller that does it lays the def out itself.
|
|
62
|
+
*/
|
|
63
|
+
const layouts = new WeakMap();
|
|
64
|
+
/**
|
|
65
|
+
* The roll each def's `onRoll` last ran at — the other axis's record, kept apart from
|
|
66
|
+
* {@link layouts} because the two move independently: a zoom leaves every roll record current,
|
|
67
|
+
* and a turn leaves every scale record current.
|
|
68
|
+
*/
|
|
69
|
+
const rolls = new WeakMap();
|
|
70
|
+
/**
|
|
71
|
+
* The sweep's backlog: defs an `apply` marked stale and {@link LayoutTargets.drain} has not
|
|
72
|
+
* reached. **One queue for every axis** — which of a queued def's rules actually runs is decided
|
|
73
|
+
* at drain time by the per-axis records above, so a def stale on both axes is dequeued once and
|
|
74
|
+
* laid out on both, and a new axis adds a record and a rule, never another queue. `deferred`
|
|
75
|
+
* holds the ones a drain pushed behind the visible — hidden defs, and defs the frustum
|
|
76
|
+
* rejected — worked off with whatever budget the stale pass leaves. Sets, not arrays, because one
|
|
77
|
+
* def can be re-marked while queued and must not be laid out twice.
|
|
78
|
+
*/
|
|
79
|
+
const pending = new Set();
|
|
80
|
+
const deferred = new Set();
|
|
81
|
+
let current = pixelSize;
|
|
82
|
+
let currentRoll = roll;
|
|
83
|
+
/**
|
|
84
|
+
* The view every rule is handed — one instance for the registry's lifetime, mutated as the axes
|
|
85
|
+
* move, which is what keeps a ten-thousand-label sweep allocation-free. `LayoutView` documents
|
|
86
|
+
* the flip side: valid during the call, so a rule destructures rather than retains it.
|
|
87
|
+
*/
|
|
88
|
+
const view = { pixelSize: current, roll: currentRoll };
|
|
89
|
+
/**
|
|
90
|
+
* Runs a def's handler and writes the result.
|
|
91
|
+
*
|
|
92
|
+
* A leaf goes through the def's own registered sync — the same closure the React path runs after
|
|
93
|
+
* `update(def)` — so a handler mutating def fields needs no `update()` call of its own, and the two
|
|
94
|
+
* paths cannot disagree about what a def looks like. A def whose instance has not mounted yet syncs
|
|
95
|
+
* to nothing here and picks the mutation up when it does. A layer among the returned defs is
|
|
96
|
+
* reconciled instead, which is how a handler changes its own membership.
|
|
97
|
+
*
|
|
98
|
+
* A handler reporting nothing skips that write, which is the whole point of the return value: it is
|
|
99
|
+
* the half that costs. The def is still recorded as laid out, so a membership change does not run
|
|
100
|
+
* the handler over it again.
|
|
101
|
+
* @param def - The def to lay out.
|
|
102
|
+
* @returns Whether anything was written.
|
|
103
|
+
*/
|
|
104
|
+
function layOut(def) {
|
|
105
|
+
// Hidden defs are laid out too. Whether a def draws at all can be exactly what its rule decides
|
|
106
|
+
// — a booth's dot appears only once the plan is far enough out for its label to stop fitting —
|
|
107
|
+
// so a registry that skipped them would never run the rule that would bring them back. Skipping
|
|
108
|
+
// the *waste* is the rule's own job instead: a handler with nothing worth drawing returns
|
|
109
|
+
// nothing, and the slot write is the half that costs.
|
|
110
|
+
if (!def.onScale || layouts.get(def) === current)
|
|
111
|
+
return false;
|
|
112
|
+
const changed = def.onScale(view);
|
|
113
|
+
// Recorded before the write, which runs the def's own sync and can reach back in here.
|
|
114
|
+
layouts.set(def, current);
|
|
115
|
+
if (!changed || changed.length === 0)
|
|
116
|
+
return false;
|
|
117
|
+
writeChanged(changed);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Runs a def's roll rule and writes the result — {@link layOut} on the other axis, with the same
|
|
122
|
+
* record-before-write ordering and the same "an empty return skips the write" contract.
|
|
123
|
+
* @param def - The def to lay out.
|
|
124
|
+
* @returns Whether anything was written.
|
|
125
|
+
*/
|
|
126
|
+
function layOutRoll(def) {
|
|
127
|
+
if (!def.onRoll || rolls.get(def) === currentRoll)
|
|
128
|
+
return false;
|
|
129
|
+
const changed = def.onRoll(view);
|
|
130
|
+
rolls.set(def, currentRoll);
|
|
131
|
+
if (!changed || changed.length === 0)
|
|
132
|
+
return false;
|
|
133
|
+
writeChanged(changed);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Runs whatever of a def's rules are stale by their records — the one unit of work a dequeued def
|
|
138
|
+
* costs, scale before roll so a rule that turns a label re-fits it at the size just applied.
|
|
139
|
+
* @param def - The def to lay out.
|
|
140
|
+
* @returns Whether anything was written.
|
|
141
|
+
*/
|
|
142
|
+
function layOutStale(def) {
|
|
143
|
+
const scaled = layOut(def);
|
|
144
|
+
const rolled = layOutRoll(def);
|
|
145
|
+
return scaled || rolled;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Marks stale defs for the sweep: the previous sweep's deferrals rejoin the queue — where the new
|
|
149
|
+
* frustum decides their priority afresh, and cross-axis work survives whichever axis moved — and
|
|
150
|
+
* every def carrying the moved axis's rule is (re-)queued.
|
|
151
|
+
* @param carriesRule - Whether a def declares the moved axis's rule.
|
|
152
|
+
*/
|
|
153
|
+
function requeue(carriesRule) {
|
|
154
|
+
for (const def of deferred)
|
|
155
|
+
pending.add(def);
|
|
156
|
+
deferred.clear();
|
|
157
|
+
// Rule presence is read at marking time, so a rule cleared after registration — a hidden
|
|
158
|
+
// bookmark's — simply stops queueing, with nothing to unregister.
|
|
159
|
+
for (const defs of groups) {
|
|
160
|
+
for (const def of defs) {
|
|
161
|
+
if (carriesRule(def))
|
|
162
|
+
pending.add(def);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
get pixelSize() {
|
|
168
|
+
return current;
|
|
169
|
+
},
|
|
170
|
+
get roll() {
|
|
171
|
+
return currentRoll;
|
|
172
|
+
},
|
|
173
|
+
get hasWork() {
|
|
174
|
+
return pending.size > 0 || deferred.size > 0;
|
|
175
|
+
},
|
|
176
|
+
register(defs) {
|
|
177
|
+
groups.add(defs);
|
|
178
|
+
let laidOut = false;
|
|
179
|
+
for (const def of defs) {
|
|
180
|
+
if (layOutStale(def))
|
|
181
|
+
laidOut = true;
|
|
182
|
+
}
|
|
183
|
+
// Registration happens in an effect rather than inside the frame loop, so anything written here
|
|
184
|
+
// needs a frame asking for it.
|
|
185
|
+
if (laidOut)
|
|
186
|
+
invalidate();
|
|
187
|
+
return () => {
|
|
188
|
+
groups.delete(defs);
|
|
189
|
+
// A queued def whose layer is gone must not be laid out from the backlog: its instances are
|
|
190
|
+
// unmounting, and its handler may read state its owner already tore down.
|
|
191
|
+
for (const def of defs) {
|
|
192
|
+
pending.delete(def);
|
|
193
|
+
deferred.delete(def);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
applyScale(nextPixelSize) {
|
|
198
|
+
current = nextPixelSize;
|
|
199
|
+
view.pixelSize = nextPixelSize;
|
|
200
|
+
requeue((def) => def.onScale !== undefined);
|
|
201
|
+
},
|
|
202
|
+
applyRoll(nextRoll) {
|
|
203
|
+
currentRoll = nextRoll;
|
|
204
|
+
view.roll = nextRoll;
|
|
205
|
+
requeue((def) => def.onRoll !== undefined);
|
|
206
|
+
},
|
|
207
|
+
drain(frustum, budgetMs = -1) {
|
|
208
|
+
if (pending.size === 0 && deferred.size === 0)
|
|
209
|
+
return false;
|
|
210
|
+
const start = performance.now();
|
|
211
|
+
const overBudget = () => budgetMs > 0 && performance.now() - start >= budgetMs;
|
|
212
|
+
let wrote = 0;
|
|
213
|
+
// Sets iterate in insertion order and tolerate deletion mid-walk, which is what makes the two
|
|
214
|
+
// passes below plain loops: the order moves arrived is the priority within a pass, and a
|
|
215
|
+
// processed def leaves its set before its rules run.
|
|
216
|
+
for (const def of pending) {
|
|
217
|
+
if (overBudget())
|
|
218
|
+
break;
|
|
219
|
+
pending.delete(def);
|
|
220
|
+
// Deferral is priority, never a skip: a hidden def, or one the camera cannot see, joins the
|
|
221
|
+
// second pass rather than being laid out now. `def.hidden` needs no frustum — hidden is not
|
|
222
|
+
// drawn wherever it sits.
|
|
223
|
+
if (def.hidden === true || (frustum !== undefined && culledBy(def, frustum))) {
|
|
224
|
+
deferred.add(def);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (layOutStale(def))
|
|
228
|
+
wrote++;
|
|
229
|
+
}
|
|
230
|
+
for (const def of deferred) {
|
|
231
|
+
if (overBudget())
|
|
232
|
+
break;
|
|
233
|
+
deferred.delete(def);
|
|
234
|
+
if (layOutStale(def))
|
|
235
|
+
wrote++;
|
|
236
|
+
}
|
|
237
|
+
const remaining = pending.size + deferred.size;
|
|
238
|
+
if (wrote > 0) {
|
|
239
|
+
const took = (performance.now() - start).toFixed(2);
|
|
240
|
+
logger.debug(`laid out ${wrote} defs (${remaining} remaining) in ${took}ms`);
|
|
241
|
+
}
|
|
242
|
+
return remaining > 0;
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type ThreeEvent } from '@react-three/fiber';
|
|
2
|
+
import { type Intersection, type Object3D, type Ray, type Raycaster } from 'three';
|
|
3
|
+
import { type PlanPoint, type Space } from '../space/Space';
|
|
4
|
+
import { type LayerDef, type RenderableDef } from '../types/defs';
|
|
5
|
+
import { type PointerHandlerName } from '../types/events';
|
|
6
|
+
/**
|
|
7
|
+
* Traces one mesh's intersection back to the def that drew it, or `undefined` when it cannot —
|
|
8
|
+
* a slot released between the raycast and the dispatch, or a mesh whose picking is not implemented.
|
|
9
|
+
*/
|
|
10
|
+
export type HitResolver = (hit: Intersection) => RenderableDef | undefined;
|
|
11
|
+
/** The picking props {@link usePointerProps} builds for a layer's `<group>`. */
|
|
12
|
+
export interface PointerGroupProps {
|
|
13
|
+
/** Refuses the raycast traversal into the subtree while the layer is off screen. */
|
|
14
|
+
raycast(): false | undefined;
|
|
15
|
+
/** Dispatches a click in the layer's subtree; attached only when the def declares one. */
|
|
16
|
+
onClick?: (event: ThreeEvent<MouseEvent>) => void;
|
|
17
|
+
/** Dispatches a move over the layer's subtree; attached only when the def declares one. */
|
|
18
|
+
onPointerMove?: (event: ThreeEvent<MouseEvent>) => void;
|
|
19
|
+
/** Dispatches the pointer leaving a def; attached only when the def declares one. */
|
|
20
|
+
onPointerOut?: (event: ThreeEvent<MouseEvent>) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The props that make a layer's group interactive: one dispatching handler per handler the def
|
|
24
|
+
* declares, and the raycast gate that un-picks the layer while it is off screen.
|
|
25
|
+
*
|
|
26
|
+
* Presence is the contract, in both directions. R3F raycasts only objects carrying handlers — and
|
|
27
|
+
* raycasts them with their whole subtree — so a handler is attached exactly when the def declares
|
|
28
|
+
* one and the layer is on screen: a layer wanting clicks alone must not be raycast on every pointer
|
|
29
|
+
* move, and a layer that is hidden, or under something hidden, must not be raycast at all. Which
|
|
30
|
+
* function a handler currently is stays out of it: dispatch looks the handler up by name on the def
|
|
31
|
+
* at event time, so swapping one takes effect with no render.
|
|
32
|
+
*
|
|
33
|
+
* Handler presence is read here as values (`layer.onClick !== undefined`) rather than through
|
|
34
|
+
* `layer` inside a callback: memoization compares what a render *read*, and a property reached only
|
|
35
|
+
* from inside a function is not that — a compiled cache keyed on the def object, whose identity
|
|
36
|
+
* never changes, would keep serving a group with no handler attached.
|
|
37
|
+
*
|
|
38
|
+
* The `raycast` gate covers the direction handler absence cannot: an interactive *ancestor* is
|
|
39
|
+
* raycast with everything beneath it, and `false` is three's own signal to stop descending —
|
|
40
|
+
* `Raycaster`'s `intersect()` keeps walking a subtree only while each object's `raycast` returns
|
|
41
|
+
* something other than `false`. Visibility is read at raycast time rather than swapped per render,
|
|
42
|
+
* since R3F ignores a prop set to `undefined` and a blocking version would stick.
|
|
43
|
+
* @param layer - The layer the group renders.
|
|
44
|
+
* @param visible - Whether the layer is on screen, ancestors included.
|
|
45
|
+
* @returns Props to spread onto the layer's `<group>`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function usePointerProps(layer: LayerDef, visible: boolean): PointerGroupProps;
|
|
48
|
+
/**
|
|
49
|
+
* Hands one of R3F's intersection events to a layer's handler, as the narrow event the public API
|
|
50
|
+
* describes: the def that was hit, and where the pointer sits on the plan plane.
|
|
51
|
+
*
|
|
52
|
+
* Two hits are dropped rather than reported. One the renderer cannot trace to a def — a released
|
|
53
|
+
* slot, or a mesh whose picking is not implemented — because a handler has nothing to act on without
|
|
54
|
+
* it. And a click that ends a drag: R3F fires `onClick` on release whenever the press landed on the
|
|
55
|
+
* same object, which on a pannable plan would select whatever the pan started over.
|
|
56
|
+
*
|
|
57
|
+
* The handler is called with no receiver, so a handler shared across layers reads `event.def` rather
|
|
58
|
+
* than the layer it was declared on.
|
|
59
|
+
* @param layer - The layer whose handler should run.
|
|
60
|
+
* @param name - Which handler to run.
|
|
61
|
+
* @param event - R3F's intersection event.
|
|
62
|
+
* @param space - The scene's space, which the event's world coordinates are reported through.
|
|
63
|
+
*/
|
|
64
|
+
export declare function dispatchPointer(layer: LayerDef, name: PointerHandlerName, event: ThreeEvent<MouseEvent>, space: Space): void;
|
|
65
|
+
/**
|
|
66
|
+
* Registers how a mesh's intersections map back to defs, for as long as the mesh is mounted.
|
|
67
|
+
*
|
|
68
|
+
* Each container knows two things no one else does: which field of an intersection identifies its
|
|
69
|
+
* slot (`batchId` for a batched mesh, `instanceId` for an instanced one, the pack position for a line
|
|
70
|
+
* batch), and which def holds that slot. It supplies both here as one closure, so the dispatch path
|
|
71
|
+
* stays free of per-mesh-type knowledge.
|
|
72
|
+
* @param mesh - The mesh whose hits the resolver speaks for.
|
|
73
|
+
* @param resolve - Maps one of its intersections to a def.
|
|
74
|
+
* @returns An unregister function, safe to call after another resolver replaced this one.
|
|
75
|
+
*/
|
|
76
|
+
export declare function registerHitResolver(mesh: Object3D, resolve: HitResolver): () => void;
|
|
77
|
+
/**
|
|
78
|
+
* The def an intersection hit.
|
|
79
|
+
*
|
|
80
|
+
* A mesh with no registered resolver reports nothing, which is how a primitive whose picking is not
|
|
81
|
+
* implemented behaves as if its layer were not interactive: the hit is dropped and no handler runs.
|
|
82
|
+
* @param hit - The intersection to trace.
|
|
83
|
+
* @returns The def, or `undefined` when the hit cannot be traced to one.
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveHit(hit: Intersection): RenderableDef | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Where a missed click landed, in plan units.
|
|
88
|
+
*
|
|
89
|
+
* **A miss is not an empty canvas.** It means no handler took the click: a layer declaring no
|
|
90
|
+
* pointer handlers is never raycast by R3F in the first place, so the pointer may be sitting
|
|
91
|
+
* squarely on a wall, a model or a booth that simply does not listen. Searching the scene here is
|
|
92
|
+
* what makes the reported position agree with what the user is looking at — the same answer a hit
|
|
93
|
+
* event carries, arrived at a moment later and with no def to name.
|
|
94
|
+
*
|
|
95
|
+
* The search prunes hidden content for free: {@link usePointerProps} refuses the traversal into a
|
|
96
|
+
* hidden layer's subtree, which is three's own signal to stop descending.
|
|
97
|
+
* @param raycaster - The scene's raycaster, holding the ray R3F cast for this click.
|
|
98
|
+
* @param scene - The scene to search.
|
|
99
|
+
* @param space - The scene's space.
|
|
100
|
+
* @returns The nearest geometry under the pointer, or — where there is none — the floor beneath it.
|
|
101
|
+
*/
|
|
102
|
+
export declare function missedPoint(raycaster: Raycaster, scene: Object3D, space: Space): PlanPoint;
|
|
103
|
+
/**
|
|
104
|
+
* Where a ray crosses the plan plane — the floor under the pointer, whatever is drawn above it.
|
|
105
|
+
*
|
|
106
|
+
* The fallback for a click that reached neither a handler nor any geometry at all: past the edge of
|
|
107
|
+
* the plan there is nothing to report but the ground the ray carries on to.
|
|
108
|
+
*
|
|
109
|
+
* Both the crossing and the fallback are taken through the scene's space, because R3F casts its rays
|
|
110
|
+
* in world coordinates and a scene fitted to the canvas draws a plan unit as some other number of
|
|
111
|
+
* those. Without it the reported position is the world one wearing plan units' name.
|
|
112
|
+
* @param ray - The ray the pointer cast, in world space.
|
|
113
|
+
* @param space - The scene's space.
|
|
114
|
+
* @returns The crossing, in plan units, or — for a ray running parallel to the plan, which crosses
|
|
115
|
+
* it nowhere — where the ray started.
|
|
116
|
+
*/
|
|
117
|
+
export declare function planePoint(ray: Ray, space: Space): PlanPoint;
|
|
118
|
+
//# sourceMappingURL=pointer.d.ts.map
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { useSpace } from '../space/Space';
|
|
2
|
+
/**
|
|
3
|
+
* The props that make a layer's group interactive: one dispatching handler per handler the def
|
|
4
|
+
* declares, and the raycast gate that un-picks the layer while it is off screen.
|
|
5
|
+
*
|
|
6
|
+
* Presence is the contract, in both directions. R3F raycasts only objects carrying handlers — and
|
|
7
|
+
* raycasts them with their whole subtree — so a handler is attached exactly when the def declares
|
|
8
|
+
* one and the layer is on screen: a layer wanting clicks alone must not be raycast on every pointer
|
|
9
|
+
* move, and a layer that is hidden, or under something hidden, must not be raycast at all. Which
|
|
10
|
+
* function a handler currently is stays out of it: dispatch looks the handler up by name on the def
|
|
11
|
+
* at event time, so swapping one takes effect with no render.
|
|
12
|
+
*
|
|
13
|
+
* Handler presence is read here as values (`layer.onClick !== undefined`) rather than through
|
|
14
|
+
* `layer` inside a callback: memoization compares what a render *read*, and a property reached only
|
|
15
|
+
* from inside a function is not that — a compiled cache keyed on the def object, whose identity
|
|
16
|
+
* never changes, would keep serving a group with no handler attached.
|
|
17
|
+
*
|
|
18
|
+
* The `raycast` gate covers the direction handler absence cannot: an interactive *ancestor* is
|
|
19
|
+
* raycast with everything beneath it, and `false` is three's own signal to stop descending —
|
|
20
|
+
* `Raycaster`'s `intersect()` keeps walking a subtree only while each object's `raycast` returns
|
|
21
|
+
* something other than `false`. Visibility is read at raycast time rather than swapped per render,
|
|
22
|
+
* since R3F ignores a prop set to `undefined` and a blocking version would stick.
|
|
23
|
+
* @param layer - The layer the group renders.
|
|
24
|
+
* @param visible - Whether the layer is on screen, ancestors included.
|
|
25
|
+
* @returns Props to spread onto the layer's `<group>`.
|
|
26
|
+
*/
|
|
27
|
+
export function usePointerProps(layer, visible) {
|
|
28
|
+
// Pointer positions are reported on the plan, and R3F casts its rays in world coordinates.
|
|
29
|
+
const space = useSpace();
|
|
30
|
+
const handlesClick = visible && layer.onClick !== undefined;
|
|
31
|
+
const handlesPointerMove = visible && layer.onPointerMove !== undefined;
|
|
32
|
+
const handlesPointerOut = visible && layer.onPointerOut !== undefined;
|
|
33
|
+
function handler(name) {
|
|
34
|
+
return (event) => dispatchPointer(layer, name, event, space);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
raycast: () => (visible ? undefined : false),
|
|
38
|
+
onClick: handlesClick ? handler('onClick') : undefined,
|
|
39
|
+
onPointerMove: handlesPointerMove ? handler('onPointerMove') : undefined,
|
|
40
|
+
onPointerOut: handlesPointerOut ? handler('onPointerOut') : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Hands one of R3F's intersection events to a layer's handler, as the narrow event the public API
|
|
45
|
+
* describes: the def that was hit, and where the pointer sits on the plan plane.
|
|
46
|
+
*
|
|
47
|
+
* Two hits are dropped rather than reported. One the renderer cannot trace to a def — a released
|
|
48
|
+
* slot, or a mesh whose picking is not implemented — because a handler has nothing to act on without
|
|
49
|
+
* it. And a click that ends a drag: R3F fires `onClick` on release whenever the press landed on the
|
|
50
|
+
* same object, which on a pannable plan would select whatever the pan started over.
|
|
51
|
+
*
|
|
52
|
+
* The handler is called with no receiver, so a handler shared across layers reads `event.def` rather
|
|
53
|
+
* than the layer it was declared on.
|
|
54
|
+
* @param layer - The layer whose handler should run.
|
|
55
|
+
* @param name - Which handler to run.
|
|
56
|
+
* @param event - R3F's intersection event.
|
|
57
|
+
* @param space - The scene's space, which the event's world coordinates are reported through.
|
|
58
|
+
*/
|
|
59
|
+
export function dispatchPointer(layer, name, event, space) {
|
|
60
|
+
const handler = layer[name];
|
|
61
|
+
if (!handler)
|
|
62
|
+
return;
|
|
63
|
+
if (name === 'onClick' && isDrag(event))
|
|
64
|
+
return;
|
|
65
|
+
const def = resolveHit(event);
|
|
66
|
+
if (!def)
|
|
67
|
+
return;
|
|
68
|
+
handler({
|
|
69
|
+
def,
|
|
70
|
+
// The ray's own intersection with the geometry, which three computed against the real
|
|
71
|
+
// triangles — so an elevated def reports the point on *it*, elevation and all, rather than the
|
|
72
|
+
// ground the ray carries on to behind it. R3F reports it in world coordinates; the cast is the
|
|
73
|
+
// assertion of which space that is.
|
|
74
|
+
point: space.worldToPlan(event.point),
|
|
75
|
+
stopPropagation: () => event.stopPropagation(),
|
|
76
|
+
nativeEvent: event.nativeEvent,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Registers how a mesh's intersections map back to defs, for as long as the mesh is mounted.
|
|
81
|
+
*
|
|
82
|
+
* Each container knows two things no one else does: which field of an intersection identifies its
|
|
83
|
+
* slot (`batchId` for a batched mesh, `instanceId` for an instanced one, the pack position for a line
|
|
84
|
+
* batch), and which def holds that slot. It supplies both here as one closure, so the dispatch path
|
|
85
|
+
* stays free of per-mesh-type knowledge.
|
|
86
|
+
* @param mesh - The mesh whose hits the resolver speaks for.
|
|
87
|
+
* @param resolve - Maps one of its intersections to a def.
|
|
88
|
+
* @returns An unregister function, safe to call after another resolver replaced this one.
|
|
89
|
+
*/
|
|
90
|
+
export function registerHitResolver(mesh, resolve) {
|
|
91
|
+
resolvers.set(mesh, resolve);
|
|
92
|
+
return () => {
|
|
93
|
+
if (resolvers.get(mesh) === resolve)
|
|
94
|
+
resolvers.delete(mesh);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The def an intersection hit.
|
|
99
|
+
*
|
|
100
|
+
* A mesh with no registered resolver reports nothing, which is how a primitive whose picking is not
|
|
101
|
+
* implemented behaves as if its layer were not interactive: the hit is dropped and no handler runs.
|
|
102
|
+
* @param hit - The intersection to trace.
|
|
103
|
+
* @returns The def, or `undefined` when the hit cannot be traced to one.
|
|
104
|
+
*/
|
|
105
|
+
export function resolveHit(hit) {
|
|
106
|
+
return resolvers.get(hit.object)?.(hit);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Where a missed click landed, in plan units.
|
|
110
|
+
*
|
|
111
|
+
* **A miss is not an empty canvas.** It means no handler took the click: a layer declaring no
|
|
112
|
+
* pointer handlers is never raycast by R3F in the first place, so the pointer may be sitting
|
|
113
|
+
* squarely on a wall, a model or a booth that simply does not listen. Searching the scene here is
|
|
114
|
+
* what makes the reported position agree with what the user is looking at — the same answer a hit
|
|
115
|
+
* event carries, arrived at a moment later and with no def to name.
|
|
116
|
+
*
|
|
117
|
+
* The search prunes hidden content for free: {@link usePointerProps} refuses the traversal into a
|
|
118
|
+
* hidden layer's subtree, which is three's own signal to stop descending.
|
|
119
|
+
* @param raycaster - The scene's raycaster, holding the ray R3F cast for this click.
|
|
120
|
+
* @param scene - The scene to search.
|
|
121
|
+
* @param space - The scene's space.
|
|
122
|
+
* @returns The nearest geometry under the pointer, or — where there is none — the floor beneath it.
|
|
123
|
+
*/
|
|
124
|
+
export function missedPoint(raycaster, scene, space) {
|
|
125
|
+
// Sorted nearest-first by three, so the first hit is the one the user is looking at.
|
|
126
|
+
const [nearest] = raycaster.intersectObject(scene, true);
|
|
127
|
+
if (nearest)
|
|
128
|
+
return space.worldToPlan(nearest.point);
|
|
129
|
+
return planePoint(raycaster.ray, space);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Where a ray crosses the plan plane — the floor under the pointer, whatever is drawn above it.
|
|
133
|
+
*
|
|
134
|
+
* The fallback for a click that reached neither a handler nor any geometry at all: past the edge of
|
|
135
|
+
* the plan there is nothing to report but the ground the ray carries on to.
|
|
136
|
+
*
|
|
137
|
+
* Both the crossing and the fallback are taken through the scene's space, because R3F casts its rays
|
|
138
|
+
* in world coordinates and a scene fitted to the canvas draws a plan unit as some other number of
|
|
139
|
+
* those. Without it the reported position is the world one wearing plan units' name.
|
|
140
|
+
* @param ray - The ray the pointer cast, in world space.
|
|
141
|
+
* @param space - The scene's space.
|
|
142
|
+
* @returns The crossing, in plan units, or — for a ray running parallel to the plan, which crosses
|
|
143
|
+
* it nowhere — where the ray started.
|
|
144
|
+
*/
|
|
145
|
+
export function planePoint(ray, space) {
|
|
146
|
+
return space.rayToPlan(ray) ?? space.worldToPlan(ray.origin);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Whether a press-and-release traveled far enough to be a drag rather than a click.
|
|
150
|
+
*
|
|
151
|
+
* R3F's `delta` is the pointer's travel since the press, rounded to whole pixels.
|
|
152
|
+
* @param event - The click event to judge.
|
|
153
|
+
* @returns Whether to treat it as a drag.
|
|
154
|
+
*/
|
|
155
|
+
function isDrag(event) {
|
|
156
|
+
return event.delta * event.delta > DRAG_SLOP_SQ_PX;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Squared pointer travel, in px², above which a release is a drag and not a click — so ~3.9 px of
|
|
160
|
+
* slop. Squared because that is how the imperative renderer's `dragThreshold` compared it, and this
|
|
161
|
+
* reproduces its feel exactly; a threshold in whole pixels would be four times looser.
|
|
162
|
+
*/
|
|
163
|
+
const DRAG_SLOP_SQ_PX = 15;
|
|
164
|
+
/**
|
|
165
|
+
* Mesh-to-def resolution for every mounted container, keyed weakly by mesh identity — so nothing has
|
|
166
|
+
* to be cleaned up per renderer, and two renderers holding the same defs never collide.
|
|
167
|
+
*/
|
|
168
|
+
const resolvers = 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
|