import type { Group, Object3D } from 'three'; export type Vec3 = readonly [number, number, number]; export type Axis = 'x' | 'y' | 'z'; export type CameraPresetId = 'front' | 'back' | 'left' | 'right' | 'top' | 'isometric'; export type MachineDifficulty = 'Beginner' | 'Intermediate' | 'Advanced'; export type MachineCategory = | 'Engines' | 'Gearboxes & Drives' | 'Pumps & Fluid Systems' | 'Mechanisms' | 'Structural / Other' | string; export type MachineAssetKind = 'procedural' | 'gltf' | 'hybrid'; export type ViewerLoadingPhase = 'idle' | 'loading' | 'preparing' | 'ready' | 'error'; export interface EngineeringFact { label: string; value: string; detail?: string; } export interface ViewerPart { id: string; name: string; shortName?: string; description: string; material?: string; color?: string; engineeringFacts?: EngineeringFact[]; specs?: Record; explodeDirection?: Vec3; annotationPosition?: Vec3; annotationLabel?: string; defaultVisible?: boolean; defaultOpacity?: number; nodeNames?: string[]; keywords?: string[]; } export interface ViewerAnnotation { id: string; partId: string; label: string; position: Vec3; offset?: Vec3; } export interface ViewerPartRuntimeSettings { visible: boolean; opacity: number; } export interface MachineMetadata { id: string; slug?: string; title: string; subtitle?: string; category?: MachineCategory; difficulty?: MachineDifficulty; description: string; keywords?: string[]; facts?: EngineeringFact[]; parts: ViewerPart[]; relatedMachineIds?: string[]; approximateBounds?: ViewerSceneBounds; } export interface ViewerSceneBounds { center: Vec3; radius: number; min?: Vec3; max?: Vec3; } export interface LoadedMachineAsset { root: Group; metadata: MachineMetadata; parts: ViewerPart[]; source: { kind: MachineAssetKind; url?: string; generatedAt?: string; }; dispose?: () => void; } export interface MachineAssetDefinition { id: string; kind: MachineAssetKind; metadata: MachineMetadata; url?: string; dracoPath?: string | boolean; ktx2TranscoderPath?: string; meshopt?: boolean; preload?: boolean; create?: () => LoadedMachineAsset; partNameMap?: Record; } export interface CameraSnapshot { position: Vec3; target: Vec3; up?: Vec3; zoom?: number; fov?: number; } export interface ExplodedViewState { enabled: boolean; distance: number; } export interface CrossSectionState { enabled: boolean; axis: Axis; offset: number; invert: boolean; showPlane: boolean; } export interface ViewerDisplaySettings { wireframe: boolean; annotations: boolean; shadows: boolean; grid: boolean; ambientOcclusion: boolean; environment: boolean; exposure: number; exploded: ExplodedViewState; crossSection: CrossSectionState; } export interface ViewerHoveredPart { partId: string; name: string; clientX: number; clientY: number; worldPosition?: Vec3; } export interface ViewerLoadingState { phase: ViewerLoadingPhase; progress: number; label?: string; error?: string; } export type ViewerCameraRequest = | { id: number; type: 'preset'; preset: CameraPresetId; } | { id: number; type: 'reset'; } | { id: number; type: 'snapshot'; snapshot: CameraSnapshot; }; export interface ViewerUrlSnapshot { machineId?: string; camera?: CameraSnapshot; cameraPreset?: CameraPresetId; selectedPartId?: string; exploded?: Partial; wireframe?: boolean; annotations?: boolean; crossSection?: Partial; hiddenPartIds?: string[]; partOpacity?: Record; } export interface PartInteractionPayload { partId: string; object: Object3D; point?: Vec3; nativeEvent?: PointerEvent; } export interface AssetFactoryContext { machineId: string; reducedMotion?: boolean; }