refactoring

This commit is contained in:
dsehnal
2026-05-30 17:02:09 +02:00
parent f238b00ef9
commit fa63209384
8 changed files with 51 additions and 19 deletions

View File

@@ -13,7 +13,9 @@ import { EasingFunction, getEasingFn } from '../../mol-math/easing';
export { CameraTransitionManager };
export interface CameraTransitionOptions {
/** If present, approximates the transion between, [current] -> [keyframes] -> -> [target] */
keyframes?: CameraTransitionManager.TransitionKeyframes,
/** Global easing, if easing is specified for keyframes, the "end" frame value is used */
easing?: EasingFunction
}
@@ -123,7 +125,7 @@ namespace CameraTransitionManager {
let tStart = 0;
let tEnd = 1;
let easingKindStart = options?.easing;
let easingKind = options?.easing;
const keyframes = options?.keyframes;
if (keyframes && keyframes.length > 0) {
@@ -132,7 +134,6 @@ namespace CameraTransitionManager {
if (t_ >= keyframe.t) {
sourcePartial = keyframe.snapshot;
tStart = keyframe.t;
easingKindStart = keyframe.easing ?? easingKindStart;
break;
}
}
@@ -141,12 +142,13 @@ namespace CameraTransitionManager {
if (keyframe.t >= t_) {
targetPartial = keyframe.snapshot;
tEnd = keyframe.t;
easingKind = keyframe.easing ?? easingKind;
break;
}
}
}
const easing = getEasingFn(easingKindStart);
const easing = getEasingFn(easingKind);
const t = easing((t_ - tStart) / (tEnd - tStart));
if (!_tempSource) _tempSource = Camera.createDefaultSnapshot();

View File

@@ -150,6 +150,10 @@ namespace Mat3 {
return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);
}
export function is(a: any): a is Mat3 {
return Array.isArray(a) && a.length === 9;
}
export function hasNaN(m: Mat3) {
for (let i = 0; i < 9; i++) if (Number.isNaN(m[i])) return true;
return false;

View File

@@ -110,6 +110,10 @@ namespace Mat4 {
return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);
}
export function is(a: any): a is Mat4 {
return Array.isArray(a) && a.length === 16;
}
export function hasNaN(m: Mat4) {
for (let i = 0; i < 16; i++) if (Number.isNaN(m[i])) return true;
return false;

View File

@@ -71,6 +71,10 @@ namespace Quat {
return out;
}
export function is(a: any): a is Quat {
return Array.isArray(a) && a.length === 4;
}
export function setAxisAngle(out: Quat, axis: Vec3, rad: number) {
rad = rad * 0.5;
const s = Math.sin(rad);

View File

@@ -58,6 +58,10 @@ namespace Vec2 {
return Number.isNaN(a[0]) || Number.isNaN(a[1]);
}
export function is(a: any): a is Vec2 {
return Array.isArray(a) && a.length === 2;
}
export function toArray<T extends NumberArray>(a: Vec2, out: T, offset: number) {
out[offset + 0] = a[0];
out[offset + 1] = a[1];

View File

@@ -48,6 +48,10 @@ export namespace Vec3 {
return out;
}
export function is(a: any): a is Vec3 {
return Array.isArray(a) && a.length === 3;
}
export function isFinite(a: Vec3): boolean {
return _isFinite(a[0]) && _isFinite(a[1]) && _isFinite(a[2]);
}

View File

@@ -71,6 +71,10 @@ namespace Vec4 {
return Number.isNaN(a[0]) || Number.isNaN(a[1]) || Number.isNaN(a[2]) || Number.isNaN(a[3]);
}
export function is(a: any): a is Vec4 {
return Array.isArray(a) && a.length === 4;
}
export function toArray<T extends NumberArray>(a: Vec4, out: T, offset: number) {
out[offset + 0] = a[0];
out[offset + 1] = a[1];

View File

@@ -35,12 +35,14 @@ export const DefaultCameraFocusOptions = {
}
};
export type CameraFocusOptions = typeof DefaultCameraFocusOptions
export type CameraFocusLociOptions = CameraFocusOptions & {
optimizeDirection?: boolean,
optimizeDirectionUp?: Vec3,
}
export const DefaultCameraFocusLociOptions = {
...DefaultCameraFocusOptions,
optimizeDirection: false,
optimizeDirectionUp: 'current' as 'current' | 'default' | Vec3,
};
export type CameraFocusOptions = typeof DefaultCameraFocusOptions;
export type CameraFocusLociOptions = typeof DefaultCameraFocusLociOptions;
export class CameraManager {
private boundaryHelper = new BoundaryHelper('98');
@@ -97,7 +99,7 @@ export class CameraManager {
return sphere;
}
private focusLociOptimized(loci: Loci | Loci[], options?: Partial<CameraFocusOptions & { up?: Vec3 }>) {
private focusLociOptimized(loci: Loci | Loci[], options?: Partial<CameraFocusLociOptions>) {
const { canvas3d } = this.plugin;
if (!canvas3d) return;
@@ -144,7 +146,13 @@ export class CameraManager {
}
Vec3.negate(direction, direction);
return canvas3d.camera.getInvariantFocus(sphere.center, radius, options?.up ?? Vec3.unitY, direction);
const upVector = options?.optimizeDirectionUp === 'default'
? Vec3.unitY
: Vec3.is(options?.optimizeDirectionUp) ? options.optimizeDirectionUp : undefined;
if (upVector) {
return canvas3d.camera.getInvariantFocus(sphere.center, radius, upVector as Vec3, direction);
}
return canvas3d.camera.getFocus(sphere.center, radius, undefined, direction);
}
private focusLociBase(loci: Loci | Loci[], options?: Partial<CameraFocusOptions>) {
@@ -157,17 +165,15 @@ export class CameraManager {
focusLoci(loci: Loci | Loci[], options?: Partial<CameraFocusLociOptions>) {
if (!this.plugin.canvas3d) return;
const options_ = { ...DefaultCameraFocusLociOptions, ...options };
let snapshot: Partial<Camera.Snapshot> | undefined;
if (options?.optimizeDirection) {
snapshot = this.focusLociOptimized(loci, {
...options,
up: options.optimizeDirectionUp,
});
if (options_.optimizeDirection) {
snapshot = this.focusLociOptimized(loci, options_);
} else {
snapshot = this.focusLociBase(loci, options);
snapshot = this.focusLociBase(loci, options_);
}
this.focusSnapshot(snapshot, options);
this.focusSnapshot(snapshot, options_);
}
focusSpheres<T>(xs: ReadonlyArray<T>, sphere: (t: T) => Sphere3D | undefined, options?: Partial<CameraFocusOptions> & { principalAxes?: PrincipalAxes, positionToFlip?: Vec3 }) {
@@ -229,9 +235,9 @@ export class CameraManager {
this.plugin.canvas3d.requestCameraReset({
snapshot,
durationMs: df * durationMs,
easing: 'cubic-out',
keyframes: t > 0.05 ? [
{ t, snapshot: zoomOut, easing: 'cubic-in' },
{ t, snapshot: zoomOut, easing: 'cubic-out' },
{ t: 1, snapshot, easing: 'cubic-in' },
] : undefined
});
}