Skip to content

Commit

Permalink
Add possiblility to use useFlexibleCameraManager (#4175)
Browse files Browse the repository at this point in the history
  • Loading branch information
nilscognite authored Feb 12, 2024
1 parent 6a20636 commit 5fc7fca
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 53 deletions.
22 changes: 22 additions & 0 deletions viewer/packages/camera-manager/src/Flexible/DampedSpherical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ export class DampedSpherical {
DampedSpherical.dampSphericalVectors(this.value, this.end, dampeningFactor);
}

static correctPhi(value: Spherical): void {
// https://github.com/mrdoob/three.js/blob/master/src/math/Spherical.js
// When phi is outside the range (-Pi/2, Pi/2), correct it so it is inside the range by
// rotatation the theta Pi radians and flipping the phi.

// First normalize so Phi is in the range (-Pi, Pi)
while (value.phi < -Math.PI) {
value.phi += 2 * Math.PI;
}
while (value.phi >= Math.PI) {
value.phi -= 2 * Math.PI;
}
// Flipping phi
if (value.phi < -Math.PI / 2) {
value.theta += Math.PI;
value.phi = -Math.PI - value.phi;
} else if (value.phi > Math.PI / 2) {
value.theta += Math.PI;
value.phi = Math.PI - value.phi;
}
}

static dampSphericalVectors(value: Spherical, end: Spherical, dampeningFactor: number): void {
const deltaPhi = end.phi - value.phi;
const deltaTheta = getShortestDeltaTheta(end.theta, value.theta);
Expand Down
4 changes: 4 additions & 0 deletions viewer/packages/camera-manager/src/Flexible/DampedVector3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class DampedVector3 {
this.value.copy(this.end);
}

synchronizeEnd(): void {
this.end.copy(this.value);
}

damp(dampeningFactor: number): void {
this.value.lerp(this.end, dampeningFactor);
}
Expand Down
94 changes: 43 additions & 51 deletions viewer/packages/camera-manager/src/Flexible/FlexibleControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
private _scrollDistance = 0; // When using the wheel this is the distance to the picked point
private _tempTarget: Vector3 | undefined = undefined;

private readonly _accumulatedMouseRotation: Vector2 = new Vector2();
private readonly _keyboard: Keyboard;
private readonly _pointEventCache: Array<PointerEvent> = [];

Expand Down Expand Up @@ -293,19 +292,12 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
if (!forceUpdate && !this.isEnabled) {
return false;
}
const isRotated = this._accumulatedMouseRotation.lengthSq() > 1;
if (isRotated) {
this.rotate(this._accumulatedMouseRotation);
this._accumulatedMouseRotation.set(0, 0);
}
const isKeyPressed = this.handleKeyboard(deltaTimeS);
const epsilon = this._options.EPSILON;
const isChanged =
this._target.isChanged(epsilon) ||
this._cameraVector.isChanged(epsilon) ||
this._cameraPosition.isChanged(epsilon);

const isKeyPressed = this.handleKeyboard(deltaTimeS);
const isRotated = this._cameraVector.isChanged(epsilon);
const isChanged = isRotated || this._target.isChanged(epsilon) || this._cameraPosition.isChanged(epsilon);
const dampeningFactor = isKeyPressed ? 1 : this.getDampingFactor(deltaTimeS);

if (isChanged && dampeningFactor < 1) {
this._cameraVector.damp(dampeningFactor);
if (isRotated) {
Expand Down Expand Up @@ -386,9 +378,6 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
private readonly onPointerUp = (event: PointerEvent) => {
if (!this.isEnabled) return;
switch (event.pointerType) {
case 'mouse':
this.onMouseUp();
break;
case 'touch':
remove(this._pointEventCache, ev => ev.pointerId === event.pointerId);
break;
Expand All @@ -397,11 +386,6 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
}
};

private readonly onMouseUp = () => {
if (!this.isEnabled) return;
this._accumulatedMouseRotation.set(0, 0);
};

private readonly onMouseWheel = (event: WheelEvent) => {
if (!this.isEnabled) return;
event.preventDefault();
Expand Down Expand Up @@ -489,11 +473,13 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
const position = getMousePosition(this._domElement, event.clientX, event.clientY);
const deltaPosition = position.clone().sub(prevPosition);
if (this._keyboard.isShiftPressed()) {
this.pan(0, 0, deltaPosition.y * this.options.mouseDollySpeed);
deltaPosition.multiplyScalar(this._options.mouseDollySpeed);
this.pan(0, 0, deltaPosition.y);
} else if (this._keyboard.isCtrlPressed()) {
this.pan(deltaPosition.x * this.options.mousePanSpeed, deltaPosition.y * this.options.mousePanSpeed, 0);
deltaPosition.multiplyScalar(this._options.mousePanSpeed);
this.pan(deltaPosition.x, deltaPosition.y, 0);
} else {
this._accumulatedMouseRotation.sub(deltaPosition);
this.rotate(deltaPosition);
}
prevPosition = position;
};
Expand All @@ -516,8 +502,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
return;
}
const position = getMousePosition(this._domElement, event.clientX, event.clientY);
prevPosition.sub(position);
this.rotate(new Vector2().subVectors(prevPosition, position));
this.rotate(new Vector2().subVectors(position, prevPosition));
prevPosition = position;
};

Expand Down Expand Up @@ -549,10 +534,13 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
return;
}
let deltaAzimuthAngle = this._options.mouseRotationSpeedAzimuth * delta.x;
const deltaPolarAngle = this._options.mouseRotationSpeedPolar * delta.y;
let deltaPolarAngle = this._options.mouseRotationSpeedPolar * delta.y;
// It is more natural that the first persion rotate slower then the other modes
if (this.controlsType == FlexibleControlsType.FirstPerson) {
deltaAzimuthAngle *= this.getAzimuthCompensationFactor();
deltaAzimuthAngle *= 0.5;
deltaPolarAngle *= 0.5;
}
deltaAzimuthAngle *= this.getAzimuthCompensationFactor();
this.rotateByAngles(deltaAzimuthAngle, deltaPolarAngle);
}

Expand All @@ -561,7 +549,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
// to make it feel more natural when looking straight up or straight down.
const deviationFromEquator = Math.abs(this._cameraVector.end.phi - Math.PI / 2);
const azimuthCompensationFactor = Math.sin(Math.PI / 2 - deviationFromEquator);
return azimuthCompensationFactor;
return 0.5 + 0.5 * azimuthCompensationFactor;
}

private rotateByAngles(deltaAzimuth: number, deltaPolar: number) {
Expand All @@ -570,7 +558,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
}

if (this.controlsType === FlexibleControlsType.OrbitInCenter) {
this.rawRotateByAngles(deltaAzimuth, -deltaPolar);
this.rawRotateByAngles(-deltaAzimuth, deltaPolar);

// Adust the camera position by
// CameraPosition = Target - DistanceToTarget * CameraVector
Expand All @@ -583,7 +571,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
const oldOffset = this.newVector3().subVectors(this._target.end, this._cameraPosition.end);
const oldCameraVectorEnd = this._cameraVector.end.clone();

this.rawRotateByAngles(deltaAzimuth, -deltaPolar);
this.rawRotateByAngles(-deltaAzimuth, deltaPolar);

// Adust the camera position so the target point is the same on the screen
const oldQuat = new Quaternion().multiplyQuaternions(
Expand All @@ -600,7 +588,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
const newCameraPosition = this.newVector3().subVectors(this._target.end, newOffset);
this._cameraPosition.end.copy(newCameraPosition);
} else {
this.rawRotateByAngles(deltaAzimuth, -deltaPolar);
this.rawRotateByAngles(-deltaAzimuth, deltaPolar);
}
}

Expand Down Expand Up @@ -738,11 +726,11 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
}

private dollyWithWheelScroll(pixelCoordinates: Vector2, deltaDistance: number) {
const translation = this.getRadiusAndTranslation(pixelCoordinates, deltaDistance);
const translation = this.getTranslation(pixelCoordinates, deltaDistance);
this.translate(translation);
}

private getRadiusAndTranslation(pixelCoordinates: Vector2, deltaDistance: number): Vector3 {
private getTranslation(pixelCoordinates: Vector2, deltaDistance: number): Vector3 {
if (this.options.shouldPick) {
return this.getTranslationByScrollCursor(pixelCoordinates, deltaDistance);
}
Expand All @@ -762,29 +750,33 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
if (this._scrollDirection === undefined) {
return this.getTranslationByDirection(pixelCoordinates, deltaDistance);
}
let step = this.options.zoomFraction * this._scrollDistance * Math.sign(deltaDistance);
if (
this.options.realMouseWheelAction === FlexibleWheelZoomType.PastCursor &&
Math.abs(step) < Math.abs(deltaDistance)
) {
// If past or near the scroll cursor, go in equal steps
step = deltaDistance;
}
const prevScrollDistance = this._scrollDistance;
const step = this.getStep(deltaDistance);
this._scrollDistance -= step;
if (
this.options.realMouseWheelAction === FlexibleWheelZoomType.ToCursor &&
this._scrollDistance < this.options.sensitivity
) {
// This avoid to close to the scroll cursor
this._scrollDistance = this.options.sensitivity;
step = prevScrollDistance - this._scrollDistance;
}
const translation = this._scrollDirection.clone();

translation.multiplyScalar(step);
return translation;
}

private getStep(deltaDistance: number): number {
const step = this.options.zoomFraction * this._scrollDistance * Math.sign(deltaDistance);
if (this.options.realMouseWheelAction === FlexibleWheelZoomType.PastCursor) {
const minStep = Math.abs(deltaDistance * 0.5);
if (Math.abs(step) < minStep) {
// If past or near the scroll cursor, go in equal steps
return minStep * Math.sign(deltaDistance);
}
}
if (this.options.realMouseWheelAction === FlexibleWheelZoomType.ToCursor) {
if (this._scrollDistance - step < this.options.sensitivity) {
// This avoid to close to the scroll cursor
// Can not get closer than options.sensitivity
return Math.max(0, this._scrollDistance - this.options.sensitivity);
}
}
return step;
}

//================================================
// INSTANCE METHODS: Keyboard
//================================================
Expand Down Expand Up @@ -821,7 +813,7 @@ export class FlexibleControls extends EventDispatcher<FlexibleControlsEvent> {
return false;
}
this.setControlsType(FlexibleControlsType.FirstPerson);
this.rotateByAngles(deltaAzimuthAngle, deltaPolarAngle);
this.rotateByAngles(-deltaAzimuthAngle, -deltaPolarAngle);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { FlexibleMouseActionType } from './FlexibleMouseActionType';
import { FlexibleWheelZoomType } from './FlexibleWheelZoomType';

const DEFAULT_POINTER_ROTATION_SPEED = (0.5 * Math.PI) / 360; // half degree per pixel
const DEFAULT_KEYBOARD_ROTATION_SPEED = DEFAULT_POINTER_ROTATION_SPEED * 5;
const DEFAULT_KEYBOARD_ROTATION_SPEED = DEFAULT_POINTER_ROTATION_SPEED * 2.5;
const DEFAULT_MIN_POLAR_ANGLE = 0.0001;

/**
Expand Down Expand Up @@ -69,7 +69,7 @@ export class FlexibleControlsOptions {
public maxOrthographicZoom = Infinity;

// Mouse speed for dolly and pan
public wheelDollySpeed = 1;
public wheelDollySpeed = 0.5;
public mousePanSpeed = 25;
public mouseDollySpeed = 100;

Expand Down

0 comments on commit 5fc7fca

Please sign in to comment.