/** * Camera orbit, pan, and zoom controls. * * Orbit uses a pivot point: * - Default pivot = camera.target (standard orbit — position rotates, target stays) * - When orbitCenter is set (raycast hit / selected object), position orbits * around the pivot and the look direction is rotated by the exact same * axis-angle rotation (Rodrigues). A small vertical clamp on the look * direction prevents the view matrix from degenerating (model flip). * Approach mirrors Blender's turntable: Y-axis horizontal + right-axis vertical. */ import type { Camera as CameraType, Vec3, Mat4 } from './types.js'; /** Projection mode for the camera */ export type ProjectionMode = 'perspective' | 'orthographic'; /** * Shared mutable state for camera sub-systems. * All sub-systems reference the same state object so changes are visible across them. */ export interface CameraInternalState { camera: CameraType; viewMatrix: Mat4; projMatrix: Mat4; viewProjMatrix: Mat4; /** Current projection mode */ projectionMode: ProjectionMode; /** Orthographic half-height in world units (controls zoom level in ortho mode) */ orthoSize: number; /** Scene bounding box for tight orthographic near/far computation */ sceneBounds: { min: { x: number; y: number; z: number; }; max: { x: number; y: number; z: number; }; } | null; /** * Optional outlier-robust bounds for anchoring the orbit pivot (issue #1394). * Distinct from `sceneBounds`: the renderer keeps `sceneBounds` synced to the * FULL model AABB (needed for near/far clipping and section ranges), but a * handful of far-flung meshes can push that AABB's centre into empty space, * which the orbit-pivot fallback would then rotate around. When set, the pivot * uses this tighter centre instead. `null` ⇒ fall back to `sceneBounds`. */ orbitAnchorBounds: { min: { x: number; y: number; z: number; }; max: { x: number; y: number; z: number; }; } | null; } /** * Handles core camera movement: orbit, pan, and zoom. */ export declare class CameraControls { private readonly state; private readonly updateMatrices; /** Optional orbit pivot (set on object selection). null = orbit around camera.target. */ private orbitCenter; constructor(state: CameraInternalState, updateMatrices: () => void); /** * Set the orbit center without moving the camera. * Future orbit() calls will rotate around this point. * Pass null to revert to orbiting around camera.target. */ setOrbitCenter(center: Vec3 | null): void; /** * Orbit the camera around a pivot point. * * `camera.up` is always world Y — setPresetView positions the camera at * phi ∈ [MIN_PHI, MAX_PHI] (never on the exact pole), so the orbit math * never hits the spherical singularity and no special pole-handling is * needed. Phi is clamped to [MIN_PHI, π − MIN_PHI], keeping it just * off both poles so sinφ stays nonzero in the spherical tangent math. * * Pattern is the same as yomotsu/camera-controls and Autodesk Viewer. */ orbit(deltaX: number, deltaY: number): void; /** * Standard orbit: rotate `point` around `pivot` by spherical deltas. * Phi clamped to [MIN_PHI, MAX_PHI]. */ private rotateAroundPivot; /** * Orbit around an external pivot (turntable style with Rodrigues). * * Standard convention: dx rotates around world Y, dy tilts the camera by * rotating offset + look around the orbit-sphere tangent perpendicular to * offset's horizontal component. Phi is clamped to [MIN_PHI, MAX_PHI] * which the preset views also respect, so the tangent axis is always * well-defined. */ private orbitAroundExternalPivot; /** * Pan camera (Y-up coordinate system). * Moves both position and target by the same offset (preserves orbit relationship). */ pan(deltaX: number, deltaY: number): void; /** * Zoom camera towards mouse position. * @param delta - Zoom delta (positive = zoom out, negative = zoom in) * @param mouseX - Mouse X position in canvas coordinates * @param mouseY - Mouse Y position in canvas coordinates * @param canvasWidth - Canvas width * @param canvasHeight - Canvas height */ zoom(delta: number, mouseX?: number, mouseY?: number, canvasWidth?: number, canvasHeight?: number, fastZoom?: boolean): void; /** Orthographic: set view volume size, keep camera distance unchanged. */ private zoomOrthographic; /** * Perspective: dolly-zoom — combines distance reduction with forward travel. * * Pure multiplicative zoom suffers from Zeno's paradox: each step covers a * smaller absolute distance, so the user asymptotically approaches the target * but can never pass it. By splitting each zoom step into distance reduction + * forward dolly, the camera always makes real progress through the scene. */ private zoomPerspective; /** Shift target toward the world point under the mouse cursor. */ private shiftTargetTowardsMouse; /** Translate target, position, and orbit center by the same offset. */ private translateAll; } //# sourceMappingURL=camera-controls.d.ts.map