@pmndrs/handle
Advanced tools
Comparing version 6.4.12-alpha.48 to 6.4.12-alpha.54
@@ -1,2 +0,2 @@ | ||
import { Camera, Quaternion, Vector3, Vector3Tuple } from 'three'; | ||
import { Object3D, Quaternion, Vector3, Vector3Tuple } from 'three'; | ||
import { StoreApi } from 'zustand/vanilla'; | ||
@@ -7,4 +7,4 @@ export declare function defaultScreenCameraApply(update: Partial<ScreenCameraState>, store: StoreApi<ScreenCameraState>): void; | ||
origin: Readonly<Vector3Tuple>; | ||
rotationY: number; | ||
rotationX: number; | ||
yaw: number; | ||
pitch: number; | ||
}; | ||
@@ -16,3 +16,5 @@ export type ScreenCameraStateAndFunctions = ScreenCameraState & { | ||
}; | ||
export declare function createScreenCameraStore({ distance, origin, rotationX, rotationY, }?: Partial<ScreenCameraState>): StoreApi<ScreenCameraStateAndFunctions>; | ||
export declare function applyScreenCameraState(store: StoreApi<ScreenCameraStateAndFunctions>, getCamera: () => Camera): () => void; | ||
export declare function computeScreenCameraStoreTransformation(pitch: number, yaw: number, cameraDistanceToOrigin: number, origin: Readonly<Vector3Tuple>, position?: Vector3, rotation?: Quaternion, up?: Vector3): void; | ||
export declare function createScreenCameraStore({ distance, origin, pitch: rotationX, yaw: rotationY }?: Partial<ScreenCameraState>, up?: Vector3): StoreApi<ScreenCameraStateAndFunctions>; | ||
export declare function applyScreenCameraState(store: StoreApi<ScreenCameraStateAndFunctions>, getTarget: () => Object3D | undefined | null): () => void; | ||
export declare function applyDampedScreenCameraState(store: StoreApi<ScreenCameraStateAndFunctions>, getTarget: () => Object3D | undefined | null, getDamping: () => number | boolean, up?: Vector3): (deltaTime: number) => void; |
@@ -1,4 +0,6 @@ | ||
import { Euler, Quaternion, Vector3 } from 'three'; | ||
import { ArrayCamera, Euler, Object3D, Quaternion, Vector3 } from 'three'; | ||
import { damp } from 'three/src/math/MathUtils.js'; | ||
import { createStore } from 'zustand/vanilla'; | ||
const negZAxis = new Vector3(0, 0, -1); | ||
const zAxis = new Vector3(0, 0, 1); | ||
const yAxis = new Vector3(0, 1, 0); | ||
export function defaultScreenCameraApply(update, store) { | ||
@@ -11,47 +13,54 @@ store.setState(update); | ||
const qHelper = new Quaternion(); | ||
function computeOriginToCameraOffset(target, state, cameraRotation) { | ||
target.copy(negZAxis).applyEuler(cameraRotation).multiplyScalar(state.distance); | ||
function computeOriginToCameraOffset(target, cameraDistanceToOrigin, cameraRotation, zToUp) { | ||
target.copy(zAxis).applyEuler(cameraRotation).applyQuaternion(zToUp).multiplyScalar(cameraDistanceToOrigin); | ||
} | ||
function buildCameraPositionUpdate(update, x, y, z, origin) { | ||
function buildCameraPositionUpdate(update, x, y, z, origin, upToZ) { | ||
v1Helper.set(x, y, z); | ||
v2Helper.set(...origin); | ||
v1Helper.sub(v2Helper); | ||
v1Helper.applyQuaternion(upToZ); | ||
const distance = v1Helper.length(); | ||
v1Helper.divideScalar(distance); | ||
qHelper.setFromUnitVectors(negZAxis, v1Helper); | ||
qHelper.setFromUnitVectors(zAxis, v1Helper); | ||
eHelper.setFromQuaternion(qHelper, 'YXZ'); | ||
update.distance = distance; | ||
update.rotationX = eHelper.x; | ||
update.rotationY = eHelper.y; | ||
update.pitch = eHelper.x; | ||
update.yaw = eHelper.y; | ||
} | ||
const offsetHelper = new Vector3(); | ||
const cameraRotationOffset = new Quaternion().setFromAxisAngle({ x: 0, y: 1, z: 0 }, Math.PI); | ||
export function createScreenCameraStore({ distance = 5, origin = [0, 0, 0], rotationX = 0, rotationY = 0, } = {}) { | ||
const zToUpHelper = new Quaternion(); | ||
export function computeScreenCameraStoreTransformation(pitch, yaw, cameraDistanceToOrigin, origin, position, rotation, up = Object3D.DEFAULT_UP) { | ||
zToUpHelper.setFromUnitVectors(yAxis, up); | ||
eHelper.set(pitch, yaw, 0, 'YXZ'); | ||
if (position != null) { | ||
computeOriginToCameraOffset(position, cameraDistanceToOrigin, eHelper, zToUpHelper); | ||
const [x, y, z] = origin; | ||
position.x += x; | ||
position.y += y; | ||
position.z += z; | ||
} | ||
if (rotation != null) { | ||
rotation.setFromEuler(eHelper).premultiply(zToUpHelper); | ||
} | ||
} | ||
export function createScreenCameraStore({ distance = 5, origin = [0, 0, 0], pitch: rotationX = 0, yaw: rotationY = 0 } = {}, up = Object3D.DEFAULT_UP) { | ||
const upToZ = new Quaternion().setFromUnitVectors(up, yAxis); | ||
const zToUp = upToZ.clone().invert(); | ||
return createStore((set, get) => ({ | ||
distance, | ||
origin, | ||
rotationX, | ||
rotationY, | ||
pitch: rotationX, | ||
yaw: rotationY, | ||
activeHandle: undefined, | ||
getCameraTransformation(position, rotation) { | ||
const state = get(); | ||
eHelper.set(state.rotationX, state.rotationY, 0, 'YXZ'); | ||
if (position != null) { | ||
computeOriginToCameraOffset(position, state, eHelper); | ||
const [x, y, z] = state.origin; | ||
position.x += x; | ||
position.y += y; | ||
position.z += z; | ||
} | ||
if (rotation != null) { | ||
rotation.setFromEuler(eHelper).multiply(cameraRotationOffset); | ||
} | ||
const { pitch, distance, yaw, origin } = get(); | ||
computeScreenCameraStoreTransformation(pitch, yaw, distance, origin, position, rotation, up); | ||
}, | ||
setCameraPosition(x, y, z, keepOffsetToOrigin) { | ||
const update = {}; | ||
buildCameraPositionUpdate(update, x, y, z, get().origin); | ||
buildCameraPositionUpdate(update, x, y, z, get().origin, upToZ); | ||
if (keepOffsetToOrigin === true) { | ||
const state = get(); | ||
eHelper.set(state.rotationX, state.rotationY, 0, 'YXZ'); | ||
computeOriginToCameraOffset(offsetHelper, state, eHelper); | ||
eHelper.set(state.pitch, state.yaw, 0, 'YXZ'); | ||
computeOriginToCameraOffset(offsetHelper, state.distance, eHelper, zToUp); | ||
offsetHelper.x -= x; | ||
@@ -71,8 +80,8 @@ offsetHelper.y -= y; | ||
const state = get(); | ||
eHelper.set(state.rotationX, state.rotationY, 0, 'YXZ'); | ||
computeOriginToCameraOffset(offsetHelper, state, eHelper); | ||
eHelper.set(state.pitch, state.yaw, 0, 'YXZ'); | ||
computeOriginToCameraOffset(offsetHelper, state.distance, eHelper, zToUp); | ||
offsetHelper.x += x; | ||
offsetHelper.y += y; | ||
offsetHelper.z += z; | ||
buildCameraPositionUpdate(update, offsetHelper.x, offsetHelper.y, offsetHelper.z, origin); | ||
buildCameraPositionUpdate(update, offsetHelper.x, offsetHelper.y, offsetHelper.z, origin, upToZ); | ||
} | ||
@@ -83,7 +92,9 @@ set(update); | ||
} | ||
//TODO: enable damping | ||
export function applyScreenCameraState(store, getCamera) { | ||
export function applyScreenCameraState(store, getTarget) { | ||
const fn = (state) => { | ||
const camera = getCamera(); | ||
state.getCameraTransformation(camera.position, camera.quaternion); | ||
const target = getTarget(); | ||
if (target == null) { | ||
return; | ||
} | ||
state.getCameraTransformation(target.position, target.quaternion); | ||
}; | ||
@@ -93,1 +104,30 @@ fn(store.getState()); | ||
} | ||
export function applyDampedScreenCameraState(store, getTarget, getDamping, up = Object3D.DEFAULT_UP) { | ||
let { distance, yaw, origin: [originX, originY, originZ], pitch, } = store.getState(); | ||
return (deltaTime) => { | ||
const target = getTarget(); | ||
//if the target is a array camera (which is used for XR stuff, we dont apply) | ||
if (target == null || target instanceof ArrayCamera) { | ||
return; | ||
} | ||
let damping = getDamping(); | ||
if (damping === false) { | ||
return; | ||
} | ||
if (damping === true) { | ||
damping = 0.01; | ||
} | ||
const { distance: targetDistance, yaw: targetYaw, origin: [targetOriginX, targetOriginY, targetOriginZ], pitch: targetPitch, } = store.getState(); | ||
distance = damp(distance, targetDistance, damping, deltaTime); | ||
let angleDistance; | ||
while (Math.abs((angleDistance = targetYaw - yaw)) > Math.PI) { | ||
yaw += (angleDistance > 0 ? 2 : -2) * Math.PI; | ||
} | ||
yaw = damp(yaw, targetYaw, damping, deltaTime); | ||
pitch = damp(pitch, targetPitch, damping, deltaTime); | ||
originX = damp(originX, targetOriginX, damping, deltaTime); | ||
originY = damp(originY, targetOriginY, damping, deltaTime); | ||
originZ = damp(originZ, targetOriginZ, damping, deltaTime); | ||
computeScreenCameraStoreTransformation(pitch, yaw, distance, [originX, originY, originZ], target.position, target.quaternion, up); | ||
}; | ||
} |
@@ -14,6 +14,8 @@ import { RotateScreenHandleStore } from './rotate.js'; | ||
private readonly getCamera; | ||
private updateDamping; | ||
private damping; | ||
constructor(canvas: HTMLCanvasElement, camera: (() => PerspectiveCamera | OrthographicCamera) | PerspectiveCamera | OrthographicCamera, store?: StoreApi<ScreenCameraStateAndFunctions>); | ||
getStore(): StoreApi<ScreenCameraStateAndFunctions>; | ||
update(): void; | ||
bind(scene: Scene): () => void; | ||
update(deltaTime: number): void; | ||
bind(scene: Scene, damping?: boolean | number): () => void; | ||
} | ||
@@ -20,0 +22,0 @@ /** |
import { RotateScreenHandleStore } from './rotate.js'; | ||
import { ZoomScreenHandleStore } from './zoom.js'; | ||
import { PanScreenHandleStore } from './pan.js'; | ||
import { applyScreenCameraState, createScreenCameraStore, } from './camera.js'; | ||
import { applyDampedScreenCameraState, applyScreenCameraState, createScreenCameraStore, } from './camera.js'; | ||
import { filterForOnePointerLeftClick, filterForOnePointerRightClickOrTwoPointer } from './index.js'; | ||
@@ -10,4 +10,4 @@ import { clamp } from 'three/src/math/MathUtils.js'; | ||
export function defaultMapHandlesScreenCameraApply(update, store) { | ||
if (update.rotationX != null) { | ||
update.rotationX = clamp(update.rotationX, 0, Math.PI / 2); | ||
if (update.pitch != null) { | ||
update.pitch = clamp(update.pitch, 0, Math.PI / 2); | ||
} | ||
@@ -22,2 +22,4 @@ store.setState(update); | ||
getCamera; | ||
updateDamping; | ||
damping = false; | ||
constructor(canvas, camera, store) { | ||
@@ -31,2 +33,3 @@ if (store == null) { | ||
this.getCamera = typeof camera === 'function' ? camera : () => camera; | ||
this.updateDamping = applyDampedScreenCameraState(store, this.getCamera, () => this.damping); | ||
this.rotate = new RotateScreenHandleStore(store, this.getCamera, filterForOnePointerRightClickOrTwoPointer, defaultMapHandlesScreenCameraApply); | ||
@@ -39,12 +42,17 @@ this.pan = new PanScreenHandleStore(canvas, store, this.getCamera, filterForOnePointerLeftClick, defaultMapHandlesScreenCameraApply); | ||
} | ||
update() { | ||
update(deltaTime) { | ||
this.rotate.update(); | ||
this.pan.update(); | ||
this.zoom.update(); | ||
this.updateDamping(deltaTime); | ||
} | ||
bind(scene) { | ||
bind(scene, damping = false) { | ||
const unbindRotate = this.rotate.bind(scene); | ||
const unbindPan = this.pan.bind(scene); | ||
const unbindZoom = this.zoom.bind(scene); | ||
const unsubscribeCamera = applyScreenCameraState(this.store, this.getCamera); | ||
let unsubscribeCamera; | ||
if (damping === false) { | ||
unsubscribeCamera = applyScreenCameraState(this.store, this.getCamera); | ||
} | ||
this.damping = damping; | ||
return () => { | ||
@@ -54,3 +62,3 @@ unbindRotate(); | ||
unbindZoom(); | ||
unsubscribeCamera(); | ||
unsubscribeCamera?.(); | ||
}; | ||
@@ -57,0 +65,0 @@ } |
@@ -14,7 +14,12 @@ import { RotateScreenHandleStore } from './rotate.js'; | ||
private readonly getCamera; | ||
private updateDamping; | ||
private damping; | ||
constructor(canvas: HTMLCanvasElement, camera: (() => PerspectiveCamera | OrthographicCamera) | PerspectiveCamera | OrthographicCamera, store?: StoreApi<ScreenCameraStateAndFunctions>); | ||
getStore(): StoreApi<ScreenCameraStateAndFunctions>; | ||
update(): void; | ||
bind(scene: Scene): () => void; | ||
update(deltaTime: number): void; | ||
bind(scene: Scene, damping?: boolean | number): () => void; | ||
} | ||
/** | ||
* @deprecated use OrbitHandles instead | ||
*/ | ||
export declare const OrbitControls: typeof OrbitHandles; |
@@ -6,7 +6,7 @@ import { RotateScreenHandleStore } from './rotate.js'; | ||
import { Vector3 } from 'three'; | ||
import { applyScreenCameraState, createScreenCameraStore, } from './camera.js'; | ||
import { applyDampedScreenCameraState, applyScreenCameraState, createScreenCameraStore, } from './camera.js'; | ||
import { clamp } from 'three/src/math/MathUtils.js'; | ||
export function defaultOrbitHandlesScreenCameraApply(update, store) { | ||
if (update.rotationX != null) { | ||
update.rotationX = clamp(update.rotationX, -Math.PI / 2, Math.PI / 2); | ||
if (update.pitch != null) { | ||
update.pitch = clamp(update.pitch, -Math.PI / 2, Math.PI / 2); | ||
} | ||
@@ -22,2 +22,4 @@ store.setState(update); | ||
getCamera; | ||
updateDamping; | ||
damping = false; | ||
constructor(canvas, camera, store) { | ||
@@ -31,2 +33,3 @@ if (store == null) { | ||
this.getCamera = typeof camera === 'function' ? camera : () => camera; | ||
this.updateDamping = applyDampedScreenCameraState(store, this.getCamera, () => this.damping); | ||
this.rotate = new RotateScreenHandleStore(store, this.getCamera, filterForOnePointerLeftClick, defaultOrbitHandlesScreenCameraApply); | ||
@@ -39,12 +42,17 @@ this.pan = new PanScreenHandleStore(canvas, store, this.getCamera, filterForOnePointerRightClickOrTwoPointer, defaultOrbitHandlesScreenCameraApply); | ||
} | ||
update() { | ||
update(deltaTime) { | ||
this.rotate.update(); | ||
this.pan.update(); | ||
this.zoom.update(); | ||
this.updateDamping(deltaTime); | ||
} | ||
bind(scene) { | ||
bind(scene, damping = false) { | ||
const unbindRotate = this.rotate.bind(scene); | ||
const unbindPan = this.pan.bind(scene); | ||
const unbindZoom = this.zoom.bind(scene); | ||
const unsubscribeCamera = applyScreenCameraState(this.store, this.getCamera); | ||
let unsubscribeCamera; | ||
if (damping === false) { | ||
unsubscribeCamera = applyScreenCameraState(this.store, this.getCamera); | ||
} | ||
this.damping = damping; | ||
return () => { | ||
@@ -54,6 +62,9 @@ unbindRotate(); | ||
unbindZoom(); | ||
unsubscribeCamera(); | ||
unsubscribeCamera?.(); | ||
}; | ||
} | ||
} | ||
/** | ||
* @deprecated use OrbitHandles instead | ||
*/ | ||
export const OrbitControls = OrbitHandles; |
@@ -12,3 +12,3 @@ import { PerspectiveCamera, Vector2 } from 'three'; | ||
constructor(store, getCamera, filter, customApply, speed) { | ||
super(([initialRotationX, initialRotationY], map) => { | ||
super(([initialPitch, initialYaw], map) => { | ||
if (!this.filter(map)) { | ||
@@ -25,6 +25,6 @@ return; | ||
(this.customApply ?? defaultScreenCameraApply)({ | ||
rotationX: vector2Helper.y + initialRotationX, | ||
rotationY: vector2Helper.x * aspect + initialRotationY, | ||
pitch: initialPitch - vector2Helper.y, | ||
yaw: initialYaw + vector2Helper.x * aspect, | ||
}, store); | ||
}, () => [store.getState().rotationX, store.getState().rotationY]); | ||
}, () => [store.getState().pitch, store.getState().yaw]); | ||
this.filter = filter; | ||
@@ -31,0 +31,0 @@ this.customApply = customApply; |
@@ -7,3 +7,3 @@ { | ||
"homepage": "https://github.com/pmndrs/xr", | ||
"version": "6.4.12-alpha.48", | ||
"version": "6.4.12-alpha.54", | ||
"keywords": [ | ||
@@ -25,3 +25,3 @@ "r3f", | ||
"zustand": "^4.5.2", | ||
"@pmndrs/pointer-events": "^6.4.12-alpha.48" | ||
"@pmndrs/pointer-events": "^6.4.12-alpha.54" | ||
}, | ||
@@ -28,0 +28,0 @@ "files": [ |
168484
3926
+ Added@pmndrs/pointer-events@6.5.3(transitive)
- Removed@pmndrs/pointer-events@6.5.4(transitive)