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; } }