@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.
- package/README.md +145 -0
- package/dist/Renderer.d.ts +102 -0
- package/dist/Renderer.js +96 -0
- package/dist/controls/CameraControls.d.ts +108 -0
- package/dist/controls/CameraControls.js +203 -0
- package/dist/controls/Controls.d.ts +53 -0
- package/dist/controls/Controls.js +54 -0
- package/dist/controls/camera-api.d.ts +226 -0
- package/dist/controls/camera-api.js +179 -0
- package/dist/controls/gestures.d.ts +71 -0
- package/dist/controls/gestures.js +98 -0
- package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
- package/dist/controls/handlers/DoubleTapZoom.js +82 -0
- package/dist/controls/handlers/Inertia.d.ts +60 -0
- package/dist/controls/handlers/Inertia.js +222 -0
- package/dist/controls/handlers/Pan.d.ts +23 -0
- package/dist/controls/handlers/Pan.js +212 -0
- package/dist/controls/handlers/Pitch.d.ts +19 -0
- package/dist/controls/handlers/Pitch.js +200 -0
- package/dist/controls/handlers/Roll.d.ts +30 -0
- package/dist/controls/handlers/Roll.js +196 -0
- package/dist/controls/handlers/Zoom.d.ts +28 -0
- package/dist/controls/handlers/Zoom.js +34 -0
- package/dist/controls/hooks.d.ts +136 -0
- package/dist/controls/hooks.js +200 -0
- package/dist/controls/index.d.ts +9 -0
- package/dist/controls/index.js +7 -0
- package/dist/controls/zoom-to.d.ts +89 -0
- package/dist/controls/zoom-to.js +91 -0
- package/dist/core/InstanceUniforms.d.ts +108 -0
- package/dist/core/InstanceUniforms.js +298 -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 +155 -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 +228 -0
- package/dist/core/lines/BatchedLines.d.ts +137 -0
- package/dist/core/lines/BatchedLines.js +215 -0
- package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
- package/dist/core/lines/FrustumCullingLines.js +85 -0
- package/dist/core/lines/RaycastingLines.d.ts +45 -0
- package/dist/core/lines/RaycastingLines.js +91 -0
- package/dist/core/lines/lines-material.d.ts +47 -0
- package/dist/core/lines/lines-material.js +124 -0
- package/dist/core/shader-source.d.ts +70 -0
- package/dist/core/shader-source.js +110 -0
- package/dist/core/sprites/BatchedSprites.d.ts +115 -0
- package/dist/core/sprites/BatchedSprites.js +173 -0
- package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
- package/dist/core/sprites/RaycastingSprites.js +71 -0
- package/dist/core/sprites/sprite-material.d.ts +59 -0
- package/dist/core/sprites/sprite-material.js +192 -0
- package/dist/core/text/BatchedText.d.ts +233 -0
- package/dist/core/text/BatchedText.js +473 -0
- package/dist/core/text/MemberTexture.d.ts +90 -0
- package/dist/core/text/MemberTexture.js +289 -0
- package/dist/core/text/Text.d.ts +88 -0
- package/dist/core/text/Text.js +230 -0
- package/dist/core/text/batched-text-material.d.ts +56 -0
- package/dist/core/text/batched-text-material.js +123 -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 +50 -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 +22 -0
- package/dist/debug/components/Gui.js +42 -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 +104 -0
- package/dist/debug/components/Stats.d.ts +9 -0
- package/dist/debug/components/Stats.js +24 -0
- package/dist/debug/debug-chunk.d.ts +26 -0
- package/dist/debug/debug-chunk.js +32 -0
- package/dist/debug/debug-context.d.ts +41 -0
- package/dist/debug/debug-context.js +59 -0
- package/dist/debug/index.d.ts +41 -0
- package/dist/debug/index.js +43 -0
- package/dist/events/LayoutDriver.d.ts +36 -0
- package/dist/events/LayoutDriver.js +192 -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 +151 -0
- package/dist/events/layout-targets.js +397 -0
- package/dist/events/placement/box.d.ts +59 -0
- package/dist/events/placement/box.js +96 -0
- package/dist/events/placement/grid.d.ts +161 -0
- package/dist/events/placement/grid.js +235 -0
- package/dist/events/placement/index.d.ts +2 -0
- package/dist/events/placement/index.js +1 -0
- package/dist/events/placement/pass.d.ts +70 -0
- package/dist/events/placement/pass.js +617 -0
- package/dist/events/pointer.d.ts +194 -0
- package/dist/events/pointer.js +356 -0
- package/dist/external/ExternalCanvas.d.ts +56 -0
- package/dist/external/ExternalCanvas.js +177 -0
- package/dist/external/external-camera.d.ts +18 -0
- package/dist/external/external-camera.js +103 -0
- package/dist/external/external-events.d.ts +14 -0
- package/dist/external/external-events.js +33 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/objects/Background.d.ts +31 -0
- package/dist/objects/Background.js +37 -0
- package/dist/objects/containers/SlotContainer.d.ts +312 -0
- package/dist/objects/containers/SlotContainer.js +311 -0
- package/dist/objects/containers/hooks.d.ts +61 -0
- package/dist/objects/containers/hooks.js +196 -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 +33 -0
- package/dist/objects/containers/props.js +1 -0
- package/dist/objects/images/ImageInstance.d.ts +29 -0
- package/dist/objects/images/ImageInstance.js +152 -0
- package/dist/objects/images/Images.d.ts +49 -0
- package/dist/objects/images/Images.js +394 -0
- package/dist/objects/images/atlas-packer.d.ts +129 -0
- package/dist/objects/images/atlas-packer.js +287 -0
- package/dist/objects/images/image-container.d.ts +74 -0
- package/dist/objects/images/image-container.js +100 -0
- package/dist/objects/images/index.d.ts +2 -0
- package/dist/objects/images/index.js +1 -0
- package/dist/objects/images/pack-sources.d.ts +190 -0
- package/dist/objects/images/pack-sources.js +272 -0
- package/dist/objects/images/texture-budget.d.ts +120 -0
- package/dist/objects/images/texture-budget.js +215 -0
- package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
- package/dist/objects/images/worker/atlas-worker.js +123 -0
- package/dist/objects/images/worker/client.d.ts +62 -0
- package/dist/objects/images/worker/client.js +249 -0
- package/dist/objects/images/worker/index.d.ts +10 -0
- package/dist/objects/images/worker/index.js +1 -0
- package/dist/objects/layer/Layer.d.ts +28 -0
- package/dist/objects/layer/Layer.js +134 -0
- package/dist/objects/layer/index.d.ts +2 -0
- package/dist/objects/layer/index.js +1 -0
- package/dist/objects/layer/layer-context.d.ts +74 -0
- package/dist/objects/layer/layer-context.js +82 -0
- package/dist/objects/layer/mount-scheduler.d.ts +131 -0
- package/dist/objects/layer/mount-scheduler.js +289 -0
- package/dist/objects/layer/visibility-gui.d.ts +19 -0
- package/dist/objects/layer/visibility-gui.js +93 -0
- package/dist/objects/lighting/Lighting.d.ts +92 -0
- package/dist/objects/lighting/Lighting.js +384 -0
- package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
- package/dist/objects/lighting/OcclusionPasses.js +271 -0
- package/dist/objects/lighting/index.d.ts +11 -0
- package/dist/objects/lighting/index.js +10 -0
- package/dist/objects/lighting/lighting-gui.d.ts +24 -0
- package/dist/objects/lighting/lighting-gui.js +114 -0
- package/dist/objects/lighting/occluder.d.ts +34 -0
- package/dist/objects/lighting/occluder.js +62 -0
- package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
- package/dist/objects/lighting/occlusion-targets.js +276 -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 +31 -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 +92 -0
- package/dist/objects/materials/ColorMaterial.js +124 -0
- package/dist/objects/materials/LineMaterial.d.ts +44 -0
- package/dist/objects/materials/LineMaterial.js +74 -0
- package/dist/objects/materials/ModelMaterial.d.ts +53 -0
- package/dist/objects/materials/ModelMaterial.js +111 -0
- package/dist/objects/materials/TextureMaterial.d.ts +33 -0
- package/dist/objects/materials/TextureMaterial.js +48 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
- package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
- package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
- package/dist/objects/materials/effects/ao/index.d.ts +129 -0
- package/dist/objects/materials/effects/ao/index.js +72 -0
- package/dist/objects/materials/effects/color-dim.d.ts +115 -0
- package/dist/objects/materials/effects/color-dim.js +206 -0
- package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
- package/dist/objects/materials/effects/contact-shadow.js +167 -0
- package/dist/objects/materials/effects/effects-context.d.ts +53 -0
- package/dist/objects/materials/effects/effects-context.js +24 -0
- package/dist/objects/materials/shared.d.ts +24 -0
- package/dist/objects/materials/shared.js +72 -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 +186 -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 +123 -0
- package/dist/objects/models/model-container.js +207 -0
- package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
- package/dist/objects/shapes/ShapeInstance.js +39 -0
- package/dist/objects/shapes/Shapes.d.ts +21 -0
- package/dist/objects/shapes/Shapes.js +131 -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 +141 -0
- package/dist/objects/shapes/shape-container.js +242 -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 +43 -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 +121 -0
- package/dist/objects/text/text-container.js +229 -0
- package/dist/objects/text/text-layout.d.ts +92 -0
- package/dist/objects/text/text-layout.js +180 -0
- package/dist/space/Camera.d.ts +103 -0
- package/dist/space/Camera.js +321 -0
- package/dist/space/Scene.d.ts +47 -0
- package/dist/space/Scene.js +127 -0
- package/dist/space/Space.d.ts +325 -0
- package/dist/space/Space.js +359 -0
- package/dist/space/VisibleRect.d.ts +45 -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 +69 -0
- package/dist/space/clipping.js +130 -0
- package/dist/space/layout-measure.d.ts +67 -0
- package/dist/space/layout-measure.js +175 -0
- package/dist/space/viewport.d.ts +112 -0
- package/dist/space/viewport.js +67 -0
- package/dist/types/defs.d.ts +234 -0
- package/dist/types/defs.js +1 -0
- package/dist/types/events.d.ts +198 -0
- package/dist/types/events.js +1 -0
- package/dist/types/guards.d.ts +21 -0
- package/dist/types/guards.js +52 -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 +76 -0
- package/dist/update/store.d.ts +41 -0
- package/dist/update/store.js +104 -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/hot-state.d.ts +41 -0
- package/dist/utils/hot-state.js +37 -0
- package/dist/utils/logging.d.ts +29 -0
- package/dist/utils/logging.js +55 -0
- package/dist/utils/math.d.ts +52 -0
- package/dist/utils/math.js +76 -0
- package/package.json +79 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isLayerDef } from '../types/guards';
|
|
2
|
+
import { hotState } from '../utils/hot-state';
|
|
3
|
+
import { update } from './store';
|
|
4
|
+
/**
|
|
5
|
+
* Registers a def's sync closure, paired with the frame request that shows what it writes.
|
|
6
|
+
*
|
|
7
|
+
* The two are registered together rather than left to each caller because a slot write nobody asked
|
|
8
|
+
* a frame for is invisible: the renderer runs a demand frameloop, so the write lands in GPU memory
|
|
9
|
+
* and waits for a frame that never comes. Pairing them here is what makes every write path — React's
|
|
10
|
+
* and the frame loop's — correct without remembering anything. The frame loop's writes ask for a
|
|
11
|
+
* frame they are already inside, which costs one more after the camera stops.
|
|
12
|
+
* @param def - The def the instance component renders.
|
|
13
|
+
* @param sync - Writes the def's current state into its slot.
|
|
14
|
+
* @param invalidate - Requests the frame that draws it.
|
|
15
|
+
* @returns An unregister function, which removes this writer and leaves the def's others alone.
|
|
16
|
+
*/
|
|
17
|
+
export function registerSlotWriter(def, sync, invalidate) {
|
|
18
|
+
/** Writes the def's slot and asks for the frame that shows it. */
|
|
19
|
+
function write() {
|
|
20
|
+
sync();
|
|
21
|
+
invalidate();
|
|
22
|
+
}
|
|
23
|
+
const writers = slotWriters.get(def) ?? new Set();
|
|
24
|
+
writers.add(write);
|
|
25
|
+
slotWriters.set(def, writers);
|
|
26
|
+
return () => {
|
|
27
|
+
writers.delete(write);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Runs every write registered for a def — each of its slots, and the frame that draws them. A no-op
|
|
32
|
+
* for defs that are not mounted.
|
|
33
|
+
* @param def - The def to re-sync.
|
|
34
|
+
*/
|
|
35
|
+
export function syncNow(def) {
|
|
36
|
+
const writers = slotWriters.get(def);
|
|
37
|
+
if (!writers)
|
|
38
|
+
return;
|
|
39
|
+
// Over a copy: a write can re-claim a slot, and a re-claim mounts and unmounts writers.
|
|
40
|
+
for (const write of [...writers])
|
|
41
|
+
write();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Writes the defs a layout handler reported changed, by the cheapest route that is correct for
|
|
45
|
+
* each: a layer means its membership moved, which only React can reconcile, while anything else is
|
|
46
|
+
* a field write that goes straight into the slot, with no render at all behind the frame that draws
|
|
47
|
+
* it. This is what a layout handler returning defs buys — see `LayoutHandlers.onScale`.
|
|
48
|
+
* @param defs - The defs the handler reported changed, or nothing when it changed nothing.
|
|
49
|
+
*/
|
|
50
|
+
export function writeChanged(defs) {
|
|
51
|
+
if (!defs)
|
|
52
|
+
return;
|
|
53
|
+
for (const def of defs) {
|
|
54
|
+
if (isLayerDef(def))
|
|
55
|
+
update(def);
|
|
56
|
+
else
|
|
57
|
+
syncNow(def);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The write closures mounted instance components register for their defs — the single write path
|
|
62
|
+
* from a def's fields into its GPU slots, and the frame that draws what they wrote.
|
|
63
|
+
*
|
|
64
|
+
* Two callers run them: the instance's own effect (after `update(def)`), and the camera re-layout
|
|
65
|
+
* frame loop — the latter mutating defs and re-syncing them without going through React at all.
|
|
66
|
+
* Sharing one closure is what keeps the two paths from disagreeing.
|
|
67
|
+
*
|
|
68
|
+
* A **set** per def, because one def can be drawn by more than one mesh: a model merged into a part
|
|
69
|
+
* per source material has an instance in each, and an image whose source is split into tiles has one
|
|
70
|
+
* per placement. Holding a single writer let the last instance to mount overwrite its siblings, so
|
|
71
|
+
* every later write reached exactly one of a def's slots — a two-part model faded half of itself.
|
|
72
|
+
*
|
|
73
|
+
* Kept across hot updates, because an instance registers from a mount effect that a re-render does
|
|
74
|
+
* not re-run: a fresh map would make every later `update(def)` a silent no-op (see {@link hotState}).
|
|
75
|
+
*/
|
|
76
|
+
const slotWriters = hotState(import.meta.hot, 'slotWriters', () => new WeakMap());
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type LayerDef, type RenderableDef, type RenderableDefCollection } from '../types/defs';
|
|
2
|
+
/**
|
|
3
|
+
* Marks defs as changed after their fields, geometry, or `children` were mutated, so the renderer
|
|
4
|
+
* mirrors the mutation into GPU state.
|
|
5
|
+
*
|
|
6
|
+
* This is the renderer's only update signal. Mutate whatever you like — scalar fields, geometry in
|
|
7
|
+
* place, a layer's `children` array — then pass the defs you touched; for a membership change pass
|
|
8
|
+
* the parent layer, and for a reparent pass both parents. Calling it before the renderer mounts, or
|
|
9
|
+
* after it unmounts, is a safe no-op: defs are the source of truth and mounting performs a full
|
|
10
|
+
* sync. Calling it on an unchanged def is harmless.
|
|
11
|
+
* @param defs - Defs that changed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function update(...defs: RenderableDef[]): void;
|
|
14
|
+
/**
|
|
15
|
+
* Subscribes to a def's change notifications. Internal — components reach this through
|
|
16
|
+
* `useDefSync` and `useStagedChildren`.
|
|
17
|
+
* @param def - The def to watch.
|
|
18
|
+
* @param listener - Called after every {@link update} touching the def.
|
|
19
|
+
* @returns An unsubscribe function.
|
|
20
|
+
*/
|
|
21
|
+
export declare function subscribeDef(def: RenderableDef, listener: () => void): () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Reads a def's current version — the value `useSyncExternalStore` snapshots.
|
|
24
|
+
* @param def - The def to read.
|
|
25
|
+
* @returns The version, or `0` when nothing is subscribed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function versionOf(def: RenderableDef): number;
|
|
28
|
+
/**
|
|
29
|
+
* Reads a layer's children as a version-keyed snapshot. Internal — components reach this through
|
|
30
|
+
* `useStagedChildren`.
|
|
31
|
+
*
|
|
32
|
+
* The layer's `children` array is mutated in place, so its identity can never signal a membership
|
|
33
|
+
* change to memoization. The snapshot restores that signal at the store level: it is rebuilt as a
|
|
34
|
+
* fresh copy exactly when the layer's version has moved, and the cached copy is returned otherwise —
|
|
35
|
+
* the identity stability `useSyncExternalStore` requires between updates, and the identity change
|
|
36
|
+
* downstream memoization invalidates on.
|
|
37
|
+
* @param def - The layer whose children to snapshot.
|
|
38
|
+
* @returns The current snapshot; a new array iff the layer was updated since the last call.
|
|
39
|
+
*/
|
|
40
|
+
export declare function childrenOf<T extends RenderableDef>(def: LayerDef<T>): Readonly<RenderableDefCollection<T>>;
|
|
41
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { hotState } from '../utils/hot-state';
|
|
2
|
+
/**
|
|
3
|
+
* Marks defs as changed after their fields, geometry, or `children` were mutated, so the renderer
|
|
4
|
+
* mirrors the mutation into GPU state.
|
|
5
|
+
*
|
|
6
|
+
* This is the renderer's only update signal. Mutate whatever you like — scalar fields, geometry in
|
|
7
|
+
* place, a layer's `children` array — then pass the defs you touched; for a membership change pass
|
|
8
|
+
* the parent layer, and for a reparent pass both parents. Calling it before the renderer mounts, or
|
|
9
|
+
* after it unmounts, is a safe no-op: defs are the source of truth and mounting performs a full
|
|
10
|
+
* sync. Calling it on an unchanged def is harmless.
|
|
11
|
+
* @param defs - Defs that changed.
|
|
12
|
+
*/
|
|
13
|
+
export function update(...defs) {
|
|
14
|
+
for (const def of defs)
|
|
15
|
+
notify(def);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Subscribes to a def's change notifications. Internal — components reach this through
|
|
19
|
+
* `useDefSync` and `useStagedChildren`.
|
|
20
|
+
* @param def - The def to watch.
|
|
21
|
+
* @param listener - Called after every {@link update} touching the def.
|
|
22
|
+
* @returns An unsubscribe function.
|
|
23
|
+
*/
|
|
24
|
+
export function subscribeDef(def, listener) {
|
|
25
|
+
const entry = entryFor(def);
|
|
26
|
+
entry.listeners.add(listener);
|
|
27
|
+
return () => {
|
|
28
|
+
const current = subscriptions.get(def);
|
|
29
|
+
if (!current)
|
|
30
|
+
return;
|
|
31
|
+
current.listeners.delete(listener);
|
|
32
|
+
// The entry outlives its listeners deliberately. `useSyncExternalStore` re-subscribes whenever
|
|
33
|
+
// its subscribe closure changes identity, which happens on every render unless the closure is
|
|
34
|
+
// memoized; dropping the entry on the last unsubscribe would reset the version to 0 mid-render,
|
|
35
|
+
// costing a spurious re-render and re-sync and breaking the monotonicity the `childrenOf` cache
|
|
36
|
+
// relies on to signal in-place mutation. Retaining it costs nothing: the map is weak, so
|
|
37
|
+
// an entry is collected with its def.
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads a def's current version — the value `useSyncExternalStore` snapshots.
|
|
42
|
+
* @param def - The def to read.
|
|
43
|
+
* @returns The version, or `0` when nothing is subscribed.
|
|
44
|
+
*/
|
|
45
|
+
export function versionOf(def) {
|
|
46
|
+
return subscriptions.get(def)?.version ?? 0;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reads a layer's children as a version-keyed snapshot. Internal — components reach this through
|
|
50
|
+
* `useStagedChildren`.
|
|
51
|
+
*
|
|
52
|
+
* The layer's `children` array is mutated in place, so its identity can never signal a membership
|
|
53
|
+
* change to memoization. The snapshot restores that signal at the store level: it is rebuilt as a
|
|
54
|
+
* fresh copy exactly when the layer's version has moved, and the cached copy is returned otherwise —
|
|
55
|
+
* the identity stability `useSyncExternalStore` requires between updates, and the identity change
|
|
56
|
+
* downstream memoization invalidates on.
|
|
57
|
+
* @param def - The layer whose children to snapshot.
|
|
58
|
+
* @returns The current snapshot; a new array iff the layer was updated since the last call.
|
|
59
|
+
*/
|
|
60
|
+
export function childrenOf(def) {
|
|
61
|
+
const entry = entryFor(def);
|
|
62
|
+
if (entry.childrenSnapshot?.version !== entry.version) {
|
|
63
|
+
entry.childrenSnapshot = { version: entry.version, snapshot: [...def.children] };
|
|
64
|
+
}
|
|
65
|
+
// A layer's children are homogeneous (`RenderableDefCollection<T>`), so the erased cache entry is
|
|
66
|
+
// safely re-narrowed to the layer's own child type.
|
|
67
|
+
return entry.childrenSnapshot.snapshot;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns a def's subscription entry, creating it on first contact.
|
|
71
|
+
* @param def - The def to look up.
|
|
72
|
+
* @returns The entry.
|
|
73
|
+
*/
|
|
74
|
+
function entryFor(def) {
|
|
75
|
+
let entry = subscriptions.get(def);
|
|
76
|
+
if (!entry) {
|
|
77
|
+
entry = { version: 0, listeners: new Set() };
|
|
78
|
+
subscriptions.set(def, entry);
|
|
79
|
+
}
|
|
80
|
+
return entry;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Bumps one def's version and notifies its listeners.
|
|
84
|
+
* @param def - The def that changed.
|
|
85
|
+
*/
|
|
86
|
+
function notify(def) {
|
|
87
|
+
const entry = subscriptions.get(def);
|
|
88
|
+
if (!entry)
|
|
89
|
+
return;
|
|
90
|
+
entry.version++;
|
|
91
|
+
for (const listener of entry.listeners)
|
|
92
|
+
listener();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Per-def subscription state, created on first subscribe and dropped when the last subscriber
|
|
96
|
+
* leaves. Keyed weakly by def identity: a def the renderer never mounted has no entry, which is
|
|
97
|
+
* exactly why {@link update} on it is a no-op rather than an error.
|
|
98
|
+
*
|
|
99
|
+
* Kept across hot updates, because a component subscribes once and `useSyncExternalStore` re-runs
|
|
100
|
+
* that only when the subscribe function changes identity: a fresh map would leave every mounted
|
|
101
|
+
* subscriber listening to entries no `update` can reach, and that "never mounted" no-op would start
|
|
102
|
+
* swallowing real changes (see {@link hotState}).
|
|
103
|
+
*/
|
|
104
|
+
const subscriptions = hotState(import.meta.hot, 'subscriptions', () => new WeakMap());
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether two arrays hold the same values in the same positions, compared by identity.
|
|
3
|
+
*
|
|
4
|
+
* The batched classes keep an applied order and are handed a desired one on every membership
|
|
5
|
+
* change; this is what lets an unchanged order skip the work of applying it — which matters most
|
|
6
|
+
* where applying it is expensive (repacking glyphs or endpoints) and the caller cannot know whether
|
|
7
|
+
* anything moved.
|
|
8
|
+
* @param a - The first sequence.
|
|
9
|
+
* @param b - The second sequence.
|
|
10
|
+
* @returns Whether they match.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sameSequence<T>(a: readonly T[], b: readonly T[]): boolean;
|
|
13
|
+
//# sourceMappingURL=arrays.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether two arrays hold the same values in the same positions, compared by identity.
|
|
3
|
+
*
|
|
4
|
+
* The batched classes keep an applied order and are handed a desired one on every membership
|
|
5
|
+
* change; this is what lets an unchanged order skip the work of applying it — which matters most
|
|
6
|
+
* where applying it is expensive (repacking glyphs or endpoints) and the caller cannot know whether
|
|
7
|
+
* anything moved.
|
|
8
|
+
* @param a - The first sequence.
|
|
9
|
+
* @param b - The second sequence.
|
|
10
|
+
* @returns Whether they match.
|
|
11
|
+
*/
|
|
12
|
+
export function sameSequence(a, b) {
|
|
13
|
+
if (a.length !== b.length)
|
|
14
|
+
return false;
|
|
15
|
+
for (let i = 0; i < a.length; i++) {
|
|
16
|
+
if (a[i] !== b[i])
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type RgbaColor } from 'colord';
|
|
2
|
+
import { type ColorInput } from '../types/defs';
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes a {@link ColorInput} into an rgba color object.
|
|
5
|
+
* @param colorInput color as a css color string or a numeric 0xRRGGBB value
|
|
6
|
+
* @returns rgba color (r/g/b 0-255, a 0-1), or undefined if the input is not a valid color
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeColor(colorInput: ColorInput): RgbaColor | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a color carries an alpha below 1, and so belongs in a translucent batch.
|
|
11
|
+
*
|
|
12
|
+
* A color that does not parse counts as **opaque**: `ShapeInstance` reports it and leaves the slot's
|
|
13
|
+
* tint alone, so moving a shape into a later render pass on the strength of a value the renderer
|
|
14
|
+
* could not read would compound one fault with another.
|
|
15
|
+
* @param colorInput color as a css color string or a numeric 0xRRGGBB value
|
|
16
|
+
* @returns true when the color is translucent
|
|
17
|
+
*/
|
|
18
|
+
export declare function isTranslucentColor(colorInput: ColorInput): boolean;
|
|
19
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { colord, extend } from 'colord';
|
|
2
|
+
import namesPlugin from 'colord/plugins/names';
|
|
3
|
+
extend([namesPlugin]);
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes a {@link ColorInput} into an rgba color object.
|
|
6
|
+
* @param colorInput color as a css color string or a numeric 0xRRGGBB value
|
|
7
|
+
* @returns rgba color (r/g/b 0-255, a 0-1), or undefined if the input is not a valid color
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeColor(colorInput) {
|
|
10
|
+
const colorString = typeof colorInput === 'string' ? colorInput : `#${colorInput.toString(16).padStart(6, '0')}`;
|
|
11
|
+
const color = colord(colorString);
|
|
12
|
+
if (!color.isValid())
|
|
13
|
+
return undefined;
|
|
14
|
+
return color.toRgb();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether a color carries an alpha below 1, and so belongs in a translucent batch.
|
|
18
|
+
*
|
|
19
|
+
* A color that does not parse counts as **opaque**: `ShapeInstance` reports it and leaves the slot's
|
|
20
|
+
* tint alone, so moving a shape into a later render pass on the strength of a value the renderer
|
|
21
|
+
* could not read would compound one fault with another.
|
|
22
|
+
* @param colorInput color as a css color string or a numeric 0xRRGGBB value
|
|
23
|
+
* @returns true when the color is translucent
|
|
24
|
+
*/
|
|
25
|
+
export function isTranslucentColor(colorInput) {
|
|
26
|
+
return (normalizeColor(colorInput)?.a ?? 1) < 1;
|
|
27
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level state that survives a hot update.
|
|
3
|
+
*
|
|
4
|
+
* A registry held in a module-level `const` is lost the moment Vite re-evaluates that module, and the
|
|
5
|
+
* renderer has several that hold live wiring rather than a cache: which def a mesh's hits resolve to,
|
|
6
|
+
* which closures `update(def)` reaches, which recognizer a canvas's gestures share. Each is filled
|
|
7
|
+
* once, from a mount effect, so React Refresh — which re-renders without re-running an effect whose
|
|
8
|
+
* dependencies did not change — never refills it. The module comes back empty, every lookup through
|
|
9
|
+
* it misses, and the feature goes quiet with no error anywhere: picking stops resolving defs, updates
|
|
10
|
+
* stop reaching slots.
|
|
11
|
+
*/
|
|
12
|
+
/** The part of Vite's `import.meta.hot` this needs, taken structurally so nothing imports its type. */
|
|
13
|
+
interface HotModule {
|
|
14
|
+
/**
|
|
15
|
+
* Carried over to the next instance of the same module by every hot update.
|
|
16
|
+
*
|
|
17
|
+
* Optional because a host may offer a hot context without one: vitest defines `import.meta.hot` for
|
|
18
|
+
* its own watch-mode reload and leaves `data` unset, so reading through it throws at import time
|
|
19
|
+
* and takes the importing module down with it.
|
|
20
|
+
*/
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The value a module keeps across its own hot updates, built on first use.
|
|
25
|
+
*
|
|
26
|
+
* Each module passes its own `import.meta.hot`, which is `undefined` in a production build — Vite
|
|
27
|
+
* replaces the expression statically, so the branch and this whole module fall away and the call is
|
|
28
|
+
* nothing but the builder. That is also what makes this safe to reach for: it changes development
|
|
29
|
+
* only, and the value a module gets is the same value either way.
|
|
30
|
+
* @param hot - The calling module's `import.meta.hot`.
|
|
31
|
+
* @param key - Names the value within the module, so one module may keep several.
|
|
32
|
+
* @param build - Builds the value the first time the module is evaluated.
|
|
33
|
+
* @returns The value, built now or carried over from the instance before.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const sources = hotState(import.meta.hot, 'sources', () => new WeakMap<Object3D, HitSource>());
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function hotState<T>(hot: HotModule | undefined, key: string, build: () => T): T;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=hot-state.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level state that survives a hot update.
|
|
3
|
+
*
|
|
4
|
+
* A registry held in a module-level `const` is lost the moment Vite re-evaluates that module, and the
|
|
5
|
+
* renderer has several that hold live wiring rather than a cache: which def a mesh's hits resolve to,
|
|
6
|
+
* which closures `update(def)` reaches, which recognizer a canvas's gestures share. Each is filled
|
|
7
|
+
* once, from a mount effect, so React Refresh — which re-renders without re-running an effect whose
|
|
8
|
+
* dependencies did not change — never refills it. The module comes back empty, every lookup through
|
|
9
|
+
* it misses, and the feature goes quiet with no error anywhere: picking stops resolving defs, updates
|
|
10
|
+
* stop reaching slots.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The value a module keeps across its own hot updates, built on first use.
|
|
14
|
+
*
|
|
15
|
+
* Each module passes its own `import.meta.hot`, which is `undefined` in a production build — Vite
|
|
16
|
+
* replaces the expression statically, so the branch and this whole module fall away and the call is
|
|
17
|
+
* nothing but the builder. That is also what makes this safe to reach for: it changes development
|
|
18
|
+
* only, and the value a module gets is the same value either way.
|
|
19
|
+
* @param hot - The calling module's `import.meta.hot`.
|
|
20
|
+
* @param key - Names the value within the module, so one module may keep several.
|
|
21
|
+
* @param build - Builds the value the first time the module is evaluated.
|
|
22
|
+
* @returns The value, built now or carried over from the instance before.
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const sources = hotState(import.meta.hot, 'sources', () => new WeakMap<Object3D, HitSource>());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function hotState(hot, key, build) {
|
|
29
|
+
const data = hot?.data;
|
|
30
|
+
if (!data)
|
|
31
|
+
return build();
|
|
32
|
+
// `in` rather than a comparison against undefined, so a value that is legitimately undefined is
|
|
33
|
+
// kept rather than rebuilt on every update.
|
|
34
|
+
if (!(key in data))
|
|
35
|
+
data[key] = build();
|
|
36
|
+
return data[key];
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Debugger } from 'debug';
|
|
2
|
+
import { type SceneDef } from '../types/defs';
|
|
3
|
+
/** Logger interface */
|
|
4
|
+
export interface Logger {
|
|
5
|
+
/** Debug level logger */
|
|
6
|
+
debug: Debugger;
|
|
7
|
+
/** Info level logger */
|
|
8
|
+
info: Debugger;
|
|
9
|
+
/** Warn level logger */
|
|
10
|
+
warn: Debugger;
|
|
11
|
+
/** Error level logger */
|
|
12
|
+
error: Debugger;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a logger for one namespace, nested under the root namespace.
|
|
16
|
+
*
|
|
17
|
+
* Each level prints only if you enable its namespace, for example with
|
|
18
|
+
* `debug.enable('efp:renderer:*')`.
|
|
19
|
+
* @param namespace - The namespace to nest under the root namespace.
|
|
20
|
+
* @returns A logger with four levels, all under that namespace.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createLogger(namespace: string): Logger;
|
|
23
|
+
/**
|
|
24
|
+
* Prints the layer tree of the given scene to the console.
|
|
25
|
+
* @param scene - Scene tree to print
|
|
26
|
+
* @param logger - The logger to use to print the tree.
|
|
27
|
+
*/
|
|
28
|
+
export declare function printScene(scene: SceneDef, logger: Debugger): void;
|
|
29
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import createLog from 'debug';
|
|
3
|
+
import { isLayerDefs } from '../types/guards';
|
|
4
|
+
const rootNamespace = 'efp:renderer';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a logger for one namespace, nested under the root namespace.
|
|
7
|
+
*
|
|
8
|
+
* Each level prints only if you enable its namespace, for example with
|
|
9
|
+
* `debug.enable('efp:renderer:*')`.
|
|
10
|
+
* @param namespace - The namespace to nest under the root namespace.
|
|
11
|
+
* @returns A logger with four levels, all under that namespace.
|
|
12
|
+
*/
|
|
13
|
+
export function createLogger(namespace) {
|
|
14
|
+
const fullNamespace = namespace ? `${rootNamespace}:${namespace}` : rootNamespace;
|
|
15
|
+
const info = createLog(fullNamespace);
|
|
16
|
+
// Each wrapper reads the console method at call time, not at setup time. A plain
|
|
17
|
+
// `console.warn.bind(console)` freezes the method the console object held at that
|
|
18
|
+
// moment. A test can later replace `console.warn`, for example with
|
|
19
|
+
// `vi.spyOn(console, 'warn')`. The frozen copy still calls the old method, so the
|
|
20
|
+
// spy never sees the call.
|
|
21
|
+
info.log = (...args) => console.info(...args);
|
|
22
|
+
const debug = info.extend('debug');
|
|
23
|
+
debug.log = (...args) => console.debug(...args);
|
|
24
|
+
const warn = info.extend('warn');
|
|
25
|
+
warn.log = (...args) => console.warn(...args);
|
|
26
|
+
const error = info.extend('error');
|
|
27
|
+
error.log = (...args) => console.error(...args);
|
|
28
|
+
return {
|
|
29
|
+
debug,
|
|
30
|
+
info,
|
|
31
|
+
warn,
|
|
32
|
+
error,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Prints the layer tree of the given scene to the console.
|
|
37
|
+
* @param scene - Scene tree to print
|
|
38
|
+
* @param logger - The logger to use to print the tree.
|
|
39
|
+
*/
|
|
40
|
+
export function printScene(scene, logger) {
|
|
41
|
+
function printLayer(def) {
|
|
42
|
+
const fullName = def.name;
|
|
43
|
+
if (isLayerDefs(def.children)) {
|
|
44
|
+
console.groupCollapsed(fullName);
|
|
45
|
+
def.children.forEach((c) => printLayer(c));
|
|
46
|
+
console.groupEnd();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
logger(fullName);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!logger.enabled)
|
|
53
|
+
return;
|
|
54
|
+
printLayer(scene.rootLayer);
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Camera, type Matrix4 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Holds a value inside an inclusive range.
|
|
4
|
+
*
|
|
5
|
+
* Written out rather than reached for from a library: three's own `clamp` is only exported as part
|
|
6
|
+
* of the whole `MathUtils` namespace object, and one import of that object pins every function it
|
|
7
|
+
* holds against tree-shaking.
|
|
8
|
+
* @param value - The value to clamp.
|
|
9
|
+
* @param min - The lowest value to return.
|
|
10
|
+
* @param max - The highest value to return.
|
|
11
|
+
* @returns The value, held within `[min, max]`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Wraps a value into `[0, modulus)`, keeping the sign of the modulus rather than of the value —
|
|
16
|
+
* which is what `%` does not do, and why an angle run through it can come back negative.
|
|
17
|
+
*
|
|
18
|
+
* Written out for the same reason {@link clamp} is: three has this one too, and reaching for it
|
|
19
|
+
* would pin the whole `MathUtils` namespace against tree-shaking.
|
|
20
|
+
* @param value - The value to wrap, of any magnitude or sign.
|
|
21
|
+
* @param modulus - The size of the range to wrap into.
|
|
22
|
+
* @returns The value in `[0, modulus)`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function euclideanModulo(value: number, modulus: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* The length one unit along an axis is drawn at, which is the scale the matrix applies there.
|
|
27
|
+
*
|
|
28
|
+
* Reads the elements directly rather than through a vector, because picking calls this per instance
|
|
29
|
+
* per test and a scratch vector would be one more shared object to reason about.
|
|
30
|
+
* @param matrix - The matrix to measure.
|
|
31
|
+
* @param axis - Which axis. `0` is x, `1` is y and `2` is z.
|
|
32
|
+
* @returns The transformed axis's length.
|
|
33
|
+
*/
|
|
34
|
+
export declare function axisLength(matrix: Matrix4, axis: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* How far the camera's own axis travels before it meets a batch's own plane, in world units.
|
|
37
|
+
*
|
|
38
|
+
* That is the depth a plan-unit size is exact at, so it is what the shader cancels the perspective
|
|
39
|
+
* divide against. It has to agree with the depth `measurePixelSize` in `space/layout-measure.ts`
|
|
40
|
+
* works at, because a caller's `onScale` rule sizes a def in plan units from the pixel size that
|
|
41
|
+
* function reports. Without controls mounted that function reads this same crossing point. With
|
|
42
|
+
* them it reads their distance to their target, and the two agree while the target sits on the
|
|
43
|
+
* plan, which is what orbiting a floor plan keeps true.
|
|
44
|
+
*
|
|
45
|
+
* The plane is the batch's own, taken from its world matrix, because a sprite batch draws at plan
|
|
46
|
+
* `z = 0` and whatever places the plan places the mesh with it.
|
|
47
|
+
* @param camera - The camera this draw uses.
|
|
48
|
+
* @param planToWorld - The batch's world matrix.
|
|
49
|
+
* @returns The distance in world units, or `0` for a camera that never meets the plane ahead of it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function aimDepth(camera: Camera, planToWorld: Matrix4): number;
|
|
52
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Vector3 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Holds a value inside an inclusive range.
|
|
4
|
+
*
|
|
5
|
+
* Written out rather than reached for from a library: three's own `clamp` is only exported as part
|
|
6
|
+
* of the whole `MathUtils` namespace object, and one import of that object pins every function it
|
|
7
|
+
* holds against tree-shaking.
|
|
8
|
+
* @param value - The value to clamp.
|
|
9
|
+
* @param min - The lowest value to return.
|
|
10
|
+
* @param max - The highest value to return.
|
|
11
|
+
* @returns The value, held within `[min, max]`.
|
|
12
|
+
*/
|
|
13
|
+
export function clamp(value, min, max) {
|
|
14
|
+
return Math.min(Math.max(value, min), max);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Wraps a value into `[0, modulus)`, keeping the sign of the modulus rather than of the value —
|
|
18
|
+
* which is what `%` does not do, and why an angle run through it can come back negative.
|
|
19
|
+
*
|
|
20
|
+
* Written out for the same reason {@link clamp} is: three has this one too, and reaching for it
|
|
21
|
+
* would pin the whole `MathUtils` namespace against tree-shaking.
|
|
22
|
+
* @param value - The value to wrap, of any magnitude or sign.
|
|
23
|
+
* @param modulus - The size of the range to wrap into.
|
|
24
|
+
* @returns The value in `[0, modulus)`.
|
|
25
|
+
*/
|
|
26
|
+
export function euclideanModulo(value, modulus) {
|
|
27
|
+
return ((value % modulus) + modulus) % modulus;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The length one unit along an axis is drawn at, which is the scale the matrix applies there.
|
|
31
|
+
*
|
|
32
|
+
* Reads the elements directly rather than through a vector, because picking calls this per instance
|
|
33
|
+
* per test and a scratch vector would be one more shared object to reason about.
|
|
34
|
+
* @param matrix - The matrix to measure.
|
|
35
|
+
* @param axis - Which axis. `0` is x, `1` is y and `2` is z.
|
|
36
|
+
* @returns The transformed axis's length.
|
|
37
|
+
*/
|
|
38
|
+
export function axisLength(matrix, axis) {
|
|
39
|
+
const elements = matrix.elements;
|
|
40
|
+
const at = axis * 4;
|
|
41
|
+
return Math.hypot(elements[at], elements[at + 1], elements[at + 2]);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* How far the camera's own axis travels before it meets a batch's own plane, in world units.
|
|
45
|
+
*
|
|
46
|
+
* That is the depth a plan-unit size is exact at, so it is what the shader cancels the perspective
|
|
47
|
+
* divide against. It has to agree with the depth `measurePixelSize` in `space/layout-measure.ts`
|
|
48
|
+
* works at, because a caller's `onScale` rule sizes a def in plan units from the pixel size that
|
|
49
|
+
* function reports. Without controls mounted that function reads this same crossing point. With
|
|
50
|
+
* them it reads their distance to their target, and the two agree while the target sits on the
|
|
51
|
+
* plan, which is what orbiting a floor plan keeps true.
|
|
52
|
+
*
|
|
53
|
+
* The plane is the batch's own, taken from its world matrix, because a sprite batch draws at plan
|
|
54
|
+
* `z = 0` and whatever places the plan places the mesh with it.
|
|
55
|
+
* @param camera - The camera this draw uses.
|
|
56
|
+
* @param planToWorld - The batch's world matrix.
|
|
57
|
+
* @returns The distance in world units, or `0` for a camera that never meets the plane ahead of it.
|
|
58
|
+
*/
|
|
59
|
+
export function aimDepth(camera, planToWorld) {
|
|
60
|
+
// A camera looks down its own negative z.
|
|
61
|
+
const direction = _direction.setFromMatrixColumn(camera.matrixWorld, 2).normalize().negate();
|
|
62
|
+
const normal = _normal.setFromMatrixColumn(planToWorld, 2).normalize();
|
|
63
|
+
const facing = direction.dot(normal);
|
|
64
|
+
// The camera looks along the plane and never meets it.
|
|
65
|
+
if (facing === 0)
|
|
66
|
+
return 0;
|
|
67
|
+
const eye = _eye.setFromMatrixPosition(camera.matrixWorld);
|
|
68
|
+
const depth = _origin.setFromMatrixPosition(planToWorld).sub(eye).dot(normal) / facing;
|
|
69
|
+
// Behind the camera is no measurement, so the shader keeps its plan-unit sizing instead.
|
|
70
|
+
return depth > 0 ? depth : 0;
|
|
71
|
+
}
|
|
72
|
+
// Scratch for aimDepth, filled and consumed within one call.
|
|
73
|
+
const _direction = new Vector3();
|
|
74
|
+
const _normal = new Vector3();
|
|
75
|
+
const _eye = new Vector3();
|
|
76
|
+
const _origin = new Vector3();
|