소스 검색

simplify rock state/trackball animation

- use sin() instead of smoothstep()
Alexander Rose 3 년 전
부모
커밋
238b70c121
2개의 변경된 파일19개의 추가작업 그리고 57개의 파일을 삭제
  1. 13 16
      src/mol-canvas3d/controls/trackball.ts
  2. 6 41
      src/mol-plugin-state/animation/built-in/camera-rock.ts

+ 13 - 16
src/mol-canvas3d/controls/trackball.ts

@@ -15,7 +15,6 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Camera } from '../camera';
 import { absMax, degToRad } from '../../mol-math/misc';
 import { Binding } from '../../mol-util/binding';
-import { smoothstep } from '../../mol-math/interpolate';
 
 const B = ButtonsType;
 const M = ModifiersKeys;
@@ -47,7 +46,7 @@ export const TrackballControlsParams = {
             speed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
         }, { description: 'Spin the 3D scene around the x-axis in view space' }),
         rock: PD.Group({
-            speed: PD.Numeric(2, { min: -20, max: 20, step: 1 }),
+            speed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
             angle: PD.Numeric(10, { min: 0, max: 90, step: 1 }, { description: 'How many degrees to rotate in each direction.' }),
         }, { description: 'Rock the 3D scene around the x-axis in view space' })
     }),
@@ -459,31 +458,29 @@ namespace TrackballControls {
             Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
         }
 
-        let _rockAngleSum = 0;
-        let _rockDirection = 1;
+        let _rockPhase = 0;
         const _rockSpeed = Vec2.create(0.005, 0);
         function rock(deltaT: number) {
             if (p.animate.name !== 'rock' || p.animate.params.speed === 0 || _isInteracting) return;
 
-            // TODO get rid of the 3.3 factor (compensates for using `smoothstep`)
-            const maxAngle = degToRad(p.animate.params.angle * 3.3) / getRotateFactor();
-            const alpha = smoothstep(0, 1, Math.abs(_rockAngleSum) / maxAngle);
+            const dt = deltaT / 4000; // normalize to 4s duration
+            const maxAngle = degToRad(p.animate.params.angle) / getRotateFactor();
 
-            const frameSpeed = p.animate.params.speed / 1000;
-            _rockSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
-            _rockAngleSum += Math.abs(_rockSpeed[0]);
-            _rockSpeed[0] *= _rockDirection * (1.1 - alpha);
+            const f = p.animate.params.speed * Math.PI * 2;
+            const angleA = Math.sin(_rockPhase * f) * maxAngle;
+            const angleB = Math.sin((_rockPhase + dt) * f) * maxAngle;
+
+            _rockSpeed[0] = angleB - angleA;
             Vec2.add(_rotCurr, _rotPrev, _rockSpeed);
 
-            if (_rockAngleSum >= maxAngle) {
-                _rockDirection *= -1;
-                _rockAngleSum = -maxAngle;
+            _rockPhase += dt;
+            if (_rockPhase >= 1) {
+                _rockPhase = 0;
             }
         }
 
         function resetRock() {
-            _rockAngleSum = 0;
-            _rockDirection = 1;
+            _rockPhase = 0;
         }
 
         function start(t: number) {

+ 6 - 41
src/mol-plugin-state/animation/built-in/camera-rock.ts

@@ -5,7 +5,7 @@
  */
 
 import { Camera } from '../../../mol-canvas3d/camera';
-import { clamp, smoothstep } from '../../../mol-math/interpolate';
+import { clamp } from '../../../mol-math/interpolate';
 import { Quat } from '../../../mol-math/linear-algebra/3d/quat';
 import { Vec3 } from '../../../mol-math/linear-algebra/3d/vec3';
 import { degToRad } from '../../../mol-math/misc';
@@ -14,10 +14,7 @@ import { PluginStateAnimation } from '../model';
 
 const _dir = Vec3(), _axis = Vec3(), _rot = Quat();
 
-type State = {
-    snapshot: Camera.Snapshot,
-    angles: number[]
-};
+type State = { snapshot: Camera.Snapshot };
 
 export const AnimateCameraRock = PluginStateAnimation.create({
     name: 'built-in.animate-camera-rock',
@@ -25,42 +22,10 @@ export const AnimateCameraRock = PluginStateAnimation.create({
     isExportable: true,
     params: () => ({
         durationInMs: PD.Numeric(4000, { min: 100, max: 20000, step: 100 }),
-        angle: PD.Numeric(10, { min: 0, max: 90, step: 1 }, { description: 'How many degrees to rotate in each direction.' }),
+        speed: PD.Numeric(1, { min: 1, max: 10, step: 1 }, { description: 'How many times to rock from side to side.' }),
+        angle: PD.Numeric(10, { min: 0, max: 180, step: 1 }, { description: 'How many degrees to rotate in each direction.' }),
     }),
-    initialState: (p, ctx) => {
-        const angles: number[] = [];
-        const frameSpeed = 1 / 1000;
-        const deltaT = 1000 / 30;
-
-        // TODO get rid of the 3.3 factor (compensates for using `smoothstep`)
-        const maxAngle = degToRad(p.angle * 3.3);
-
-        let angleSum = 0;
-        let direction = 1;
-        let zeroAngleCount = 0;
-        let prevAngle = 0;
-        while (true) {
-            const alpha = smoothstep(0, 1, Math.abs(angleSum) / maxAngle);
-            const rockSpeed = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
-            angleSum += Math.abs(rockSpeed);
-            const angle = prevAngle + rockSpeed * direction * (1.1 - alpha);
-            angles.push(angle);
-            if (Math.sign(prevAngle) !== Math.sign(angle)) {
-                zeroAngleCount += 1;
-                if (zeroAngleCount === 3) break;
-            }
-            prevAngle = angle;
-            if (angleSum >= maxAngle) {
-                direction *= -1;
-                angleSum = -maxAngle;
-            }
-        }
-
-        return {
-            snapshot: ctx.canvas3d!.camera.getSnapshot(),
-            angles
-        } as State;
-    },
+    initialState: (p, ctx) => ({ snapshot: ctx.canvas3d!.camera.getSnapshot() }) as State,
     getDuration: p => ({ kind: 'fixed', durationMs: p.durationInMs }),
     teardown: (_, state: State, ctx) => {
         ctx.canvas3d?.requestCameraReset({ snapshot: state.snapshot, durationMs: 0 });
@@ -79,7 +44,7 @@ export const AnimateCameraRock = PluginStateAnimation.create({
         const phase = t.animation
             ? t.animation?.currentFrame / (t.animation.frameCount + 1)
             : clamp(t.current / ctx.params.durationInMs, 0, 1);
-        const angle = animState.angles[Math.round(phase * (animState.angles.length - 1))];
+        const angle = Math.sin(phase * ctx.params.speed * Math.PI * 2) * degToRad(ctx.params.angle);
 
         Vec3.sub(_dir, snapshot.position, snapshot.target);
         Vec3.normalize(_axis, snapshot.up);