import { GraphEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { BasePluginOptions } from './base-plugin';
import { BasePlugin } from './base-plugin';
export interface CameraSettingOptions extends BasePluginOptions {
/**
* 投影模式,透视投影仅在 3D 场景下有效
* - `'perspective'` : 透视投影
* - `'orthographic'` : 正交投影
*
* Projection mode, perspective projection is only valid in 3D scenes
* - `'perspective'` : perspective projection
* - `'orthographic'` : Orthogonal projection
*/
projectionMode?: 'perspective' | 'orthographic';
/**
* 相机类型
* - `'orbiting'`: 固定视点,改变相机位置
* - `'exploring'`: 类似 orbiting,但允许相机在北极和南极之间旋转
* - `'tracking'`: 固定相机位置,改变视点
*
* Camera type
* - `'orbiting'`: Fixed viewpoint, change camera position
* - `'exploring'`: Similar to orbiting, but allows the camera to rotate between the North Pole and the South Pole
* - `'tracking'`: Fixed camera position, change viewpoint
*/
cameraType?: 'orbiting' | 'exploring' | 'tracking';
/**
* 近平面位置
*
* The position of the near plane
*/
near?: number;
/**
* 远平面位置
*
* The position of the far plane
*/
far?: number;
/**
* 相机视角,仅在透视相机下有效
*
* Camera field of view, only valid in perspective camera
*/
fov?: number;
/**
* 相机视口宽高比,仅在透视相机下有效
* - number : 具体的宽高比
* - `'auto'` : 自动设置为画布的宽高比
*
* Camera viewport aspect ratio, only valid in perspective camera.
* - number : Specific aspect ratio
* - `'auto'` : Automatically set to the aspect ratio of the canvas
*/
aspect?: number | 'auto';
/**
* 相机距离目标的距离
*
* The distance from the camera to the target
* @defaultValue 500
*/
distance?: number;
/**
* 最小视距
*
* Minimum distance
*/
minDistance?: number;
/**
* 最大视距
*
* Maximum distance
*/
maxDistance?: number;
/**
* 滚转角
*
* Roll
*/
roll?: number;
/**
* 仰角
*
* Elevation
*/
elevation?: number;
/**
* 方位角
*
* Azimuth
*/
azimuth?: number;
}
/**
* 配置相机参数
*
* Configure camera parameters
*/
export class CameraSetting extends BasePlugin {
constructor(context: RuntimeContext, options: CameraSettingOptions) {
super(context, options);
this.bindEvents();
}
/**
* 更新相机参数
*
* Update camera parameters
* @param options - 相机配置项 | Camera configuration options
* @internal
*/
public update(options: Partial): void {
this.setOptions(options);
super.update(options);
}
private bindEvents() {
this.context.graph.once(GraphEvent.BEFORE_DRAW, () => this.setOptions(this.options));
}
private setOptions = (options: Partial) => {
const caller = {
cameraType: 'setType',
near: 'setNear',
far: 'setFar',
fov: 'setFov',
aspect: 'setAspect',
// 确保 projectionMode 在 near/far/fov/aspect 之后设置
// Ensure that projectionMode is set after near/far/fov/aspect
projectionMode: 'setProjectionMode',
distance: 'setDistance',
minDistance: 'setMinDistance',
maxDistance: 'setMaxDistance',
roll: 'setRoll',
elevation: 'setElevation',
azimuth: 'setAzimuth',
} as const;
const valueMapper = (key: string, value: string) => {
switch (key) {
case 'projectionMode':
return value === 'perspective' ? 1 : 0;
case 'cameraType':
return { orbiting: 0, exploring: 1, tracking: 2 }[value]!;
case 'aspect':
if (typeof value === 'number') return value;
return this.getCanvasAspect();
default:
return value;
}
};
Object.entries(caller).forEach(([key, method]) => {
const value = options[key];
if (value !== undefined) {
const actualValue = valueMapper(key, value);
// @ts-expect-error incorrect ts type check
this.context.canvas.getCamera()[method](actualValue);
}
});
};
private getCanvasAspect() {
const [width, height] = this.context.viewport!.getCanvasSize();
return width / height;
}
}