@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,105 @@
|
|
|
1
|
+
import { type Intersection } from 'three';
|
|
2
|
+
import { type BatchedTextExt } from '../../core/text/BatchedText';
|
|
3
|
+
import { TextExt } from '../../core/text/Text';
|
|
4
|
+
import { type RenderableDef, type TextDef } from '../../types/defs';
|
|
5
|
+
import { SlotContainer } from '../containers';
|
|
6
|
+
/** One def's members: a `TextExt` per line, and the batch slot each of them occupies. */
|
|
7
|
+
export interface LineGroup {
|
|
8
|
+
/** The members, in the def's line order. */
|
|
9
|
+
readonly texts: readonly TextExt[];
|
|
10
|
+
/** Each member's batch slot, index-parallel to {@link texts}. */
|
|
11
|
+
readonly slots: readonly number[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One text layer's bookkeeping: a group of members per def, and the paint order across them.
|
|
15
|
+
*
|
|
16
|
+
* The slot a def claims here is a **group**, not a batch member — a `TextDef` holds an array of
|
|
17
|
+
* lines and each line is its own `TextExt`. That is the one place this differs from the shape and
|
|
18
|
+
* image containers, and it falls out of the def type rather than being a choice: the lines of one
|
|
19
|
+
* def are laid out together (a centered block needs every line's height before it can place the
|
|
20
|
+
* first), so they cannot be independent instances.
|
|
21
|
+
*
|
|
22
|
+
* Group size is the slot's resource, so a def that gains or loses a line re-claims automatically —
|
|
23
|
+
* which is what makes `TextDef.lines` updatable here, where the imperative renderer required it to
|
|
24
|
+
* be fixed.
|
|
25
|
+
*
|
|
26
|
+
* Ordering differs too: a text batch draws through packed glyph attributes rather than a sortable
|
|
27
|
+
* draw list, so {@link applyOrder} hands the batch a rebuilt member list instead of labeling slots,
|
|
28
|
+
* and the inherited `customSort` goes unused.
|
|
29
|
+
*/
|
|
30
|
+
export declare class TextContainer extends SlotContainer<number, BatchedTextExt, TextDef> {
|
|
31
|
+
/** Groups by slot, with holes for released ones. */
|
|
32
|
+
private readonly groups;
|
|
33
|
+
private readonly freeGroups;
|
|
34
|
+
/**
|
|
35
|
+
* Batch member to the group holding it, so a hit — which names a member — resolves to a def. The
|
|
36
|
+
* batch reuses a released member's slot, so entries are dropped with the group that owned them.
|
|
37
|
+
*/
|
|
38
|
+
private readonly groupByMember;
|
|
39
|
+
private groupCount;
|
|
40
|
+
/** The last order applied to the batch, kept so an unchanged one can skip the repack. */
|
|
41
|
+
private appliedOrder;
|
|
42
|
+
private syncScheduled;
|
|
43
|
+
private readonly invalidate;
|
|
44
|
+
/**
|
|
45
|
+
* Builds a text layer's bookkeeping. The batch itself is R3F's — the component renders
|
|
46
|
+
* `<BatchedText>` and hands it over through {@link SlotContainer.bind} — so R3F disposes it on
|
|
47
|
+
* unmount.
|
|
48
|
+
* @param invalidate - R3F's frame request, called when an asynchronous sync completes. Stable for
|
|
49
|
+
* the lifetime of a canvas, so capturing it here is safe.
|
|
50
|
+
*/
|
|
51
|
+
constructor(invalidate: () => void);
|
|
52
|
+
/**
|
|
53
|
+
* The members backing one def.
|
|
54
|
+
* @param slot - The def's group.
|
|
55
|
+
* @returns Its members and their batch slots.
|
|
56
|
+
*/
|
|
57
|
+
linesAt(slot: number): LineGroup;
|
|
58
|
+
/**
|
|
59
|
+
* Requests one batch sync, coalescing every request made in the same tick.
|
|
60
|
+
*
|
|
61
|
+
* Eager rather than waiting for `onBeforeRender`, because syncing is asynchronous — troika loads
|
|
62
|
+
* and parses the font — and under on-demand rendering a sync started during a frame has nothing to
|
|
63
|
+
* request the *next* frame when it completes. The callback carries R3F's `invalidate`, which is
|
|
64
|
+
* what closes that loop; without it new text appears only when something else happens to
|
|
65
|
+
* invalidate.
|
|
66
|
+
*/
|
|
67
|
+
scheduleSync(): void;
|
|
68
|
+
claim(def: RenderableDef, lineCount: number): number;
|
|
69
|
+
setVisibleAt(slot: number, visible: boolean): void;
|
|
70
|
+
/**
|
|
71
|
+
* Sets the draw order for the whole layer, then syncs.
|
|
72
|
+
*
|
|
73
|
+
* The sync rides along so one structural change costs exactly one sync — a mount writes content
|
|
74
|
+
* through its instances in the same effect flush, and every request in the tick coalesces.
|
|
75
|
+
* @param defs - The layer's texts, in paint order.
|
|
76
|
+
* @param layerDimmed - Whether the layer is dimmed, inherited by defs without a `dim` of their own.
|
|
77
|
+
*/
|
|
78
|
+
setPaintOrder(defs: readonly TextDef[], layerDimmed: boolean): void;
|
|
79
|
+
/**
|
|
80
|
+
* A hit names the batch member it struck, and a def owns one member per line.
|
|
81
|
+
* @param hit - The intersection to trace.
|
|
82
|
+
* @returns The def, or `undefined` when no claimed group holds that member.
|
|
83
|
+
*/
|
|
84
|
+
defFromHit(hit: Intersection): RenderableDef | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Rebuilds the flat member order from the current defs and hands it to the batch, unless nothing
|
|
87
|
+
* moved.
|
|
88
|
+
*
|
|
89
|
+
* Skipping an unchanged order matters more here than for the other containers: a text batch has no
|
|
90
|
+
* `customSort`, so reordering means repacking the glyph attributes, and `useStagedChildren` hands out
|
|
91
|
+
* a fresh snapshot on *every* layer update — including ones that moved nothing.
|
|
92
|
+
*/
|
|
93
|
+
protected applyOrder(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Per member, not per def: a def's lines are separate batch members, and the uniform row is
|
|
96
|
+
* addressed by member. One `dim` on the def therefore writes each of its lines, exactly as
|
|
97
|
+
* visibility does.
|
|
98
|
+
* @param slot - The def's group.
|
|
99
|
+
* @param amount - The encoded amount.
|
|
100
|
+
*/
|
|
101
|
+
protected writeDim(slot: number, amount: number): void;
|
|
102
|
+
protected addInstance(lineCount: number): number;
|
|
103
|
+
protected removeInstance(slot: number): void;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=text-container.d.ts.map
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { TextExt } from '../../core/text/Text';
|
|
2
|
+
import { sameSequence } from '../../utils/arrays';
|
|
3
|
+
import { SlotContainer } from '../containers';
|
|
4
|
+
import { dimOrdered, isDimmed } from '../materials/dim';
|
|
5
|
+
import { LAYOUT_FONT_SIZE, LINE_HEIGHT } from './text-layout';
|
|
6
|
+
/**
|
|
7
|
+
* One text layer's bookkeeping: a group of members per def, and the paint order across them.
|
|
8
|
+
*
|
|
9
|
+
* The slot a def claims here is a **group**, not a batch member — a `TextDef` holds an array of
|
|
10
|
+
* lines and each line is its own `TextExt`. That is the one place this differs from the shape and
|
|
11
|
+
* image containers, and it falls out of the def type rather than being a choice: the lines of one
|
|
12
|
+
* def are laid out together (a centered block needs every line's height before it can place the
|
|
13
|
+
* first), so they cannot be independent instances.
|
|
14
|
+
*
|
|
15
|
+
* Group size is the slot's resource, so a def that gains or loses a line re-claims automatically —
|
|
16
|
+
* which is what makes `TextDef.lines` updatable here, where the imperative renderer required it to
|
|
17
|
+
* be fixed.
|
|
18
|
+
*
|
|
19
|
+
* Ordering differs too: a text batch draws through packed glyph attributes rather than a sortable
|
|
20
|
+
* draw list, so {@link applyOrder} hands the batch a rebuilt member list instead of labeling slots,
|
|
21
|
+
* and the inherited `customSort` goes unused.
|
|
22
|
+
*/
|
|
23
|
+
export class TextContainer extends SlotContainer {
|
|
24
|
+
/** Groups by slot, with holes for released ones. */
|
|
25
|
+
groups = [];
|
|
26
|
+
freeGroups = [];
|
|
27
|
+
/**
|
|
28
|
+
* Batch member to the group holding it, so a hit — which names a member — resolves to a def. The
|
|
29
|
+
* batch reuses a released member's slot, so entries are dropped with the group that owned them.
|
|
30
|
+
*/
|
|
31
|
+
groupByMember = new Map();
|
|
32
|
+
groupCount = 0;
|
|
33
|
+
/** The last order applied to the batch, kept so an unchanged one can skip the repack. */
|
|
34
|
+
appliedOrder = [];
|
|
35
|
+
syncScheduled = false;
|
|
36
|
+
invalidate;
|
|
37
|
+
/**
|
|
38
|
+
* Builds a text layer's bookkeeping. The batch itself is R3F's — the component renders
|
|
39
|
+
* `<BatchedText>` and hands it over through {@link SlotContainer.bind} — so R3F disposes it on
|
|
40
|
+
* unmount.
|
|
41
|
+
* @param invalidate - R3F's frame request, called when an asynchronous sync completes. Stable for
|
|
42
|
+
* the lifetime of a canvas, so capturing it here is safe.
|
|
43
|
+
*/
|
|
44
|
+
constructor(invalidate) {
|
|
45
|
+
super();
|
|
46
|
+
this.invalidate = invalidate;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The members backing one def.
|
|
50
|
+
* @param slot - The def's group.
|
|
51
|
+
* @returns Its members and their batch slots.
|
|
52
|
+
*/
|
|
53
|
+
linesAt(slot) {
|
|
54
|
+
return this.groups[slot] ?? EMPTY_GROUP;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Requests one batch sync, coalescing every request made in the same tick.
|
|
58
|
+
*
|
|
59
|
+
* Eager rather than waiting for `onBeforeRender`, because syncing is asynchronous — troika loads
|
|
60
|
+
* and parses the font — and under on-demand rendering a sync started during a frame has nothing to
|
|
61
|
+
* request the *next* frame when it completes. The callback carries R3F's `invalidate`, which is
|
|
62
|
+
* what closes that loop; without it new text appears only when something else happens to
|
|
63
|
+
* invalidate.
|
|
64
|
+
*/
|
|
65
|
+
scheduleSync() {
|
|
66
|
+
if (this.syncScheduled)
|
|
67
|
+
return;
|
|
68
|
+
this.syncScheduled = true;
|
|
69
|
+
// `Promise.resolve().then`, not `queueMicrotask`: the SDK targets Android WebViews as old as
|
|
70
|
+
// Chrome 66, which predates it. Same microtask timing.
|
|
71
|
+
void Promise.resolve().then(() => {
|
|
72
|
+
this.syncScheduled = false;
|
|
73
|
+
// Unmounted between the request and the microtask; the batch is R3F's and already disposed.
|
|
74
|
+
if (!this.mesh)
|
|
75
|
+
return;
|
|
76
|
+
this.mesh.sync(this.invalidate);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
claim(def, lineCount) {
|
|
80
|
+
const slot = super.claim(def, lineCount);
|
|
81
|
+
// `addText` appends to the batch's order, so a first mount draws in claim order and the layer's
|
|
82
|
+
// own pass relabels it in the same effect flush. A *re*-claim after that pass has to re-run it,
|
|
83
|
+
// or the new members would stay stuck at the end of the paint order.
|
|
84
|
+
if (this.layerDefs.length)
|
|
85
|
+
this.applyOrder();
|
|
86
|
+
return slot;
|
|
87
|
+
}
|
|
88
|
+
setVisibleAt(slot, visible) {
|
|
89
|
+
const group = this.groups[slot];
|
|
90
|
+
if (!group)
|
|
91
|
+
return;
|
|
92
|
+
for (const member of group.slots) {
|
|
93
|
+
this.mesh.setVisibleAt(member, visible);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Sets the draw order for the whole layer, then syncs.
|
|
98
|
+
*
|
|
99
|
+
* The sync rides along so one structural change costs exactly one sync — a mount writes content
|
|
100
|
+
* through its instances in the same effect flush, and every request in the tick coalesces.
|
|
101
|
+
* @param defs - The layer's texts, in paint order.
|
|
102
|
+
* @param layerDimmed - Whether the layer is dimmed, inherited by defs without a `dim` of their own.
|
|
103
|
+
*/
|
|
104
|
+
setPaintOrder(defs, layerDimmed) {
|
|
105
|
+
super.setPaintOrder(defs, layerDimmed);
|
|
106
|
+
this.scheduleSync();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A hit names the batch member it struck, and a def owns one member per line.
|
|
110
|
+
* @param hit - The intersection to trace.
|
|
111
|
+
* @returns The def, or `undefined` when no claimed group holds that member.
|
|
112
|
+
*/
|
|
113
|
+
defFromHit(hit) {
|
|
114
|
+
const member = hit.instanceId;
|
|
115
|
+
return member === undefined ? undefined : this.defAt(this.groupByMember.get(member));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Rebuilds the flat member order from the current defs and hands it to the batch, unless nothing
|
|
119
|
+
* moved.
|
|
120
|
+
*
|
|
121
|
+
* Skipping an unchanged order matters more here than for the other containers: a text batch has no
|
|
122
|
+
* `customSort`, so reordering means repacking the glyph attributes, and `useStagedChildren` hands out
|
|
123
|
+
* a fresh snapshot on *every* layer update — including ones that moved nothing.
|
|
124
|
+
*/
|
|
125
|
+
applyOrder() {
|
|
126
|
+
const order = [];
|
|
127
|
+
for (const def of dimOrdered(this.layerDefs, this.layerDimmed)) {
|
|
128
|
+
const slot = this.slotOf(def);
|
|
129
|
+
if (slot === undefined)
|
|
130
|
+
continue;
|
|
131
|
+
const group = this.groups[slot];
|
|
132
|
+
if (!group)
|
|
133
|
+
continue;
|
|
134
|
+
order.push(...group.texts);
|
|
135
|
+
this.dimmedBySlot[slot] = isDimmed(def.dim, this.layerDimmed);
|
|
136
|
+
}
|
|
137
|
+
if (sameSequence(order, this.appliedOrder))
|
|
138
|
+
return;
|
|
139
|
+
this.appliedOrder = order;
|
|
140
|
+
this.mesh.setOrder(order);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Per member, not per def: a def's lines are separate batch members, and the uniform row is
|
|
144
|
+
* addressed by member. One `dim` on the def therefore writes each of its lines, exactly as
|
|
145
|
+
* visibility does.
|
|
146
|
+
* @param slot - The def's group.
|
|
147
|
+
* @param amount - The encoded amount.
|
|
148
|
+
*/
|
|
149
|
+
writeDim(slot, amount) {
|
|
150
|
+
const group = this.groups[slot];
|
|
151
|
+
if (!group)
|
|
152
|
+
return;
|
|
153
|
+
for (const member of group.slots) {
|
|
154
|
+
super.writeDim(member, amount);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
addInstance(lineCount) {
|
|
158
|
+
const slot = this.freeGroups.pop() ?? this.groupCount++;
|
|
159
|
+
const texts = [];
|
|
160
|
+
const slots = [];
|
|
161
|
+
for (let line = 0; line < lineCount; line++) {
|
|
162
|
+
const text = makeMember();
|
|
163
|
+
texts.push(text);
|
|
164
|
+
slots.push(this.mesh.addText(text));
|
|
165
|
+
}
|
|
166
|
+
this.groups[slot] = { texts, slots };
|
|
167
|
+
for (const member of slots)
|
|
168
|
+
this.groupByMember.set(member, slot);
|
|
169
|
+
return slot;
|
|
170
|
+
}
|
|
171
|
+
removeInstance(slot) {
|
|
172
|
+
const group = this.groups[slot];
|
|
173
|
+
if (!group)
|
|
174
|
+
return;
|
|
175
|
+
for (const member of group.slots)
|
|
176
|
+
this.groupByMember.delete(member);
|
|
177
|
+
for (const text of group.texts) {
|
|
178
|
+
this.mesh.removeText(text);
|
|
179
|
+
// The member's own `GlyphsGeometry`, which only ever held its bounds — the batch draws from
|
|
180
|
+
// its own. Nothing was uploaded, so this is bookkeeping, not a GPU free.
|
|
181
|
+
text.dispose();
|
|
182
|
+
}
|
|
183
|
+
this.groups[slot] = undefined;
|
|
184
|
+
this.freeGroups.push(slot);
|
|
185
|
+
// Members left the batch's order with them; drop the stale copy so the next pass reapplies.
|
|
186
|
+
this.appliedOrder = [];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Builds a batch member with the properties that never change for its lifetime.
|
|
191
|
+
*
|
|
192
|
+
* All three are syncable, so writing them once here — rather than per update — is what keeps a
|
|
193
|
+
* layout change from re-running troika's async layout and repacking the batch.
|
|
194
|
+
* @returns The member.
|
|
195
|
+
*/
|
|
196
|
+
function makeMember() {
|
|
197
|
+
const text = new TextExt();
|
|
198
|
+
text.fontSize = LAYOUT_FONT_SIZE;
|
|
199
|
+
text.lineHeight = LINE_HEIGHT;
|
|
200
|
+
// Wrapping is the floorplan's job: it measures and breaks lines itself, then hands over the
|
|
201
|
+
// result with explicit newlines. Letting troika re-wrap on top would fight that.
|
|
202
|
+
text.whiteSpace = 'nowrap';
|
|
203
|
+
return text;
|
|
204
|
+
}
|
|
205
|
+
/** Returned for an unclaimed slot, so a caller reading one iterates nothing rather than throwing. */
|
|
206
|
+
const EMPTY_GROUP = { texts: [], slots: [] };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type Rect } from '@expofp/geometry';
|
|
2
|
+
import { type TextExt } from '../../core/text/Text';
|
|
3
|
+
import { type TextDef, type TextLineDef } from '../../types/defs';
|
|
4
|
+
/**
|
|
5
|
+
* The em size every member lays out at, fixed for its lifetime.
|
|
6
|
+
*
|
|
7
|
+
* `fontSize` is a syncable property: changing it re-runs troika's async layout and repacks the whole
|
|
8
|
+
* batch. A label's size changes on every zoom step, so paying that would make zooming a sequence of
|
|
9
|
+
* repacks. Laying out at a constant 1 em and carrying the real size in the member's `scale` costs a
|
|
10
|
+
* matrix write instead — the scale is not syncable, so nothing re-lays-out.
|
|
11
|
+
*
|
|
12
|
+
* One, not zero: troika reads `this.fontSize || 0.1`, so a member left at zero lays out at a tenth of
|
|
13
|
+
* an em and every scale below would have to carry a compensating factor of ten.
|
|
14
|
+
*/
|
|
15
|
+
export declare const LAYOUT_FONT_SIZE = 1;
|
|
16
|
+
/** Line box height as a multiple of the font size, matching the `lineHeight` members are given. */
|
|
17
|
+
export declare const LINE_HEIGHT = 1.1;
|
|
18
|
+
/**
|
|
19
|
+
* A line's placement **in its def's own unrotated frame**, as an offset from the bounds' center.
|
|
20
|
+
*
|
|
21
|
+
* Layout stops here on purpose: where the bounds sit in the plan, and how they are turned, is a
|
|
22
|
+
* separate step ({@link applyPlacement}). Keeping the two apart is what lets the stacking be
|
|
23
|
+
* asserted as plain numbers, with no trigonometry in the test.
|
|
24
|
+
*/
|
|
25
|
+
export interface LaidOutLine {
|
|
26
|
+
/** Which horizontal point of the line's box sits at its origin. */
|
|
27
|
+
anchorX: 'left' | 'center' | 'right';
|
|
28
|
+
/** Which vertical point of the line's box sits at its origin; only the outer edges are used. */
|
|
29
|
+
anchorY: 'top' | 'bottom';
|
|
30
|
+
/** How wrapped rows align against each other within this line. */
|
|
31
|
+
textAlign: 'left' | 'center' | 'right';
|
|
32
|
+
/** Offset from the bounds' center along the bounds' own x axis. */
|
|
33
|
+
offsetX: number;
|
|
34
|
+
/** Offset from the bounds' center along the bounds' own y axis. */
|
|
35
|
+
offsetY: number;
|
|
36
|
+
/** Horizontal scale: the line's font size, which is its em height in plan units. */
|
|
37
|
+
scaleX: number;
|
|
38
|
+
/** Vertical scale: negative, because plan space runs y-down and text lays out y-up. */
|
|
39
|
+
scaleY: number;
|
|
40
|
+
/** `[minX, minY, maxX, maxY]` in the line's own em space, clipping it to the def's bounds. */
|
|
41
|
+
clipRect: [number, number, number, number];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Stacks a text def's lines inside its bounds, aligned as the def asks.
|
|
45
|
+
*
|
|
46
|
+
* Works entirely in the bounds' unrotated frame, so a rotated def lays out identically to an
|
|
47
|
+
* unrotated one — the rotation is applied afterwards, to the whole stack at once. That is also what
|
|
48
|
+
* lets the clip rect be a plain offset from the center: the member carries the same rotation, so the
|
|
49
|
+
* rect turns with it.
|
|
50
|
+
*
|
|
51
|
+
* **The y flip lives in `scaleY`.** Plan space runs y-down; troika lays text out y-up. Rather than
|
|
52
|
+
* troika's `orientation` — a `lookAt` rotation, which would leave the clip rect to be flipped
|
|
53
|
+
* separately — the flip rides on the scale the lines need anyway for their font size, so the clip
|
|
54
|
+
* rect, which divides by that scale, inherits it for free.
|
|
55
|
+
*
|
|
56
|
+
* **Plan units throughout**, font sizes included. A plan that wants a label to hold an on-screen size
|
|
57
|
+
* converts for itself, against the pixel size the renderer publishes — the same way it sizes every
|
|
58
|
+
* other pixel-authored thing it draws.
|
|
59
|
+
* @param def - The text to lay out.
|
|
60
|
+
* @returns One placement per line, in the def's line order.
|
|
61
|
+
*/
|
|
62
|
+
export declare function layoutText(def: TextDef): LaidOutLine[];
|
|
63
|
+
/**
|
|
64
|
+
* Writes a laid-out line onto its member: the bounds' rotation and position applied to the local
|
|
65
|
+
* offset, the line's scale, and its clip rect.
|
|
66
|
+
*
|
|
67
|
+
* The post-transform half of {@link layoutText}. `updateMatrix` is what reports the move to the
|
|
68
|
+
* batch — members are not in the scene graph, so nothing else would.
|
|
69
|
+
* @param text - The member to place.
|
|
70
|
+
* @param line - The line's local placement.
|
|
71
|
+
* @param bounds - The def's bounds, supplying the rotation, center and elevation.
|
|
72
|
+
*/
|
|
73
|
+
export declare function applyPlacement(text: TextExt, line: LaidOutLine, bounds: Rect): void;
|
|
74
|
+
/**
|
|
75
|
+
* The height one line's box occupies.
|
|
76
|
+
*
|
|
77
|
+
* A single `TextLineDef` can hold embedded newlines — the floorplan joins an exhibitor's name rows
|
|
78
|
+
* into one def — so its box is as many line boxes tall as it has rows.
|
|
79
|
+
* @param line - The line to measure.
|
|
80
|
+
* @returns The height, in plan units.
|
|
81
|
+
*/
|
|
82
|
+
export declare function lineHeight(line: TextLineDef): number;
|
|
83
|
+
//# sourceMappingURL=text-layout.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Vector2 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* The em size every member lays out at, fixed for its lifetime.
|
|
4
|
+
*
|
|
5
|
+
* `fontSize` is a syncable property: changing it re-runs troika's async layout and repacks the whole
|
|
6
|
+
* batch. A label's size changes on every zoom step, so paying that would make zooming a sequence of
|
|
7
|
+
* repacks. Laying out at a constant 1 em and carrying the real size in the member's `scale` costs a
|
|
8
|
+
* matrix write instead — the scale is not syncable, so nothing re-lays-out.
|
|
9
|
+
*
|
|
10
|
+
* One, not zero: troika reads `this.fontSize || 0.1`, so a member left at zero lays out at a tenth of
|
|
11
|
+
* an em and every scale below would have to carry a compensating factor of ten.
|
|
12
|
+
*/
|
|
13
|
+
export const LAYOUT_FONT_SIZE = 1;
|
|
14
|
+
/** Line box height as a multiple of the font size, matching the `lineHeight` members are given. */
|
|
15
|
+
export const LINE_HEIGHT = 1.1;
|
|
16
|
+
/**
|
|
17
|
+
* Stacks a text def's lines inside its bounds, aligned as the def asks.
|
|
18
|
+
*
|
|
19
|
+
* Works entirely in the bounds' unrotated frame, so a rotated def lays out identically to an
|
|
20
|
+
* unrotated one — the rotation is applied afterwards, to the whole stack at once. That is also what
|
|
21
|
+
* lets the clip rect be a plain offset from the center: the member carries the same rotation, so the
|
|
22
|
+
* rect turns with it.
|
|
23
|
+
*
|
|
24
|
+
* **The y flip lives in `scaleY`.** Plan space runs y-down; troika lays text out y-up. Rather than
|
|
25
|
+
* troika's `orientation` — a `lookAt` rotation, which would leave the clip rect to be flipped
|
|
26
|
+
* separately — the flip rides on the scale the lines need anyway for their font size, so the clip
|
|
27
|
+
* rect, which divides by that scale, inherits it for free.
|
|
28
|
+
*
|
|
29
|
+
* **Plan units throughout**, font sizes included. A plan that wants a label to hold an on-screen size
|
|
30
|
+
* converts for itself, against the pixel size the renderer publishes — the same way it sizes every
|
|
31
|
+
* other pixel-authored thing it draws.
|
|
32
|
+
* @param def - The text to lay out.
|
|
33
|
+
* @returns One placement per line, in the def's line order.
|
|
34
|
+
*/
|
|
35
|
+
export function layoutText(def) {
|
|
36
|
+
const { bounds, lines, alignment, padding } = def;
|
|
37
|
+
const half = _half.set(bounds.size.x, bounds.size.y).multiplyScalar(0.5);
|
|
38
|
+
// Where the first line's anchor sits. A centered block starts half the leftover space in from the
|
|
39
|
+
// top edge, so the stack ends up centered as a whole; every other alignment starts at the padded
|
|
40
|
+
// edge itself.
|
|
41
|
+
const offset = _offset.copy(half);
|
|
42
|
+
if (alignment.vertical === 'center') {
|
|
43
|
+
let stackHeight = 0;
|
|
44
|
+
for (const line of lines)
|
|
45
|
+
stackHeight += lineHeight(line);
|
|
46
|
+
offset.y -= (bounds.size.y - stackHeight) / 2;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
offset.sub(_padding.fromArray(padding));
|
|
50
|
+
}
|
|
51
|
+
offset.multiply(alignmentFactors(alignment));
|
|
52
|
+
const placed = [];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
const scaleX = line.fontSize;
|
|
55
|
+
// Negative: the y flip. Also why the clip rect below comes out correctly ordered — dividing by a
|
|
56
|
+
// negative scale swaps its min and max back.
|
|
57
|
+
const scaleY = -scaleX;
|
|
58
|
+
placed.push({
|
|
59
|
+
anchorX: alignment.horizontal,
|
|
60
|
+
// Only the outer edges: a middle-anchored line would stack against its own center, so the
|
|
61
|
+
// block's vertical alignment is carried by the starting offset above instead.
|
|
62
|
+
anchorY: alignment.vertical === 'bottom' ? 'bottom' : 'top',
|
|
63
|
+
textAlign: alignment.text ?? alignment.horizontal,
|
|
64
|
+
offsetX: offset.x,
|
|
65
|
+
offsetY: offset.y,
|
|
66
|
+
scaleX,
|
|
67
|
+
scaleY,
|
|
68
|
+
// The bounds' corners relative to this line's origin, converted from plan units into the
|
|
69
|
+
// line's em space. A zero-size line would divide by zero, but those are hidden before they
|
|
70
|
+
// draw (see `TextInstance`), so the clip rect is never read.
|
|
71
|
+
clipRect: [
|
|
72
|
+
(-half.x - offset.x) / scaleX,
|
|
73
|
+
(half.y - offset.y) / scaleY,
|
|
74
|
+
(half.x - offset.x) / scaleX,
|
|
75
|
+
(-half.y - offset.y) / scaleY,
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
offset.y += lineHeight(line);
|
|
79
|
+
}
|
|
80
|
+
return placed;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Writes a laid-out line onto its member: the bounds' rotation and position applied to the local
|
|
84
|
+
* offset, the line's scale, and its clip rect.
|
|
85
|
+
*
|
|
86
|
+
* The post-transform half of {@link layoutText}. `updateMatrix` is what reports the move to the
|
|
87
|
+
* batch — members are not in the scene graph, so nothing else would.
|
|
88
|
+
* @param text - The member to place.
|
|
89
|
+
* @param line - The line's local placement.
|
|
90
|
+
* @param bounds - The def's bounds, supplying the rotation, center and elevation.
|
|
91
|
+
*/
|
|
92
|
+
export function applyPlacement(text, line, bounds) {
|
|
93
|
+
const { rotation } = bounds;
|
|
94
|
+
const position = _position
|
|
95
|
+
.set(line.offsetX, line.offsetY)
|
|
96
|
+
.rotateAround(ORIGIN, rotation)
|
|
97
|
+
.add(bounds.center);
|
|
98
|
+
text.position.set(position.x, position.y, bounds.elevation);
|
|
99
|
+
text.rotation.set(0, 0, rotation);
|
|
100
|
+
text.scale.set(line.scaleX, line.scaleY, 1);
|
|
101
|
+
text.updateMatrix();
|
|
102
|
+
// Held as one array for the member's life: `clipRect` is not a syncable property, so this is a
|
|
103
|
+
// plain per-frame read and mutating it in place avoids a per-update allocation.
|
|
104
|
+
const clipRect = (text.clipRect ??= [0, 0, 0, 0]);
|
|
105
|
+
clipRect[0] = line.clipRect[0];
|
|
106
|
+
clipRect[1] = line.clipRect[1];
|
|
107
|
+
clipRect[2] = line.clipRect[2];
|
|
108
|
+
clipRect[3] = line.clipRect[3];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The height one line's box occupies.
|
|
112
|
+
*
|
|
113
|
+
* A single `TextLineDef` can hold embedded newlines — the floorplan joins an exhibitor's name rows
|
|
114
|
+
* into one def — so its box is as many line boxes tall as it has rows.
|
|
115
|
+
* @param line - The line to measure.
|
|
116
|
+
* @returns The height, in plan units.
|
|
117
|
+
*/
|
|
118
|
+
export function lineHeight(line) {
|
|
119
|
+
return line.fontSize * LINE_HEIGHT * line.text.split('\n').length;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Which way each axis is offset from the bounds' center for a given alignment.
|
|
123
|
+
*
|
|
124
|
+
* Vertical `center` shares `top`'s factor deliberately: a centered block still stacks downward from
|
|
125
|
+
* a start position, and it is that start position the caller moves.
|
|
126
|
+
* @param alignment - The def's alignment.
|
|
127
|
+
* @returns The scratch factors, valid until the next call.
|
|
128
|
+
*/
|
|
129
|
+
function alignmentFactors(alignment) {
|
|
130
|
+
return _factors.set(HORIZONTAL_FACTORS[alignment.horizontal], VERTICAL_FACTORS[alignment.vertical]);
|
|
131
|
+
}
|
|
132
|
+
/** Which edge a horizontal alignment offsets toward: `-1` left, `0` centered, `1` right. */
|
|
133
|
+
const HORIZONTAL_FACTORS = {
|
|
134
|
+
left: -1,
|
|
135
|
+
center: 0,
|
|
136
|
+
right: 1,
|
|
137
|
+
};
|
|
138
|
+
/** Which edge a vertical alignment offsets toward; `center` shares `top`'s sign (see above). */
|
|
139
|
+
const VERTICAL_FACTORS = {
|
|
140
|
+
top: -1,
|
|
141
|
+
center: -1,
|
|
142
|
+
bottom: 1,
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Scratch reused across calls: layout runs once per def per update and never escapes a synchronous
|
|
146
|
+
* block, so it allocates nothing.
|
|
147
|
+
*/
|
|
148
|
+
const _half = new Vector2();
|
|
149
|
+
const _offset = new Vector2();
|
|
150
|
+
const _padding = new Vector2();
|
|
151
|
+
const _factors = new Vector2();
|
|
152
|
+
const _position = new Vector2();
|
|
153
|
+
/** The point a rotated block turns about — its own bounds center, which the offsets are relative to. */
|
|
154
|
+
const ORIGIN = new Vector2(0, 0);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type Box } from '@expofp/geometry';
|
|
2
|
+
import { type PropsWithChildren } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* How far the camera may go: the limits every move is held to, dragged or programmatic.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from what a *user* may do, which is `ControlsConfig` — a caller switches a gesture off
|
|
7
|
+
* without changing where the camera may go, and constrains the camera without taking a gesture
|
|
8
|
+
* away. `RendererProps` carries both, and these are the half that ends up here.
|
|
9
|
+
*
|
|
10
|
+
* Every one is optional and defaulted where it is used, a few lines below, so nothing else in the
|
|
11
|
+
* renderer has to know or import what an omitted limit means.
|
|
12
|
+
*/
|
|
13
|
+
export interface CameraProps {
|
|
14
|
+
/**
|
|
15
|
+
* How far the plan may be zoomed out, as a factor of the framing that fits the whole viewbox;
|
|
16
|
+
* defaults to `0.1`. Which viewbox zoom factor `1` frames is the {@link Space}'s business, not
|
|
17
|
+
* this component's — all it needs is the bounds.
|
|
18
|
+
*/
|
|
19
|
+
minZoom?: number;
|
|
20
|
+
/**
|
|
21
|
+
* How far the plan may be zoomed in, as a factor of the framing that fits the whole viewbox;
|
|
22
|
+
* defaults to `35`.
|
|
23
|
+
*/
|
|
24
|
+
maxZoom?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The shallowest the plan may be pitched away from face-on, in degrees — `0` looks straight down
|
|
27
|
+
* at it; defaults to `0`.
|
|
28
|
+
*/
|
|
29
|
+
minPitch?: number;
|
|
30
|
+
/**
|
|
31
|
+
* The steepest the plan may be pitched away from face-on, in degrees; defaults to
|
|
32
|
+
* {@link MAX_PITCH}, which is also the most that may be asked for — the depth guarantee is sized
|
|
33
|
+
* against that angle, so a steeper view is one it never allowed for.
|
|
34
|
+
*/
|
|
35
|
+
maxPitch?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Where the camera may look: the region its target is held inside, in plan units. Omitted — the
|
|
38
|
+
* default — lets it look anywhere.
|
|
39
|
+
*
|
|
40
|
+
* A limit like the four above rather than a command, which is what makes it a prop: a host whose
|
|
41
|
+
* bounds follow the floor on show changes them by rendering, with no reaction to write.
|
|
42
|
+
*/
|
|
43
|
+
bounds?: Box;
|
|
44
|
+
/**
|
|
45
|
+
* How long a programmatic move takes to settle; see {@link SmoothTime}. Any duration left out
|
|
46
|
+
* takes {@link DEFAULT_SMOOTH_TIME}, so this prop always says the whole truth rather than adding
|
|
47
|
+
* to what was set before.
|
|
48
|
+
*
|
|
49
|
+
* Only programmatic moves: how a *dragged* one feels is the gesture's business and is set where
|
|
50
|
+
* the controls are mounted.
|
|
51
|
+
*/
|
|
52
|
+
smoothTime?: SmoothTime;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* How long each kind of programmatic camera move takes to settle, in seconds.
|
|
56
|
+
*
|
|
57
|
+
* Named for what a caller asked the plan to do rather than for how `camera-controls` does it, which
|
|
58
|
+
* is why this is the renderer's own type and not the library's: a host turning the plan asked for a
|
|
59
|
+
* roll*, and has no reason to know that it is driven by an azimuth. The same four durations the
|
|
60
|
+
* imperative renderer took, under the same four names.
|
|
61
|
+
*/
|
|
62
|
+
export interface SmoothTime {
|
|
63
|
+
/** How long {@link CameraAPI.zoomTo} and {@link CameraAPI.zoomBy} take. */
|
|
64
|
+
zoomTime?: number;
|
|
65
|
+
/** How long {@link CameraAPI.panTo} and {@link CameraAPI.panBy} take. */
|
|
66
|
+
panTime?: number;
|
|
67
|
+
/** How long {@link CameraAPI.rollTo} and {@link CameraAPI.rollBy} take. */
|
|
68
|
+
rollTime?: number;
|
|
69
|
+
/** How long {@link CameraAPI.pitchTo} and {@link CameraAPI.pitchBy} take. */
|
|
70
|
+
pitchTime?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The camera the plan is drawn through, and the zoom that camera is read as.
|
|
74
|
+
*
|
|
75
|
+
* Everything the imperative renderer's camera system did, with one simplification the viewbox fit
|
|
76
|
+
* pays for: because {@link Space} sizes a world unit to a device pixel, the distance that frames the
|
|
77
|
+
* plan follows from the drawing buffer alone and none of the arithmetic here has to know what the
|
|
78
|
+
* scene holds. The near plane is the one exception, its guarantee being stated in plan units.
|
|
79
|
+
*
|
|
80
|
+
* **Zoom is the state and distance is derived.** The camera stores a distance, which the buffer
|
|
81
|
+
* height converts to a factor — so a resize would silently change the zoom if nothing held it. This
|
|
82
|
+
* remembers the factor and dollies back to it, which is what the imperative renderer kept a previous
|
|
83
|
+
* viewport height for; a user dolly writes the factor back on the controls' own update.
|
|
84
|
+
*
|
|
85
|
+
* **The one thing it needs from the scene is `space.scale`,** and only for the near plane. The
|
|
86
|
+
* imperative renderer kept its camera and scene independent because it applied its depth guarantee
|
|
87
|
+
* to world distances; that guarantee is stated in plan units, so converting it is what makes it hold
|
|
88
|
+
* on a plan drawn at any scale (see {@link nearPlane}). Nothing else here reads the scene.
|
|
89
|
+
*
|
|
90
|
+
* **All four limits are written here**, zoom and pitch alike, because they are the same kind of
|
|
91
|
+
* thing: a constraint on where the camera may go, holding for a programmatic move as much as for a
|
|
92
|
+
* dragged one. What the *user* may do is `Controls`'s business and nothing to do with these. Keeping
|
|
93
|
+
* them together also puts each limit beside the clipping plane derived from it — `maxPolarAngle`
|
|
94
|
+
* sizes the near plane, the maximum distance the far one.
|
|
95
|
+
*
|
|
96
|
+
* Configures R3F's camera rather than replacing it, which leaves `aspect` to R3F — it writes that on
|
|
97
|
+
* every resize, from the same size this reads. Mutations go through the store getter because the
|
|
98
|
+
* React Compiler refuses to lower a write to anything a hook returned.
|
|
99
|
+
* @param props - How far the plan may be zoomed and pitched, and what should reach the handle.
|
|
100
|
+
* @returns The children, under the camera handle.
|
|
101
|
+
*/
|
|
102
|
+
export declare function Camera(props: PropsWithChildren<CameraProps>): import("react").JSX.Element;
|
|
103
|
+
//# sourceMappingURL=Camera.d.ts.map
|