@expofp/renderer 0.0.0-experimental.cf063fc
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,53 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type PanConfig } from './handlers/Pan';
|
|
3
|
+
import { type RollConfig } from './handlers/Roll';
|
|
4
|
+
import { type ZoomConfig } from './handlers/Zoom';
|
|
5
|
+
/**
|
|
6
|
+
* What the user may do with the plan.
|
|
7
|
+
*
|
|
8
|
+
* Each handler takes `false` to switch it off, `true` or nothing for its defaults, and — where it
|
|
9
|
+
* has any — an options object, whose own defaults the handler applies.
|
|
10
|
+
*
|
|
11
|
+
* **Pan and zoom default on; roll and pitch default to whether this is a development build**, so
|
|
12
|
+
* the renderer's own Storybook and dev server carry them and a production plan does not until a
|
|
13
|
+
* caller asks. An explicit value always wins, so a host driving roll from a user-facing setting
|
|
14
|
+
* never has to think about the dev default.
|
|
15
|
+
*
|
|
16
|
+
* The pitch and zoom **limits** are not here: they hold whether or not the matching gesture does,
|
|
17
|
+
* which makes them the camera's — see `CameraProps`.
|
|
18
|
+
*/
|
|
19
|
+
export interface ControlsConfig {
|
|
20
|
+
/** Left-drag and one-finger drag to pan the plan. Defaults on. */
|
|
21
|
+
pan?: boolean | PanConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Wheel and two-finger pinch to zoom, toward the cursor. Defaults on; a double-click or
|
|
24
|
+
* double-tap zooms too only when its options ask for it.
|
|
25
|
+
*/
|
|
26
|
+
zoom?: boolean | ZoomConfig;
|
|
27
|
+
/** Right-drag and two-finger twist to turn the plan. Defaults to a development build. */
|
|
28
|
+
roll?: boolean | RollConfig;
|
|
29
|
+
/** Right-drag and two-finger vertical drag to tilt the plan. Defaults to a development build. */
|
|
30
|
+
pitch?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Props for {@link Controls}. */
|
|
33
|
+
export interface ControlsProps {
|
|
34
|
+
/** What the user may do with the plan; see {@link ControlsConfig}. */
|
|
35
|
+
config?: ControlsConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The scene's camera controls, and the gesture handlers that drive them.
|
|
39
|
+
*
|
|
40
|
+
* Registered as R3F's default controls (`makeDefault`) so everything else reaches them through
|
|
41
|
+
* `state.controls`. Every mouse and touch action starts switched off, and each handler mounted
|
|
42
|
+
* below claims the ones it needs — so what a user can do is exactly the set of handlers standing
|
|
43
|
+
* here, and turning one off is unmounting it.
|
|
44
|
+
*
|
|
45
|
+
* **How far the camera may go is not decided here.** The zoom and pitch bounds constrain a
|
|
46
|
+
* programmatic move as much as a dragged one, so they belong to the camera; `Camera` writes them,
|
|
47
|
+
* beside the clipping planes derived from the same numbers. This component is only about what the
|
|
48
|
+
* user may do.
|
|
49
|
+
* @param props - What the user may do with the plan.
|
|
50
|
+
* @returns The controls and their handlers.
|
|
51
|
+
*/
|
|
52
|
+
export declare function Controls(props: ControlsProps): ReactNode;
|
|
53
|
+
//# sourceMappingURL=Controls.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { CameraControls } from './CameraControls';
|
|
4
|
+
import { Pan } from './handlers/Pan';
|
|
5
|
+
import { Pitch } from './handlers/Pitch';
|
|
6
|
+
import { Roll } from './handlers/Roll';
|
|
7
|
+
import { Zoom } from './handlers/Zoom';
|
|
8
|
+
/**
|
|
9
|
+
* The scene's camera controls, and the gesture handlers that drive them.
|
|
10
|
+
*
|
|
11
|
+
* Registered as R3F's default controls (`makeDefault`) so everything else reaches them through
|
|
12
|
+
* `state.controls`. Every mouse and touch action starts switched off, and each handler mounted
|
|
13
|
+
* below claims the ones it needs — so what a user can do is exactly the set of handlers standing
|
|
14
|
+
* here, and turning one off is unmounting it.
|
|
15
|
+
*
|
|
16
|
+
* **How far the camera may go is not decided here.** The zoom and pitch bounds constrain a
|
|
17
|
+
* programmatic move as much as a dragged one, so they belong to the camera; `Camera` writes them,
|
|
18
|
+
* beside the clipping planes derived from the same numbers. This component is only about what the
|
|
19
|
+
* user may do.
|
|
20
|
+
* @param props - What the user may do with the plan.
|
|
21
|
+
* @returns The controls and their handlers.
|
|
22
|
+
*/
|
|
23
|
+
export function Controls(props) {
|
|
24
|
+
const controlsRef = useRef(null);
|
|
25
|
+
const config = props.config ?? {};
|
|
26
|
+
const pan = config.pan ?? true;
|
|
27
|
+
const zoom = config.zoom ?? true;
|
|
28
|
+
const roll = config.roll ?? DEV;
|
|
29
|
+
const pitch = config.pitch ?? DEV;
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// Straight down by default. Set through `rotateTo` rather than by assigning `polarAngle`, which
|
|
32
|
+
// skips `Spherical.makeSafe` — and a polar angle of exactly zero causes trouble with roll on
|
|
33
|
+
// mobile. Nothing sets a target: the scene's fit puts the plan's centre on the world origin,
|
|
34
|
+
// which is where the controls already orbit.
|
|
35
|
+
void controlsRef.current.rotateTo(0, 0);
|
|
36
|
+
}, []);
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsx(CameraControls, { ref: controlsRef, makeDefault: true, events: true, dollyToCursor: true, controlSmoothTime: { rotate: 0, truck: 0 } }), pan !== false && _jsx(Pan, { ...optionsOf(pan) }), zoom !== false && _jsx(Zoom, { ...optionsOf(zoom) }), roll !== false && _jsx(Roll, { ...optionsOf(roll) }), pitch !== false && _jsx(Pitch, {})] }));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The options a handler was configured with, if it was given any rather than a bare flag.
|
|
41
|
+
* @param config - What the caller wrote for it.
|
|
42
|
+
* @returns The options, or an empty set for a handler switched on without any.
|
|
43
|
+
*/
|
|
44
|
+
function optionsOf(config) {
|
|
45
|
+
return typeof config === 'object' ? config : {};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether this is a development build, which is what roll and pitch default to.
|
|
49
|
+
*
|
|
50
|
+
* Typed inline rather than through `vite/client`, matching `stories/support/screenshot.ts`: the
|
|
51
|
+
* library build compiles without those types, and the optional chain keeps a consumer whose bundler
|
|
52
|
+
* defines no `import.meta.env` from throwing on it.
|
|
53
|
+
*/
|
|
54
|
+
const DEV = import.meta.env?.DEV === true;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { type Box } from '@expofp/geometry';
|
|
2
|
+
import { type CameraHandle } from '../space/camera-handle';
|
|
3
|
+
import { type PlanPointLike, type Space } from '../space/Space';
|
|
4
|
+
import { type CameraControlsImpl } from './CameraControls';
|
|
5
|
+
import { type ViewOffset } from './zoom-to';
|
|
6
|
+
/** How a camera move should be made. */
|
|
7
|
+
export interface TransitionOptions {
|
|
8
|
+
/** Jump rather than ease; defaults to easing. How long an eased move takes is `smoothTime`. */
|
|
9
|
+
immediate?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** How {@link CameraAPI.zoomTo} should frame its target. */
|
|
12
|
+
export interface ZoomToOptions extends TransitionOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Fraction of the region left as margin on each side; defaults to none. `0.25` spends half the
|
|
15
|
+
* region on margin, which is what the imperative renderer asked for around a single booth.
|
|
16
|
+
*/
|
|
17
|
+
padding?: number;
|
|
18
|
+
/** The closest the fit may bring the plan, as a zoom factor. The camera's `maxZoom` still caps it. */
|
|
19
|
+
maxZoom?: number;
|
|
20
|
+
/**
|
|
21
|
+
* The part of the canvas to frame the target inside, in css pixels from its top-left corner;
|
|
22
|
+
* defaults to the whole canvas.
|
|
23
|
+
*
|
|
24
|
+
* **Framing only.** Nothing is rendered differently for it and the plan does not move when one
|
|
25
|
+
* appears — which is what makes it the right place for a panel that covers part of the canvas,
|
|
26
|
+
* as opposed to `visibleRect`, which is where the plan is *drawn*.
|
|
27
|
+
*/
|
|
28
|
+
within?: Box;
|
|
29
|
+
}
|
|
30
|
+
/** Which axes {@link CameraAPI.reset} returns to their starting values. */
|
|
31
|
+
export interface CameraAxes {
|
|
32
|
+
/** Look at the middle of the plan again. */
|
|
33
|
+
pan?: boolean;
|
|
34
|
+
/** Frame the whole viewbox again — zoom factor `1`. */
|
|
35
|
+
zoom?: boolean;
|
|
36
|
+
/** Face north again. */
|
|
37
|
+
roll?: boolean;
|
|
38
|
+
/** Look straight down again, as far as `minPitch` allows. */
|
|
39
|
+
pitch?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Moving the camera: what a host asks the plan to do, as opposed to what a *user* may do with it,
|
|
43
|
+
* which is `ControlsConfig`.
|
|
44
|
+
*
|
|
45
|
+
* Every move is held to the camera's own limits — `minZoom`, `maxZoom`, `minPitch`, `maxPitch`,
|
|
46
|
+
* `bounds` — and to nothing else. In particular it does **not** matter which gestures are mounted:
|
|
47
|
+
* a plan with roll switched off still rolls when its host asks it to, because a limit on where the
|
|
48
|
+
* camera may go and a decision about what a finger may do are different questions. That is the one
|
|
49
|
+
* place this departs from the imperative renderer, whose `resetCamera` skipped any axis whose
|
|
50
|
+
* handler happened to be disabled.
|
|
51
|
+
*
|
|
52
|
+
* Every method resolves when the move has settled, so a caller can sequence them; an `immediate`
|
|
53
|
+
* move resolves at once.
|
|
54
|
+
*/
|
|
55
|
+
export interface CameraAPI {
|
|
56
|
+
/**
|
|
57
|
+
* Frames a box of the plan.
|
|
58
|
+
* @param target - The box, in plan units.
|
|
59
|
+
* @param options - How to frame it.
|
|
60
|
+
* @returns When the move has settled.
|
|
61
|
+
*/
|
|
62
|
+
zoomTo(target: Box, options?: ZoomToOptions): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Zooms by a factor of where it already is — `1.5` closer, `0.66` further out.
|
|
65
|
+
* @param factor - What to multiply the zoom factor by.
|
|
66
|
+
* @param options - How to make the move.
|
|
67
|
+
* @returns When the move has settled.
|
|
68
|
+
*/
|
|
69
|
+
zoomBy(factor: number, options?: TransitionOptions): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Looks at a point on the plan.
|
|
72
|
+
* @param point - Where to look, in plan units. A `z` is ignored: the camera looks at the floor
|
|
73
|
+
* beneath an elevated point, not at the point itself.
|
|
74
|
+
* @param options - How to make the move.
|
|
75
|
+
* @returns When the move has settled.
|
|
76
|
+
*/
|
|
77
|
+
panTo(point: PlanPointLike, options?: TransitionOptions): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Slides the view across the plan.
|
|
80
|
+
* @param offset - How far to slide, in plan units.
|
|
81
|
+
* @param options - How to make the move.
|
|
82
|
+
* @returns When the move has settled.
|
|
83
|
+
*/
|
|
84
|
+
panBy(offset: PlanPointLike, options?: TransitionOptions): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Turns the plan to face a heading, by whichever way round is shorter.
|
|
87
|
+
* @param degrees - The heading, clockwise from north.
|
|
88
|
+
* @param options - How to make the move.
|
|
89
|
+
* @returns When the move has settled.
|
|
90
|
+
*/
|
|
91
|
+
rollTo(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Turns the plan further.
|
|
94
|
+
* @param degrees - How far to turn it, clockwise.
|
|
95
|
+
* @param options - How to make the move.
|
|
96
|
+
* @returns When the move has settled.
|
|
97
|
+
*/
|
|
98
|
+
rollBy(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Tilts the plan to an angle away from face-on, held inside the pitch limits.
|
|
101
|
+
* @param degrees - The angle; `0` looks straight down.
|
|
102
|
+
* @param options - How to make the move.
|
|
103
|
+
* @returns When the move has settled.
|
|
104
|
+
*/
|
|
105
|
+
pitchTo(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Tilts the plan further, held inside the pitch limits.
|
|
108
|
+
* @param degrees - How much further to tilt it.
|
|
109
|
+
* @param options - How to make the move.
|
|
110
|
+
* @returns When the move has settled.
|
|
111
|
+
*/
|
|
112
|
+
pitchBy(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Returns the named axes to where they started; all four when none are named.
|
|
115
|
+
* @param axes - Which axes to reset.
|
|
116
|
+
* @param options - How to make the move.
|
|
117
|
+
* @returns When every reset has settled.
|
|
118
|
+
*/
|
|
119
|
+
reset(axes?: CameraAxes, options?: TransitionOptions): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* What {@link CameraCommands} needs from the scene it drives.
|
|
123
|
+
*
|
|
124
|
+
* Handed over whole rather than reached for, so the class stays free of React and can be exercised
|
|
125
|
+
* against a bare camera. Nothing here is the camera three draws through: the one thing ever wanted
|
|
126
|
+
* from it is its view offset, which arrives as {@link CameraContext.view} — naming both a `camera`
|
|
127
|
+
* and a way to reach three's camera would be two different objects under one word.
|
|
128
|
+
*/
|
|
129
|
+
interface CameraContext {
|
|
130
|
+
/** The controls every command drives, once they have mounted. */
|
|
131
|
+
controls: CameraControlsImpl | undefined;
|
|
132
|
+
/** The camera's zoom handle, once it is published. */
|
|
133
|
+
camera: CameraHandle | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* What a visible rect put on the projection, read at the moment of asking.
|
|
136
|
+
*
|
|
137
|
+
* A function and not a value, because it is written by an effect after this context was built —
|
|
138
|
+
* a scene with a visible rect spends its first render without one — and three mutates the object
|
|
139
|
+
* in place rather than replacing it, so nothing short of a fresh read is reliably current.
|
|
140
|
+
*/
|
|
141
|
+
view: () => ViewOffset;
|
|
142
|
+
/** The scene's space, which takes plan coordinates into world ones. */
|
|
143
|
+
space: Space;
|
|
144
|
+
/** The canvas width in css pixels. */
|
|
145
|
+
width: number;
|
|
146
|
+
/** Its height. */
|
|
147
|
+
height: number;
|
|
148
|
+
/** Device pixels per css pixel. */
|
|
149
|
+
dpr: number;
|
|
150
|
+
/**
|
|
151
|
+
* Asks React Three Fiber to draw another frame.
|
|
152
|
+
*
|
|
153
|
+
* A command changes the controls directly. That change happens outside a React commit. React
|
|
154
|
+
* Three Fiber cannot see it on its own. Calling this schedules the frame that shows the change,
|
|
155
|
+
* under a demand frame loop that would otherwise never draw one.
|
|
156
|
+
*/
|
|
157
|
+
invalidate: () => void;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The camera commands, over whatever the scene currently holds.
|
|
161
|
+
*
|
|
162
|
+
* A class rather than an object literal because it is the renderer's published surface: the methods
|
|
163
|
+
* document themselves in one place, and a caller reading `CameraAPI` in an editor lands here. It is
|
|
164
|
+
* built fresh whenever its context moves rather than mutated in place, so nothing here is state and
|
|
165
|
+
* every method reads the scene as it stood when the component last rendered — bar the camera's own
|
|
166
|
+
* position and heading, which are read from the controls at the moment of asking.
|
|
167
|
+
*/
|
|
168
|
+
export declare class CameraCommands implements CameraAPI {
|
|
169
|
+
private readonly context;
|
|
170
|
+
/**
|
|
171
|
+
* @param context - What the scene holds; the instance is rebuilt whenever it moves.
|
|
172
|
+
*/
|
|
173
|
+
constructor(context: CameraContext);
|
|
174
|
+
/** @inheritdoc */
|
|
175
|
+
zoomTo(target: Box, options?: ZoomToOptions): Promise<void>;
|
|
176
|
+
/** @inheritdoc */
|
|
177
|
+
zoomBy(factor: number, options?: TransitionOptions): Promise<void>;
|
|
178
|
+
/** @inheritdoc */
|
|
179
|
+
panTo(point: PlanPointLike, options?: TransitionOptions): Promise<void>;
|
|
180
|
+
/** @inheritdoc */
|
|
181
|
+
panBy(offset: PlanPointLike, options?: TransitionOptions): Promise<void>;
|
|
182
|
+
/** @inheritdoc */
|
|
183
|
+
rollTo(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
184
|
+
/** @inheritdoc */
|
|
185
|
+
rollBy(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
186
|
+
/** @inheritdoc */
|
|
187
|
+
pitchTo(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
188
|
+
/** @inheritdoc */
|
|
189
|
+
pitchBy(degrees: number, options?: TransitionOptions): Promise<void>;
|
|
190
|
+
/** @inheritdoc */
|
|
191
|
+
reset(axes?: CameraAxes, options?: TransitionOptions): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Runs a move against the controls and asks for the frame that shows it.
|
|
194
|
+
*
|
|
195
|
+
* Every method above that takes {@link TransitionOptions} funnels its move through this one
|
|
196
|
+
* helper. A method that calls `controls` directly instead skips it, and looks obviously unlike
|
|
197
|
+
* its siblings.
|
|
198
|
+
*
|
|
199
|
+
* The invalidate happens before the move settles, not after, and every time, not only when the
|
|
200
|
+
* move is immediate. An eased move already triggers its own invalidate through `transitionstart`.
|
|
201
|
+
* An immediate move fires no event at all. `camera-controls` only applies an immediate move inside
|
|
202
|
+
* its own `update`, which runs from a frame that only an invalidate schedules. Invalidating after
|
|
203
|
+
* the move's promise settles would be too late to schedule that frame.
|
|
204
|
+
*
|
|
205
|
+
* A field and not a method, so it never lands on `CameraCommands.prototype`. The guard spec walks
|
|
206
|
+
* that prototype and calls everything it finds there, and an implementation detail would fail that
|
|
207
|
+
* walk for no reason.
|
|
208
|
+
* @param move - Issues the move against the controls and returns its promise.
|
|
209
|
+
* @returns The same promise `move` returned.
|
|
210
|
+
*/
|
|
211
|
+
private withInvalidate;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* How far the plan is turned from north: clockwise, in radians, in `[0, 2π)`.
|
|
215
|
+
*
|
|
216
|
+
* The camera holds an azimuth, which runs the other way and over any range the user has spun it
|
|
217
|
+
* through. Every reader of "which way is the plan facing" wants this instead, so the negation and
|
|
218
|
+
* the wrap live here once rather than at each of them — the same getter the imperative renderer put
|
|
219
|
+
* on its controller. Beside the rest of "where the camera is", which is the only question it
|
|
220
|
+
* answers.
|
|
221
|
+
* @param controls - The scene's controls.
|
|
222
|
+
* @returns The bearing in radians.
|
|
223
|
+
*/
|
|
224
|
+
export declare function bearing(controls: CameraControlsImpl): number;
|
|
225
|
+
export {};
|
|
226
|
+
//# sourceMappingURL=camera-api.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { angleDiff, degToRad } from '@expofp/geometry';
|
|
2
|
+
import { Vector3 } from 'three';
|
|
3
|
+
import { euclideanModulo } from '../utils/math';
|
|
4
|
+
import { fitBox, framingRegion } from './zoom-to';
|
|
5
|
+
/**
|
|
6
|
+
* The camera commands, over whatever the scene currently holds.
|
|
7
|
+
*
|
|
8
|
+
* A class rather than an object literal because it is the renderer's published surface: the methods
|
|
9
|
+
* document themselves in one place, and a caller reading `CameraAPI` in an editor lands here. It is
|
|
10
|
+
* built fresh whenever its context moves rather than mutated in place, so nothing here is state and
|
|
11
|
+
* every method reads the scene as it stood when the component last rendered — bar the camera's own
|
|
12
|
+
* position and heading, which are read from the controls at the moment of asking.
|
|
13
|
+
*/
|
|
14
|
+
export class CameraCommands {
|
|
15
|
+
context;
|
|
16
|
+
/**
|
|
17
|
+
* @param context - What the scene holds; the instance is rebuilt whenever it moves.
|
|
18
|
+
*/
|
|
19
|
+
constructor(context) {
|
|
20
|
+
this.context = context;
|
|
21
|
+
}
|
|
22
|
+
/** @inheritdoc */
|
|
23
|
+
async zoomTo(target, options) {
|
|
24
|
+
const { controls, camera, space, view, width, height, dpr } = this.context;
|
|
25
|
+
if (!controls || !camera)
|
|
26
|
+
return;
|
|
27
|
+
const fit = fitBox({
|
|
28
|
+
target,
|
|
29
|
+
space,
|
|
30
|
+
bearing: bearing(controls),
|
|
31
|
+
region: framingRegion(options?.within, width, height, dpr, view()),
|
|
32
|
+
padding: options?.padding,
|
|
33
|
+
maxZoom: options?.maxZoom,
|
|
34
|
+
});
|
|
35
|
+
if (!fit)
|
|
36
|
+
return;
|
|
37
|
+
const transition = !options?.immediate;
|
|
38
|
+
await this.withInvalidate(() => Promise.all([
|
|
39
|
+
controls.moveTo(fit.center.x, fit.center.y, 0, transition),
|
|
40
|
+
controls.dollyTo(camera.zoomFactorToDistance(fit.zoom), transition),
|
|
41
|
+
]));
|
|
42
|
+
}
|
|
43
|
+
/** @inheritdoc */
|
|
44
|
+
zoomBy(factor, options) {
|
|
45
|
+
const { controls, camera } = this.context;
|
|
46
|
+
if (!controls || !camera)
|
|
47
|
+
return RESOLVED;
|
|
48
|
+
return this.withInvalidate(() => controls.dollyTo(camera.zoomFactorToDistance(camera.zoomFactor * factor), !options?.immediate));
|
|
49
|
+
}
|
|
50
|
+
/** @inheritdoc */
|
|
51
|
+
panTo(point, options) {
|
|
52
|
+
const { controls, space } = this.context;
|
|
53
|
+
if (!controls)
|
|
54
|
+
return RESOLVED;
|
|
55
|
+
const world = toWorld(space, point);
|
|
56
|
+
return this.withInvalidate(() => controls.moveTo(world.x, world.y, world.z, !options?.immediate));
|
|
57
|
+
}
|
|
58
|
+
/** @inheritdoc */
|
|
59
|
+
panBy(offset, options) {
|
|
60
|
+
const { controls, space } = this.context;
|
|
61
|
+
if (!controls)
|
|
62
|
+
return RESOLVED;
|
|
63
|
+
// Two conversions rather than one, because a plan offset is a direction and the fit carries a
|
|
64
|
+
// translation: only the difference between two converted points is free of it.
|
|
65
|
+
const origin = toWorld(space, ORIGIN, _origin);
|
|
66
|
+
const shifted = toWorld(space, offset);
|
|
67
|
+
const current = controls.getTarget(_current);
|
|
68
|
+
return this.withInvalidate(() => controls.moveTo(current.x + shifted.x - origin.x, current.y + shifted.y - origin.y, current.z + shifted.z - origin.z, !options?.immediate));
|
|
69
|
+
}
|
|
70
|
+
/** @inheritdoc */
|
|
71
|
+
rollTo(degrees, options) {
|
|
72
|
+
const { controls } = this.context;
|
|
73
|
+
if (!controls)
|
|
74
|
+
return RESOLVED;
|
|
75
|
+
// Whichever way round is shorter, which is what keeps a turn from 350° to 10° going the long
|
|
76
|
+
// way about.
|
|
77
|
+
const delta = angleDiff(-degToRad(degrees), controls.azimuthAngle);
|
|
78
|
+
return this.withInvalidate(() => controls.rotateAzimuthTo(controls.azimuthAngle + delta, !options?.immediate));
|
|
79
|
+
}
|
|
80
|
+
/** @inheritdoc */
|
|
81
|
+
rollBy(degrees, options) {
|
|
82
|
+
const { controls } = this.context;
|
|
83
|
+
if (!controls)
|
|
84
|
+
return RESOLVED;
|
|
85
|
+
return this.withInvalidate(() => controls.rotateAzimuthTo(controls.azimuthAngle - degToRad(degrees), !options?.immediate));
|
|
86
|
+
}
|
|
87
|
+
/** @inheritdoc */
|
|
88
|
+
pitchTo(degrees, options) {
|
|
89
|
+
const { controls } = this.context;
|
|
90
|
+
if (!controls)
|
|
91
|
+
return RESOLVED;
|
|
92
|
+
// Held inside the pitch limits by the controls themselves, which is where those limits live.
|
|
93
|
+
return this.withInvalidate(() => controls.rotatePolarTo(degToRad(degrees), !options?.immediate));
|
|
94
|
+
}
|
|
95
|
+
/** @inheritdoc */
|
|
96
|
+
pitchBy(degrees, options) {
|
|
97
|
+
const { controls } = this.context;
|
|
98
|
+
if (!controls)
|
|
99
|
+
return RESOLVED;
|
|
100
|
+
return this.withInvalidate(() => controls.rotatePolarTo(controls.polarAngle + degToRad(degrees), !options?.immediate));
|
|
101
|
+
}
|
|
102
|
+
/** @inheritdoc */
|
|
103
|
+
async reset(axes = ALL_AXES, options) {
|
|
104
|
+
const { controls, camera } = this.context;
|
|
105
|
+
if (!controls || !camera)
|
|
106
|
+
return;
|
|
107
|
+
const transition = !options?.immediate;
|
|
108
|
+
const moves = [];
|
|
109
|
+
// Every target is the camera's own, which is why there is no registry of what to ask: the
|
|
110
|
+
// viewbox fit puts the plan's middle on the world origin, zoom factor 1 frames it, north is
|
|
111
|
+
// azimuth zero, and how flat the plan may lie is `minPitch`.
|
|
112
|
+
if (axes.pan)
|
|
113
|
+
moves.push(controls.moveTo(0, 0, 0, transition));
|
|
114
|
+
if (axes.zoom)
|
|
115
|
+
moves.push(controls.dollyTo(camera.zoomFactorToDistance(1), transition));
|
|
116
|
+
// Normalized first, so a plan turned several times over unwinds to the nearest north rather
|
|
117
|
+
// than spinning back through every turn it was given.
|
|
118
|
+
if (axes.roll)
|
|
119
|
+
moves.push(controls.normalizeRotations().rotateAzimuthTo(0, transition));
|
|
120
|
+
if (axes.pitch)
|
|
121
|
+
moves.push(controls.rotatePolarTo(controls.minPolarAngle, transition));
|
|
122
|
+
await this.withInvalidate(() => Promise.all(moves));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Runs a move against the controls and asks for the frame that shows it.
|
|
126
|
+
*
|
|
127
|
+
* Every method above that takes {@link TransitionOptions} funnels its move through this one
|
|
128
|
+
* helper. A method that calls `controls` directly instead skips it, and looks obviously unlike
|
|
129
|
+
* its siblings.
|
|
130
|
+
*
|
|
131
|
+
* The invalidate happens before the move settles, not after, and every time, not only when the
|
|
132
|
+
* move is immediate. An eased move already triggers its own invalidate through `transitionstart`.
|
|
133
|
+
* An immediate move fires no event at all. `camera-controls` only applies an immediate move inside
|
|
134
|
+
* its own `update`, which runs from a frame that only an invalidate schedules. Invalidating after
|
|
135
|
+
* the move's promise settles would be too late to schedule that frame.
|
|
136
|
+
*
|
|
137
|
+
* A field and not a method, so it never lands on `CameraCommands.prototype`. The guard spec walks
|
|
138
|
+
* that prototype and calls everything it finds there, and an implementation detail would fail that
|
|
139
|
+
* walk for no reason.
|
|
140
|
+
* @param move - Issues the move against the controls and returns its promise.
|
|
141
|
+
* @returns The same promise `move` returned.
|
|
142
|
+
*/
|
|
143
|
+
withInvalidate = (move) => {
|
|
144
|
+
this.context.invalidate();
|
|
145
|
+
return move();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* How far the plan is turned from north: clockwise, in radians, in `[0, 2π)`.
|
|
150
|
+
*
|
|
151
|
+
* The camera holds an azimuth, which runs the other way and over any range the user has spun it
|
|
152
|
+
* through. Every reader of "which way is the plan facing" wants this instead, so the negation and
|
|
153
|
+
* the wrap live here once rather than at each of them — the same getter the imperative renderer put
|
|
154
|
+
* on its controller. Beside the rest of "where the camera is", which is the only question it
|
|
155
|
+
* answers.
|
|
156
|
+
* @param controls - The scene's controls.
|
|
157
|
+
* @returns The bearing in radians.
|
|
158
|
+
*/
|
|
159
|
+
export function bearing(controls) {
|
|
160
|
+
return euclideanModulo(-controls.azimuthAngle, 2 * Math.PI);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Takes a point on the plan into world coordinates.
|
|
164
|
+
* @param space - The scene's space.
|
|
165
|
+
* @param point - The point, in plan units.
|
|
166
|
+
* @param out - Where to write the result.
|
|
167
|
+
* @returns The world point.
|
|
168
|
+
*/
|
|
169
|
+
function toWorld(space, point, out = _target) {
|
|
170
|
+
return space.planToWorld({ x: point.x, y: point.y, z: 0 }, out);
|
|
171
|
+
}
|
|
172
|
+
/** Every axis, which is what {@link CameraAPI.reset} resets when a caller names none. */
|
|
173
|
+
const ALL_AXES = { pan: true, zoom: true, roll: true, pitch: true };
|
|
174
|
+
/** What a command hands back before the controls have mounted and there is nothing to move. */
|
|
175
|
+
const RESOLVED = Promise.resolve();
|
|
176
|
+
const ORIGIN = { x: 0, y: 0 };
|
|
177
|
+
const _target = new Vector3();
|
|
178
|
+
const _origin = new Vector3();
|
|
179
|
+
const _current = new Vector3();
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { EventManager, type MjolnirGestureEvent } from 'mjolnir.js';
|
|
2
|
+
import { type Camera } from 'three';
|
|
3
|
+
import { type CanvasPointLike, type Space } from '../space/Space';
|
|
4
|
+
import { type CameraControlsImpl } from './CameraControls';
|
|
5
|
+
/**
|
|
6
|
+
* The canvas's multi-pointer gesture recognizer.
|
|
7
|
+
*
|
|
8
|
+
* Aliased so a reader is never left wondering which `EventManager` is meant: R3F has one of its own
|
|
9
|
+
* — the pointer-event dispatcher behind `state.events` — and the two meet in this file.
|
|
10
|
+
*/
|
|
11
|
+
export type GestureManager = EventManager;
|
|
12
|
+
/**
|
|
13
|
+
* What a gesture needs from the scene at the moment it fires.
|
|
14
|
+
*
|
|
15
|
+
* Handed in on every event rather than held by the gesture, because all three may be swapped out
|
|
16
|
+
* under a mounted handler — R3F replaces the camera, the controls publish themselves a commit
|
|
17
|
+
* later — and a gesture that captured them once would go on driving the old ones.
|
|
18
|
+
*/
|
|
19
|
+
export interface GestureContext {
|
|
20
|
+
/** The controls the gesture moves. */
|
|
21
|
+
controls: CameraControlsImpl;
|
|
22
|
+
/** The camera the canvas is drawn through, which canvas positions are cast from. */
|
|
23
|
+
camera: Camera;
|
|
24
|
+
/** The scene's space, which canvas positions are resolved on the plan through. */
|
|
25
|
+
space: Space;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Subscribes to one of the canvas's gestures for as long as the component is mounted.
|
|
29
|
+
*
|
|
30
|
+
* **Two fingers are why any of this exists.** camera-controls recognizes a pinch and an orbit, but
|
|
31
|
+
* neither of the two-finger gestures the plan needs: a twist that turns the plan about the point
|
|
32
|
+
* between the fingers, and a vertical drag that tilts it. Both come off `mjolnir.js`, whose
|
|
33
|
+
* recognizer configuration is the imperative renderer's verbatim — as does the double tap that
|
|
34
|
+
* `Zoom` zooms on, which camera-controls does not recognize either.
|
|
35
|
+
*
|
|
36
|
+
* The recognizer itself is shared: `Roll`, `Pitch` and `Zoom` want one on the same canvas, and two would
|
|
37
|
+
* mean two sets of pointer listeners racing over the same fingers. It is built on the first
|
|
38
|
+
* subscription and torn down with the last, since it attaches its listeners in its constructor and
|
|
39
|
+
* offers no disconnect. Every recognizer stays dormant until something subscribes to its event, so
|
|
40
|
+
* mounting `Roll` is literally what turns `rotate` on.
|
|
41
|
+
* @param type - The gesture event to listen for, e.g. `rotatestart`.
|
|
42
|
+
* @param handler - What to run when it fires. Re-subscribes whenever its identity changes, so build
|
|
43
|
+
* it where the React Compiler can cache it against what it captures.
|
|
44
|
+
*/
|
|
45
|
+
export declare function useGesture(type: string, handler: (event: MjolnirGestureEvent) => void): void;
|
|
46
|
+
/**
|
|
47
|
+
* Takes out a share in the canvas's recognizer, building it if this is the first.
|
|
48
|
+
*
|
|
49
|
+
* The plumbing behind {@link useGesture}, which is what a component uses; exported so the sharing
|
|
50
|
+
* and the teardown can be pinned by a spec. Every call must be paired with a release.
|
|
51
|
+
* @param element - The element to recognize on.
|
|
52
|
+
* @returns The manager.
|
|
53
|
+
*/
|
|
54
|
+
export declare function acquireGestureManager(element: HTMLElement): GestureManager;
|
|
55
|
+
/**
|
|
56
|
+
* Gives back a share taken out by {@link acquireGestureManager}, tearing the recognizer down once
|
|
57
|
+
* the last one is returned.
|
|
58
|
+
* @param element - The element it was acquired for.
|
|
59
|
+
*/
|
|
60
|
+
export declare function releaseGestureManager(element: HTMLElement): void;
|
|
61
|
+
/**
|
|
62
|
+
* Where one of a gesture's pointers sits on the canvas, in css pixels from its top-left corner.
|
|
63
|
+
*
|
|
64
|
+
* The one place a DOM event crosses into {@link CanvasPointLike}, which is why the assertion is
|
|
65
|
+
* written here rather than at each handler: `offsetX`/`offsetY` are canvas coordinates by
|
|
66
|
+
* definition, being measured from the target's own padding edge.
|
|
67
|
+
* @param event - One of the pointer events a gesture carries.
|
|
68
|
+
* @returns The point on the canvas.
|
|
69
|
+
*/
|
|
70
|
+
export declare function canvasPoint(event: MouseEvent): CanvasPointLike;
|
|
71
|
+
//# sourceMappingURL=gestures.d.ts.map
|