@giszhc/three-model-viewer 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["THREE","CSS2DRenderer","OrbitControls","TrackballControls","TWEEN","JSZip","THREE","computeBoundsTree","disposeBoundsTree","acceleratedRaycast","MTLLoader","OBJLoader","THREE","THREE","THREE","CSS2DObject","TWEEN","THREE","THREE","THREE","CSS2DObject","THREE","CSS2DObject","THREE","THREE","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","three","three_addons_controls_OrbitControls_js","three_addons_controls_TrackballControls_js","three_addons_renderers_CSS2DRenderer_js","_tweenjs_tween_js","three_addons_loaders_OBJLoader_js","three_addons_loaders_MTLLoader_js","three_mesh_bvh","jszip","SceneManager","options","width","height","orbit","tb","hemiLight","ambientLight","dirLight1","dirLight2","headlight","animate","alpha","color","EventEmitter","event","listener","listeners","index","args","err","ZipExtractor","arrayBuffer","onProgress","source","response","zip","blobUrls","objZipEntry","mtlZipEntry","imageExtensions","files","totalFiles","filename","entry","lower","currentPercent","ext","baseName","blob","objectUrl","url","objContent","objFileName","mtlContent","mtlFileName","loadingManager","cleanUrl","blobUrl","ModelLoader","zipContent","materialsCreator","objLoader","text","modelGroup","result","mtlUrl","objUrl","xhr","p","vertexCount","faceCount","enhanceTexture","tex","ensureVisibleMaterial","mat","m","initialBox","group","origCenter","origSize","child","mesh","geom","plainMat","managedItem","curMat","item","setMatProps","style","showTexture","maxWidth","maxHeight","inspectMat","img","w","h","planes","applyToMat","computeMeshVolume","geometry","position","totalVolume","p1","p2","p3","matrixWorld","indices","i1","i2","i3","signedVolumeOfTriangle","computeGroupVolume","meshVolume","root","bbox","size","bboxVolume","compute3DPolygonArea","points","normal","current","next","formatUnit","value","unit","decimals","BoundingBoxTool","scene","model","box","min","max","edgeIndices","boxGeom","posBuffer","lineMaterial","divX","divY","divZ","line","matrix","posAttr","ptr","idx","v","bestCorner","minDistance","camPos","camera","dist","cornerV","neighborX","neighborY","neighborZ","pos","visible","CameraTool","controls","tweenGroup","enabled","axis","direction","speed","offset","radius","dirSign","angle","rotAxis","currentOffset","curRadius","hRadius","targetY","angleXZ","curY","targetObject","sphere","center","fovRad","fitDistance","dir","up","view","targetCamPos","duration","resolve","startPos","startTarget","animData","object","currentDir","findSnapPoint","modelMeshes","raycaster","mouse","intersects","hit","hitPoint","faceNormal","closestVertex","minScreenDist","snapPixelThreshold","vIndices","vWorld","vScreen","screenX","viewportWidth","screenY","viewportHeight","mouseScreenX","mouseScreenY","screenDist","ClippingTool","container","modelLoader","emitter","interactive","sx","sy","sz","meshMat","baseColor","edges","borderMat","borderColor","border","arrowGroup","handleScale","arrowMat","sphereGeom","centerSphere","shaftGeom","shaftMesh","coneGeom1","coneMesh1","coneGeom2","coneMesh2","hitMeshes","div","label","widget","a","defaultW","newState","reverse","range","progress","isEmpty","minX","maxX","minY","maxY","minZ","maxZ","calcProg","wx","wy","wz","axes","activeW","curAxisProg","curAxisPos","curAxisInv","rect","candidateWidgets","closestHit","activeWidget","axisVector","p0","ndc0","screen0","ndc1","screen1","deltaMouse","deltaUnits","axisSpan","newPos","hoveredHitAxis","inverted","snap","point","markerGeom","markerMat","marker","cutLine","viewDir","vFovRad","tanHalfFov","obj","radiusPx","worldPos","worldRadius","activePlanes","MeasurementTool","snapGeom","snapMat","mode","clickedPoint","currentPoint","distance","midPoint","verticalHeight","horizontalDistance","totalDistance","directGeom","directMat","directLine","corner","hGeom","hMat","hLine","vGeom","vMat","vLine","vMid","area","loopPoints","lineGeom","lineMat","centroid","vertex","v1","v2","dot","angleRad","angleDeg","geom1","geom2","arcRadius","arcPoints","t","arcVec","arcGeom","arcMat","midDir","labelPos","pixelRadius","lastMarker","updateMesh","el","ScreenshotTool","renderer","getModel","checkMat","isOriginal","scale","type","quality","download","originalSize","originalPixelRatio","originalBackground","aspect","targetWidth","targetHeight","texSize","maxDim","baseDim","modelOnly","hiddenObjects","currentModel","isKeep","c","dataUrl","link","wasVisible","origH","Viewer","percent","message","error","baseGeom","baseMat","baseMesh","cylinderGeom","cylinderMat","cylMesh","ringGeom","ringMat","ringMesh","bldgGeom","bldgMat","bldg","roofGeom","roofMat","roof","torusGeom","torusMat","postProcessed","state"],"sources":["../src/core/SceneManager.ts","../src/core/EventEmitter.ts","../src/loaders/ZipExtractor.ts","../src/loaders/ModelLoader.ts","../src/utils/math.ts","../src/tools/BoundingBoxTool.ts","../src/tools/CameraTool.ts","../src/utils/snapping.ts","../src/tools/ClippingTool.ts","../src/tools/MeasurementTool.ts","../src/tools/ScreenshotTool.ts","../src/core/Viewer.ts"],"sourcesContent":["import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';\nimport * as TWEEN from '@tweenjs/tween.js';\nimport { ViewerOptions } from '../types';\n\nexport class SceneManager {\n public container: HTMLElement;\n public scene: THREE.Scene;\n public camera: THREE.PerspectiveCamera;\n public renderer: THREE.WebGLRenderer;\n public labelRenderer: CSS2DRenderer;\n public controls: TrackballControls | OrbitControls;\n public lightsGroup: THREE.Group;\n public gridHelper: THREE.GridHelper;\n public tweenGroup: TWEEN.Group;\n public onBeforeRender?: () => void;\n\n private animationFrameId: number | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private isDestroyed: boolean = false;\n\n constructor(options: ViewerOptions) {\n this.container = options.container;\n\n // 1. Scene\n this.scene = new THREE.Scene();\n if (options.background !== undefined && options.background !== null) {\n if (typeof options.background === 'string' || typeof options.background === 'number') {\n this.scene.background = new THREE.Color(options.background);\n }\n } else {\n this.scene.background = new THREE.Color(0x181c24); // Modern dark slate\n }\n\n // 2. Camera\n const width = this.container.clientWidth || 800;\n const height = this.container.clientHeight || 600;\n this.camera = new THREE.PerspectiveCamera(\n options.cameraFov ?? 45,\n width / height,\n options.cameraNear ?? 0.1,\n options.cameraFar ?? 5000\n );\n this.camera.position.set(10, 10, 10);\n\n // 3. WebGLRenderer\n this.renderer = new THREE.WebGLRenderer({\n antialias: options.antialias ?? true,\n alpha: options.alpha ?? false,\n preserveDrawingBuffer: true,\n powerPreference: 'high-performance',\n });\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n this.renderer.localClippingEnabled = true; // Crucial for slicing tool\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping;\n this.renderer.toneMappingExposure = 1.0;\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.domElement.style.display = 'block';\n this.renderer.domElement.style.width = '100%';\n this.renderer.domElement.style.height = '100%';\n this.container.appendChild(this.renderer.domElement);\n\n // 4. CSS2D Label Renderer (for measurements & dimensions)\n this.labelRenderer = new CSS2DRenderer();\n this.labelRenderer.setSize(width, height);\n this.labelRenderer.domElement.style.position = 'absolute';\n this.labelRenderer.domElement.style.top = '0';\n this.labelRenderer.domElement.style.left = '0';\n this.labelRenderer.domElement.style.pointerEvents = 'none';\n this.labelRenderer.domElement.style.width = '100%';\n this.labelRenderer.domElement.style.height = '100%';\n this.labelRenderer.domElement.className = 'gmv-css2d-container';\n this.container.appendChild(this.labelRenderer.domElement);\n\n // 5. Controls: TrackballControls gives infinite unconstrained 360° rotation without pole limits\n const controlsType = options.controlsType ?? 'trackball';\n if (controlsType === 'orbit') {\n const orbit = new OrbitControls(this.camera, this.renderer.domElement);\n orbit.enableDamping = true;\n orbit.dampingFactor = 0.05;\n orbit.screenSpacePanning = true;\n this.controls = orbit;\n } else {\n const tb = new TrackballControls(this.camera, this.renderer.domElement);\n tb.rotateSpeed = 2.5;\n tb.zoomSpeed = 1.2;\n tb.panSpeed = 0.8;\n tb.staticMoving = false;\n tb.dynamicDampingFactor = 0.15;\n this.controls = tb;\n }\n\n // 6. Tween Group\n this.tweenGroup = new TWEEN.Group();\n\n // 7. Lighting System\n this.lightsGroup = new THREE.Group();\n this.lightsGroup.name = 'gmv-lights-group';\n this.initLights();\n this.scene.add(this.lightsGroup);\n\n // 8. Grid Helper\n this.gridHelper = new THREE.GridHelper(50, 50, 0x4a5568, 0x2d3748);\n this.gridHelper.name = 'gmv-grid-helper';\n this.gridHelper.position.y = 0;\n this.gridHelper.visible = false;\n this.scene.add(this.gridHelper);\n\n // 9. Event listeners & Resize\n this.initResizeObserver();\n this.startAnimationLoop();\n\n // Ensure immediate crisp sizing once DOM layout settles\n requestAnimationFrame(() => this.handleResize());\n setTimeout(() => this.handleResize(), 50);\n }\n\n private initLights(): void {\n // 1. Hemisphere Light & Ambient Light for all-around illumination\n const hemiLight = new THREE.HemisphereLight(0xffffff, 0x475569, 1.0);\n hemiLight.position.set(0, 50, 0);\n this.lightsGroup.add(hemiLight);\n\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);\n this.lightsGroup.add(ambientLight);\n\n // 2. Multi-angle Key/Fill lights\n const dirLight1 = new THREE.DirectionalLight(0xffffff, 1.2);\n dirLight1.position.set(50, 100, 50);\n this.lightsGroup.add(dirLight1);\n\n const dirLight2 = new THREE.DirectionalLight(0xffffff, 0.7);\n dirLight2.position.set(-50, 50, -50);\n this.lightsGroup.add(dirLight2);\n\n // 3. Headlight attached to camera (always illuminates the view from camera!)\n const headlight = new THREE.DirectionalLight(0xffffff, 0.9);\n headlight.position.set(0, 0, 1);\n this.camera.add(headlight);\n this.scene.add(this.camera);\n }\n\n private initResizeObserver(): void {\n this.resizeObserver = new ResizeObserver(() => {\n this.handleResize();\n });\n this.resizeObserver.observe(this.container);\n }\n\n public handleResize(): void {\n if (this.isDestroyed || !this.container) return;\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n\n if (width === 0 || height === 0) return;\n\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n\n this.renderer.setSize(width, height);\n this.labelRenderer.setSize(width, height);\n\n if ('handleResize' in this.controls && typeof (this.controls as unknown as { handleResize: () => void }).handleResize === 'function') {\n (this.controls as unknown as { handleResize: () => void }).handleResize();\n }\n }\n\n private startAnimationLoop(): void {\n const animate = () => {\n if (this.isDestroyed) return;\n\n this.animationFrameId = requestAnimationFrame(animate);\n\n // Update Tween animations\n this.tweenGroup.update();\n\n // Update Controls\n this.controls.update();\n\n // Hook before render (e.g. dynamic bounding box edge selection)\n if (this.onBeforeRender) {\n this.onBeforeRender();\n }\n\n // Render 3D and 2D Label Scenes\n this.renderer.render(this.scene, this.camera);\n this.labelRenderer.render(this.scene, this.camera);\n };\n\n this.animationFrameId = requestAnimationFrame(animate);\n }\n\n public setBackground(color: string | number | null, alpha?: boolean): void {\n if (alpha) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else if (color === null || color === undefined) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else {\n this.scene.background = new THREE.Color(color);\n this.renderer.setClearAlpha(1);\n }\n }\n\n public destroy(): void {\n this.isDestroyed = true;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n }\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n this.controls.dispose();\n this.renderer.dispose();\n\n if (this.renderer.domElement.parentElement) {\n this.renderer.domElement.parentElement.removeChild(this.renderer.domElement);\n }\n if (this.labelRenderer.domElement.parentElement) {\n this.labelRenderer.domElement.parentElement.removeChild(this.labelRenderer.domElement);\n }\n }\n}\n","export type Listener<T = unknown> = (...args: T[]) => void;\n\nexport class EventEmitter {\n private events: Map<string, Listener[]>;\n\n constructor() {\n this.events = new Map();\n }\n\n public on(event: string, listener: Listener): this {\n if (!this.events.has(event)) {\n this.events.set(event, []);\n }\n this.events.get(event)!.push(listener);\n return this;\n }\n\n public off(event: string, listener: Listener): this {\n const listeners = this.events.get(event);\n if (!listeners) return this;\n\n const index = listeners.indexOf(listener);\n if (index !== -1) {\n listeners.splice(index, 1);\n }\n return this;\n }\n\n public emit(event: string, ...args: unknown[]): boolean {\n const listeners = this.events.get(event);\n if (!listeners || listeners.length === 0) return false;\n\n // Clone array in case listener removes itself\n [...listeners].forEach((listener) => {\n try {\n listener(...args);\n } catch (err) {\n console.error(`Error in event listener for \"${event}\":`, err);\n }\n });\n\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.events.delete(event);\n } else {\n this.events.clear();\n }\n return this;\n }\n}\n","import JSZip from 'jszip';\nimport * as THREE from 'three';\n\nexport interface ExtractedZipContent {\n objContent: string | ArrayBuffer;\n objFileName: string;\n mtlContent: string | null;\n mtlFileName: string | null;\n blobUrls: Map<string, string>;\n loadingManager: THREE.LoadingManager;\n dispose: () => void;\n}\n\nexport type ProgressCallback = (percent: number, message: string) => void;\n\nexport class ZipExtractor {\n public static async extract(\n source: File | Blob | ArrayBuffer | string,\n onProgress?: ProgressCallback\n ): Promise<ExtractedZipContent> {\n let arrayBuffer: ArrayBuffer;\n\n onProgress?.(5, '正在读取文件字节流...');\n\n if (typeof source === 'string') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch zip file: ${response.statusText}`);\n }\n arrayBuffer = await response.arrayBuffer();\n } else if (source instanceof Blob || source instanceof File) {\n arrayBuffer = await source.arrayBuffer();\n } else {\n arrayBuffer = source;\n }\n\n onProgress?.(15, '正在解压 ZIP 文件包...');\n const zip = await JSZip.loadAsync(arrayBuffer);\n const blobUrls = new Map<string, string>();\n\n let objZipEntry: JSZip.JSZipObject | null = null;\n let mtlZipEntry: JSZip.JSZipObject | null = null;\n\n const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.bmp', '.tga', '.dds'];\n\n // 1. Identify files in ZIP\n const files = Object.keys(zip.files);\n const totalFiles = files.length || 1;\n\n for (let i = 0; i < files.length; i++) {\n const filename = files[i];\n const entry = zip.files[filename];\n if (entry.dir) continue;\n\n // Filter out Mac / system hidden files\n if (filename.includes('__MACOSX') || filename.split('/').pop()?.startsWith('._')) {\n continue;\n }\n\n const lower = filename.toLowerCase();\n const currentPercent = Math.round(20 + (i / totalFiles) * 35); // 20% -> 55%\n\n // Find OBJ file (prefer top-level or first found)\n if (lower.endsWith('.obj') && !objZipEntry) {\n objZipEntry = entry;\n onProgress?.(currentPercent, `找到模型文件: ${filename.split('/').pop()}`);\n } else if (lower.endsWith('.mtl') && !mtlZipEntry) {\n mtlZipEntry = entry;\n onProgress?.(currentPercent, `找到材质定义: ${filename.split('/').pop()}`);\n }\n\n // Check for image textures\n const isImage = imageExtensions.some((ext) => lower.endsWith(ext));\n if (isImage) {\n const baseName = filename.split('/').pop() || filename;\n onProgress?.(currentPercent, `加载贴图: ${baseName}`);\n const blob = await entry.async('blob');\n const objectUrl = URL.createObjectURL(blob);\n // Store full path, simple filename, and lowercase versions for robust lookup\n blobUrls.set(filename, objectUrl);\n blobUrls.set(baseName, objectUrl);\n blobUrls.set(baseName.toLowerCase(), objectUrl);\n }\n }\n\n onProgress?.(60, '提取完成,准备解析三维几何网格...');\n\n if (!objZipEntry) {\n // Clean up created URLs if no OBJ found\n blobUrls.forEach((url) => URL.revokeObjectURL(url));\n throw new Error('No .obj file found in the zip archive.');\n }\n\n const objContent = await objZipEntry.async('string');\n const objFileName = objZipEntry.name;\n\n let mtlContent: string | null = null;\n let mtlFileName: string | null = null;\n\n if (mtlZipEntry) {\n mtlContent = await mtlZipEntry.async('string');\n mtlFileName = mtlZipEntry.name;\n }\n\n // 2. Setup Three.js LoadingManager to intercept and resolve texture URLs\n const loadingManager = new THREE.LoadingManager();\n loadingManager.setURLModifier((url: string) => {\n // Decode URL if encoded\n const cleanUrl = decodeURIComponent(url);\n const baseName = cleanUrl.split('/').pop()?.split('\\\\').pop() || cleanUrl;\n\n if (blobUrls.has(cleanUrl)) {\n return blobUrls.get(cleanUrl)!;\n }\n if (blobUrls.has(baseName)) {\n return blobUrls.get(baseName)!;\n }\n\n // Case-insensitive fallback\n for (const [key, blobUrl] of blobUrls.entries()) {\n if (key.toLowerCase() === baseName.toLowerCase()) {\n return blobUrl;\n }\n }\n\n return url;\n });\n\n const dispose = () => {\n blobUrls.forEach((url) => {\n try {\n URL.revokeObjectURL(url);\n } catch {\n // ignore\n }\n });\n blobUrls.clear();\n };\n\n return {\n objContent,\n objFileName,\n mtlContent,\n mtlFileName,\n blobUrls,\n loadingManager,\n dispose,\n };\n }\n}\n","import * as THREE from 'three';\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js';\nimport { MTLLoader } from 'three/addons/loaders/MTLLoader.js';\nimport { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';\nimport { ZipExtractor, ExtractedZipContent } from './ZipExtractor';\nimport { ModelLoadResult, ModelDisplayStyle } from '../types';\n\n// Setup three-mesh-bvh acceleration\n(THREE.BufferGeometry.prototype as unknown as { computeBoundsTree: typeof computeBoundsTree }).computeBoundsTree = computeBoundsTree;\n(THREE.BufferGeometry.prototype as unknown as { disposeBoundsTree: typeof disposeBoundsTree }).disposeBoundsTree = disposeBoundsTree;\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\n\nexport interface ManagedMeshMaterial {\n mesh: THREE.Mesh;\n originalMaterial: THREE.Material | THREE.Material[];\n plainMaterial: THREE.Material;\n}\n\nexport class ModelLoader {\n private currentZipContent: ExtractedZipContent | null = null;\n private managedMaterials: ManagedMeshMaterial[] = [];\n private isTextureEnabled: boolean = true;\n private displayStyle: ModelDisplayStyle = 'solid';\n private plainColor: number = 0x4c8cd6; // Digital3DCloud / 先临三维标准数模扫描蓝\n private currentClippingPlanes: THREE.Plane[] = [];\n\n /**\n * Load model from a ZIP file containing .obj, .mtl, and image textures\n */\n public async loadZip(\n source: File | Blob | ArrayBuffer | string,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n this.cleanup();\n\n const zipContent = await ZipExtractor.extract(source, onProgress);\n this.currentZipContent = zipContent;\n\n const { objContent, mtlContent, loadingManager } = zipContent;\n\n onProgress?.(65, '正在解析材质与贴图数据...');\n let materialsCreator: MTLLoader.MaterialCreator | null = null;\n if (mtlContent) {\n const mtlLoader = new MTLLoader(loadingManager);\n materialsCreator = mtlLoader.parse(mtlContent, '');\n materialsCreator.preload();\n }\n\n onProgress?.(75, '正在解析 OBJ 网格几何数据...');\n const objLoader = new OBJLoader(loadingManager);\n if (materialsCreator) {\n objLoader.setMaterials(materialsCreator);\n }\n\n const text = typeof objContent === 'string' ? objContent : new TextDecoder().decode(objContent);\n const modelGroup = objLoader.parse(text);\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(modelGroup);\n\n onProgress?.(100, '加载完成');\n return result;\n }\n\n /**\n * Load model directly from OBJ and optional MTL URLs\n */\n public async loadOBJ(\n objUrl: string,\n mtlUrl?: string,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n this.cleanup();\n\n onProgress?.(20, '正在加载材质定义...');\n let materialsCreator: MTLLoader.MaterialCreator | null = null;\n if (mtlUrl) {\n const mtlLoader = new MTLLoader();\n materialsCreator = await mtlLoader.loadAsync(mtlUrl);\n materialsCreator.preload();\n }\n\n onProgress?.(50, '正在加载 OBJ 模型几何数据...');\n const objLoader = new OBJLoader();\n if (materialsCreator) {\n objLoader.setMaterials(materialsCreator);\n }\n\n const modelGroup = await objLoader.loadAsync(objUrl, (xhr) => {\n if (xhr.lengthComputable && xhr.total > 0) {\n const p = Math.round(50 + (xhr.loaded / xhr.total) * 35);\n onProgress?.(p, `正在传输模型数据 (${Math.round((xhr.loaded / xhr.total) * 100)}%)...`);\n }\n });\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(modelGroup);\n\n onProgress?.(100, '加载完成');\n return result;\n }\n\n /**\n * Post-process loaded model:\n * - Build BVH acceleration trees on all meshes\n * - Count vertices & faces\n * - Cache original materials and prepare clay/white-model materials\n * - Calculate bounding box and center\n */\n public postProcessModel(group: THREE.Group): ModelLoadResult {\n let vertexCount = 0;\n let faceCount = 0;\n this.managedMaterials = [];\n\n const enhanceTexture = (tex: THREE.Texture | null) => {\n if (tex) {\n tex.anisotropy = 16;\n tex.generateMipmaps = true;\n tex.minFilter = THREE.LinearMipmapLinearFilter;\n tex.magFilter = THREE.LinearFilter;\n tex.colorSpace = THREE.SRGBColorSpace;\n tex.needsUpdate = true;\n }\n };\n\n // Helper to ensure materials are visible, sharp and two-sided\n const ensureVisibleMaterial = (mat: THREE.Material) => {\n mat.side = THREE.DoubleSide; // Critical: avoid invisible faces due to inverted normals\n mat.needsUpdate = true;\n if ('color' in mat) {\n const m = mat as THREE.MeshStandardMaterial;\n enhanceTexture(m.map);\n if ('normalMap' in m) enhanceTexture(m.normalMap);\n if ('bumpMap' in m) enhanceTexture(m.bumpMap);\n\n // If diffuse color is black or very dark without texture, brighten it\n if (!m.map && m.color && m.color.getHex() < 0x222222) {\n m.color.setHex(0xb0b8c4);\n }\n }\n };\n\n // 1. Initial pass: calculate original bounds and center\n const initialBox = new THREE.Box3().setFromObject(group);\n const origCenter = new THREE.Vector3();\n const origSize = new THREE.Vector3();\n initialBox.getCenter(origCenter);\n initialBox.getSize(origSize);\n\n // 2. Process meshes, center geometries to (0,0,0) and ensure visible materials\n group.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n\n if (mesh.material) {\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach(ensureVisibleMaterial);\n } else {\n ensureVisibleMaterial(mesh.material);\n }\n } else {\n mesh.material = new THREE.MeshStandardMaterial({\n color: this.plainColor,\n roughness: 0.45,\n metalness: 0.12,\n side: THREE.DoubleSide,\n });\n }\n\n const geom = mesh.geometry;\n if (geom) {\n // Center geometry around (0, 0, 0)\n geom.translate(-origCenter.x, -origCenter.y, -origCenter.z);\n\n // Ensure normals exist\n if (!geom.attributes.normal) {\n geom.computeVertexNormals();\n }\n\n // Build BVH for raycasting acceleration and snapping\n if (geom.computeBoundsTree) {\n geom.computeBoundsTree();\n }\n\n // Vertex & face counting\n if (geom.attributes.position) {\n vertexCount += geom.attributes.position.count;\n }\n if (geom.index) {\n faceCount += geom.index.count / 3;\n } else if (geom.attributes.position) {\n faceCount += geom.attributes.position.count / 3;\n }\n\n // Create Digital3DCloud standard CAD/scan blue material for texture toggling\n const plainMat = new THREE.MeshStandardMaterial({\n color: this.plainColor,\n roughness: 0.45,\n metalness: 0.12,\n side: THREE.DoubleSide,\n clippingPlanes: this.currentClippingPlanes.length > 0 ? this.currentClippingPlanes : null,\n clipShadows: true,\n });\n\n const managedItem: ManagedMeshMaterial = {\n mesh,\n originalMaterial: mesh.material,\n plainMaterial: plainMat,\n };\n\n this.applyStyleToItem(managedItem);\n this.managedMaterials.push(managedItem);\n }\n }\n });\n\n group.position.set(0, 0, 0);\n group.rotation.set(0, 0, 0);\n group.scale.set(1, 1, 1);\n group.updateMatrixWorld(true);\n\n // Compute updated bounding box (centered at 0,0,0)\n const box = new THREE.Box3().setFromObject(group);\n\n return {\n model: group,\n box,\n size: origSize,\n center: origCenter,\n vertexCount,\n faceCount: Math.round(faceCount),\n };\n }\n\n /**\n * Internal helper to apply display style (solid/wireframe) to a managed mesh\n */\n private applyStyleToItem(item: ManagedMeshMaterial): void {\n const curMat = this.isTextureEnabled ? item.originalMaterial : item.plainMaterial;\n item.mesh.material = curMat;\n\n const setMatProps = (mat: THREE.Material) => {\n mat.visible = true;\n if ('wireframe' in mat) {\n (mat as THREE.MeshStandardMaterial).wireframe = (this.displayStyle === 'wireframe');\n }\n mat.needsUpdate = true;\n };\n\n if (Array.isArray(curMat)) {\n curMat.forEach(setMatProps);\n } else {\n setMatProps(curMat);\n }\n }\n\n /**\n * Set model display style: 'solid' (实体), 'wireframe' (线框)\n */\n public setDisplayStyle(style: ModelDisplayStyle): void {\n this.displayStyle = style;\n for (const item of this.managedMaterials) {\n this.applyStyleToItem(item);\n }\n }\n\n public getDisplayStyle(): ModelDisplayStyle {\n return this.displayStyle;\n }\n\n /**\n * Toggle texture on/off\n */\n public toggleTexture(showTexture: boolean): void {\n this.isTextureEnabled = showTexture;\n for (const item of this.managedMaterials) {\n this.applyStyleToItem(item);\n }\n }\n\n public getTextureState(): boolean {\n return this.isTextureEnabled;\n }\n\n /**\n * Set color for non-texture display (defaults to Digital3DCloud standard CAD blue 0x4c8cd6)\n */\n public setPlainColor(color: THREE.ColorRepresentation): void {\n this.plainColor = typeof color === 'string' ? new THREE.Color(color).getHex() : Number(color);\n for (const item of this.managedMaterials) {\n if (item.plainMaterial && 'color' in item.plainMaterial) {\n (item.plainMaterial as THREE.MeshStandardMaterial).color.set(this.plainColor);\n item.plainMaterial.needsUpdate = true;\n }\n }\n }\n\n public getPlainColor(): number {\n return this.plainColor;\n }\n\n /**\n * Retrieves the maximum original texture resolution (width & height) from the model's materials\n */\n public getModelTextureSize(): { width: number; height: number } | null {\n let maxWidth = 0;\n let maxHeight = 0;\n\n const inspectMat = (mat: THREE.Material) => {\n if ('map' in mat && (mat as THREE.MeshStandardMaterial).map) {\n const tex = (mat as THREE.MeshStandardMaterial).map;\n if (tex) {\n const img = (tex.image || (tex.source as unknown as { data?: { width?: number; height?: number } })?.data) as\n | { width?: number; height?: number }\n | undefined;\n if (img) {\n const w = img.width || 0;\n const h = img.height || 0;\n if (w > 0 && h > 0 && w * h > maxWidth * maxHeight) {\n maxWidth = w;\n maxHeight = h;\n }\n }\n }\n }\n };\n\n for (const item of this.managedMaterials) {\n if (Array.isArray(item.originalMaterial)) {\n item.originalMaterial.forEach(inspectMat);\n } else if (item.originalMaterial) {\n inspectMat(item.originalMaterial);\n }\n }\n\n if (maxWidth > 0 && maxHeight > 0) {\n return { width: maxWidth, height: maxHeight };\n }\n return null;\n }\n\n /**\n * Retrieve all meshes of the model for raycasting and snapping\n */\n public getMeshes(): THREE.Mesh[] {\n return this.managedMaterials.map((m) => m.mesh);\n }\n\n /**\n * Apply clipping planes to all materials\n */\n public applyClippingPlanes(planes: THREE.Plane[]): void {\n this.currentClippingPlanes = planes;\n for (const item of this.managedMaterials) {\n const applyToMat = (mat: THREE.Material) => {\n mat.clippingPlanes = planes.length > 0 ? planes : null;\n mat.clipShadows = true;\n mat.needsUpdate = true;\n };\n\n if (Array.isArray(item.originalMaterial)) {\n item.originalMaterial.forEach(applyToMat);\n } else {\n applyToMat(item.originalMaterial);\n }\n applyToMat(item.plainMaterial);\n }\n }\n\n /**\n * Clean up resources\n */\n public cleanup(): void {\n if (this.currentZipContent) {\n this.currentZipContent.dispose();\n this.currentZipContent = null;\n }\n\n for (const item of this.managedMaterials) {\n if (item.plainMaterial) {\n item.plainMaterial.dispose();\n }\n if (item.mesh.geometry && item.mesh.geometry.disposeBoundsTree) {\n item.mesh.geometry.disposeBoundsTree();\n }\n }\n this.managedMaterials = [];\n }\n}\n","import * as THREE from 'three';\n\n/**\n * Calculates the exact signed volume of a 3D Mesh using divergence theorem.\n * Works accurately for closed (watertight) meshes.\n */\nexport function computeMeshVolume(mesh: THREE.Mesh): number {\n const geometry = mesh.geometry;\n if (!geometry) return 0;\n\n const position = geometry.attributes.position;\n if (!position) return 0;\n\n let totalVolume = 0;\n const p1 = new THREE.Vector3();\n const p2 = new THREE.Vector3();\n const p3 = new THREE.Vector3();\n\n mesh.updateMatrixWorld(true);\n const matrixWorld = mesh.matrixWorld;\n\n if (geometry.index) {\n const indices = geometry.index;\n for (let i = 0; i < indices.count; i += 3) {\n const i1 = indices.getX(i);\n const i2 = indices.getX(i + 1);\n const i3 = indices.getX(i + 2);\n\n p1.fromBufferAttribute(position, i1).applyMatrix4(matrixWorld);\n p2.fromBufferAttribute(position, i2).applyMatrix4(matrixWorld);\n p3.fromBufferAttribute(position, i3).applyMatrix4(matrixWorld);\n\n totalVolume += signedVolumeOfTriangle(p1, p2, p3);\n }\n } else {\n for (let i = 0; i < position.count; i += 3) {\n p1.fromBufferAttribute(position, i).applyMatrix4(matrixWorld);\n p2.fromBufferAttribute(position, i + 1).applyMatrix4(matrixWorld);\n p3.fromBufferAttribute(position, i + 2).applyMatrix4(matrixWorld);\n\n totalVolume += signedVolumeOfTriangle(p1, p2, p3);\n }\n }\n\n return Math.abs(totalVolume);\n}\n\n/**\n * Computes the total volume of all meshes inside a Group or Object3D\n */\nexport function computeGroupVolume(root: THREE.Object3D): { meshVolume: number; bboxVolume: number } {\n let meshVolume = 0;\n root.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n meshVolume += computeMeshVolume(child as THREE.Mesh);\n }\n });\n\n const bbox = new THREE.Box3().setFromObject(root);\n const size = new THREE.Vector3();\n bbox.getSize(size);\n const bboxVolume = size.x * size.y * size.z;\n\n return { meshVolume, bboxVolume };\n}\n\nfunction signedVolumeOfTriangle(p1: THREE.Vector3, p2: THREE.Vector3, p3: THREE.Vector3): number {\n return p1.dot(p2.clone().cross(p3)) / 6.0;\n}\n\n/**\n * Calculates the area of a 3D polygon defined by an array of 3D vertices using Newell's method.\n */\nexport function compute3DPolygonArea(points: THREE.Vector3[]): number {\n const n = points.length;\n if (n < 3) return 0;\n\n const normal = new THREE.Vector3(0, 0, 0);\n for (let i = 0; i < n; i++) {\n const current = points[i];\n const next = points[(i + 1) % n];\n\n normal.x += (current.y - next.y) * (current.z + next.z);\n normal.y += (current.z - next.z) * (current.x + next.x);\n normal.z += (current.x - next.x) * (current.y + next.y);\n }\n\n return normal.length() * 0.5;\n}\n\n/**\n * Formats numeric value with unit\n */\nexport function formatUnit(value: number, unit: string = 'm', decimals: number = 2): string {\n if (Math.abs(value) < 0.001) {\n return `0.00 ${unit}`;\n }\n return `${value.toFixed(decimals)} ${unit}`;\n}\n","import * as THREE from 'three';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { formatUnit } from '../utils/math';\nimport { BoundingBoxOptions } from '../types';\n\nexport class BoundingBoxTool {\n private scene: THREE.Scene;\n private boxGroup: THREE.Group;\n private wireframeBox: THREE.LineSegments | null = null;\n\n // 3 Dynamic active dimension lines\n private lineX: THREE.Line | null = null;\n private lineY: THREE.Line | null = null;\n private lineZ: THREE.Line | null = null;\n\n // 3 Dynamic CSS2D labels\n private labelX: CSS2DObject | null = null;\n private labelY: CSS2DObject | null = null;\n private labelZ: CSS2DObject | null = null;\n\n private currentModel: THREE.Object3D | null = null;\n private isVisible: boolean = false;\n private unit: string = 'm';\n\n // Cached local 8 corner vertices of the model's bounding box\n private localVertices: THREE.Vector3[] = [];\n private worldVertices: THREE.Vector3[] = [\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n ];\n\n // Cached model dimensions\n private modelSize: THREE.Vector3 = new THREE.Vector3();\n\n constructor(scene: THREE.Scene) {\n this.scene = scene;\n this.boxGroup = new THREE.Group();\n this.boxGroup.name = 'gmv-bounding-box-group';\n this.scene.add(this.boxGroup);\n }\n\n /**\n * Attaches bounding box to a model and initializes dynamic dimensional lines\n */\n public attachToModel(model: THREE.Object3D, options?: Partial<BoundingBoxOptions>): void {\n this.detach();\n this.currentModel = model;\n this.unit = options?.unit || 'm';\n\n // 1. Compute model's local bounding box\n model.updateMatrixWorld(true);\n const box = new THREE.Box3();\n\n // Traverse all meshes in model to compute local bounds\n model.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n if (mesh.geometry) {\n if (!mesh.geometry.boundingBox) {\n mesh.geometry.computeBoundingBox();\n }\n if (mesh.geometry.boundingBox) {\n box.union(mesh.geometry.boundingBox);\n }\n }\n }\n });\n\n if (box.isEmpty()) {\n box.setFromObject(model);\n }\n\n const min = box.min;\n const max = box.max;\n box.getSize(this.modelSize);\n\n // 2. Generate 8 local corners\n // Index encoding: x (bit 0), y (bit 1), z (bit 2)\n this.localVertices = [\n new THREE.Vector3(min.x, min.y, min.z), // 0: 000\n new THREE.Vector3(max.x, min.y, min.z), // 1: 001 (x)\n new THREE.Vector3(min.x, max.y, min.z), // 2: 010 (y)\n new THREE.Vector3(max.x, max.y, min.z), // 3: 011 (xy)\n new THREE.Vector3(min.x, min.y, max.z), // 4: 100 (z)\n new THREE.Vector3(max.x, min.y, max.z), // 5: 101 (xz)\n new THREE.Vector3(min.x, max.y, max.z), // 6: 110 (yz)\n new THREE.Vector3(max.x, max.y, max.z), // 7: 111 (xyz)\n ];\n\n // 3. Create full 12-edge wireframe box\n // 12 edges indices:\n // Along X: (0,1), (2,3), (4,5), (6,7)\n // Along Y: (0,2), (1,3), (4,6), (5,7)\n // Along Z: (0,4), (1,5), (2,6), (3,7)\n const edgeIndices = [\n 0, 1, 2, 3, 4, 5, 6, 7, // X edges\n 0, 2, 1, 3, 4, 6, 5, 7, // Y edges\n 0, 4, 1, 5, 2, 6, 3, 7 // Z edges\n ];\n\n const boxGeom = new THREE.BufferGeometry();\n const posBuffer = new Float32Array(edgeIndices.length * 3);\n boxGeom.setAttribute('position', new THREE.BufferAttribute(posBuffer, 3));\n\n const lineMaterial = new THREE.LineBasicMaterial({\n color: options?.color ? new THREE.Color(options.color) : 0x00d2ff,\n linewidth: 1,\n transparent: true,\n opacity: 0.6,\n depthTest: true,\n });\n this.wireframeBox = new THREE.LineSegments(boxGeom, lineMaterial);\n this.boxGroup.add(this.wireframeBox);\n\n // 4. Create 3 Dynamic Colored Active Dimension Lines\n // X (Length): Red, Y (Height): Green, Z (Width): Blue\n this.lineX = this.createDynamicLine(0xef4444);\n this.lineY = this.createDynamicLine(0x10b981);\n this.lineZ = this.createDynamicLine(0x3b82f6);\n\n // 5. Create CSS2D Labels\n const divX = document.createElement('div');\n divX.className = 'gmv-dimension-badge dim-x';\n divX.innerHTML = `<span>长 (X):</span> <strong>${formatUnit(this.modelSize.x, this.unit)}</strong>`;\n this.labelX = new CSS2DObject(divX);\n this.boxGroup.add(this.labelX);\n\n const divY = document.createElement('div');\n divY.className = 'gmv-dimension-badge dim-y';\n divY.innerHTML = `<span>高 (Y):</span> <strong>${formatUnit(this.modelSize.y, this.unit)}</strong>`;\n this.labelY = new CSS2DObject(divY);\n this.boxGroup.add(this.labelY);\n\n const divZ = document.createElement('div');\n divZ.className = 'gmv-dimension-badge dim-z';\n divZ.innerHTML = `<span>宽 (Z):</span> <strong>${formatUnit(this.modelSize.z, this.unit)}</strong>`;\n this.labelZ = new CSS2DObject(divZ);\n this.boxGroup.add(this.labelZ);\n\n this.setVisible(options?.visible ?? false);\n }\n\n private createDynamicLine(color: number): THREE.Line {\n const geom = new THREE.BufferGeometry().setFromPoints([\n new THREE.Vector3(),\n new THREE.Vector3(),\n ]);\n const mat = new THREE.LineBasicMaterial({\n color,\n linewidth: 3,\n depthTest: false,\n });\n const line = new THREE.Line(geom, mat);\n this.boxGroup.add(line);\n return line;\n }\n\n /**\n * Updates bounding box and selects the CURRENT front-facing 3 edges dynamically\n * based on the model's current rotation and camera perspective!\n */\n public update(camera: THREE.Camera): void {\n if (!this.isVisible || !this.currentModel || this.localVertices.length < 8) return;\n\n this.currentModel.updateMatrixWorld(true);\n const matrix = this.currentModel.matrixWorld;\n\n // 1. Transform all 8 local corners to world coordinates\n for (let i = 0; i < 8; i++) {\n this.worldVertices[i].copy(this.localVertices[i]).applyMatrix4(matrix);\n }\n\n // 2. Update 12-edges wireframe box positions\n if (this.wireframeBox) {\n const edgeIndices = [\n 0, 1, 2, 3, 4, 5, 6, 7, // X edges\n 0, 2, 1, 3, 4, 6, 5, 7, // Y edges\n 0, 4, 1, 5, 2, 6, 3, 7 // Z edges\n ];\n const posAttr = this.wireframeBox.geometry.attributes.position;\n let ptr = 0;\n for (const idx of edgeIndices) {\n const v = this.worldVertices[idx];\n posAttr.setXYZ(ptr++, v.x, v.y, v.z);\n }\n posAttr.needsUpdate = true;\n }\n\n // 3. Find the corner vertex CLOSEST to the camera in world space\n // Out of the 8 corners, the closest corner faces the user!\n let bestCorner = 0;\n let minDistance = Infinity;\n const camPos = camera.position;\n\n for (let i = 0; i < 8; i++) {\n const dist = this.worldVertices[i].distanceTo(camPos);\n if (dist < minDistance) {\n minDistance = dist;\n bestCorner = i;\n }\n }\n\n // 4. From bestCorner, the 3 connected edges along X, Y, Z are:\n // Neighbor along X: bestCorner ^ 1 (flips bit 0)\n // Neighbor along Y: bestCorner ^ 2 (flips bit 1)\n // Neighbor along Z: bestCorner ^ 4 (flips bit 2)\n const cornerV = this.worldVertices[bestCorner];\n const neighborX = this.worldVertices[bestCorner ^ 1];\n const neighborY = this.worldVertices[bestCorner ^ 2];\n const neighborZ = this.worldVertices[bestCorner ^ 4];\n\n // 5. Update the 3 active dimension lines\n this.updateLine(this.lineX, cornerV, neighborX);\n this.updateLine(this.lineY, cornerV, neighborY);\n this.updateLine(this.lineZ, cornerV, neighborZ);\n\n // 6. Update CSS2D label positions to midpoints of the current 3 front edges\n if (this.labelX) {\n this.labelX.position.copy(cornerV).add(neighborX).multiplyScalar(0.5);\n }\n if (this.labelY) {\n this.labelY.position.copy(cornerV).add(neighborY).multiplyScalar(0.5);\n }\n if (this.labelZ) {\n this.labelZ.position.copy(cornerV).add(neighborZ).multiplyScalar(0.5);\n }\n }\n\n private updateLine(line: THREE.Line | null, p1: THREE.Vector3, p2: THREE.Vector3): void {\n if (!line) return;\n const pos = line.geometry.attributes.position;\n pos.setXYZ(0, p1.x, p1.y, p1.z);\n pos.setXYZ(1, p2.x, p2.y, p2.z);\n pos.needsUpdate = true;\n }\n\n public setVisible(visible: boolean): void {\n this.isVisible = visible;\n this.boxGroup.visible = visible;\n }\n\n public getVisible(): boolean {\n return this.isVisible;\n }\n\n public setUnit(unit: string): void {\n this.unit = unit;\n if (this.currentModel) {\n this.attachToModel(this.currentModel, { visible: this.isVisible, unit });\n }\n }\n\n public detach(): void {\n if (this.wireframeBox) {\n this.wireframeBox.geometry.dispose();\n (this.wireframeBox.material as THREE.Material).dispose();\n this.boxGroup.remove(this.wireframeBox);\n this.wireframeBox = null;\n }\n\n if (this.lineX) {\n this.lineX.geometry.dispose();\n (this.lineX.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineX);\n this.lineX = null;\n }\n if (this.lineY) {\n this.lineY.geometry.dispose();\n (this.lineY.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineY);\n this.lineY = null;\n }\n if (this.lineZ) {\n this.lineZ.geometry.dispose();\n (this.lineZ.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineZ);\n this.lineZ = null;\n }\n\n if (this.labelX) {\n this.boxGroup.remove(this.labelX);\n this.labelX = null;\n }\n if (this.labelY) {\n this.boxGroup.remove(this.labelY);\n this.labelY = null;\n }\n if (this.labelZ) {\n this.boxGroup.remove(this.labelZ);\n this.labelZ = null;\n }\n\n this.localVertices = [];\n this.currentModel = null;\n }\n\n public destroy(): void {\n this.detach();\n this.scene.remove(this.boxGroup);\n }\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport * as TWEEN from '@tweenjs/tween.js';\nimport { AutoRotateAxis, AutoRotateDirection, AutoRotateOptions, CameraView, RotationLockAxis } from '../types';\n\nexport class CameraTool {\n private camera: THREE.PerspectiveCamera;\n private controls: OrbitControls | TrackballControls;\n private tweenGroup: TWEEN.Group;\n private currentTween: TWEEN.Tween<{ t: number }> | null = null;\n\n // Auto-Rotation State (Default: X-axis rotation, clockwise)\n private isAutoRotating: boolean = false;\n private autoRotateAxis: AutoRotateAxis = 'x';\n private autoRotateDirection: AutoRotateDirection = 'clockwise';\n private autoRotateSpeed: number = 1.0;\n\n // Axis Rotation Lock State\n private lockedAxis: RotationLockAxis = null;\n private lockedPitchAngle: number = 0;\n private lockedYawAngle: number = 0;\n\n constructor(\n camera: THREE.PerspectiveCamera,\n controls: OrbitControls | TrackballControls,\n tweenGroup?: TWEEN.Group\n ) {\n this.camera = camera;\n this.controls = controls;\n this.tweenGroup = tweenGroup || new TWEEN.Group();\n }\n\n // ==========================================\n // Auto-Rotation Controls\n // ==========================================\n\n public setAutoRotate(\n enabled: boolean,\n axis?: AutoRotateAxis,\n direction?: AutoRotateDirection,\n speed?: number\n ): void {\n this.isAutoRotating = enabled;\n if (axis !== undefined) this.autoRotateAxis = axis;\n if (direction !== undefined) this.autoRotateDirection = direction;\n if (speed !== undefined) this.autoRotateSpeed = speed;\n }\n\n public toggleAutoRotate(): boolean {\n this.isAutoRotating = !this.isAutoRotating;\n return this.isAutoRotating;\n }\n\n public setAutoRotateAxis(axis: AutoRotateAxis): void {\n this.autoRotateAxis = axis;\n }\n\n public setAutoRotateDirection(direction: AutoRotateDirection): void {\n this.autoRotateDirection = direction;\n }\n\n public setAutoRotateSpeed(speed: number): void {\n this.autoRotateSpeed = Math.max(0.1, Math.min(10, speed));\n }\n\n public getAutoRotateState(): AutoRotateOptions {\n return {\n enabled: this.isAutoRotating,\n axis: this.autoRotateAxis,\n direction: this.autoRotateDirection,\n speed: this.autoRotateSpeed,\n };\n }\n\n // ==========================================\n // Axis Rotation Lock Controls (X / Y / Z Lock)\n // ==========================================\n\n public setLockedAxis(axis: RotationLockAxis): void {\n this.lockedAxis = axis;\n const offset = this.camera.position.clone().sub(this.controls.target);\n const radius = offset.length();\n\n if (radius > 0.0001) {\n this.lockedPitchAngle = Math.asin(Math.max(-1, Math.min(1, offset.y / radius)));\n this.lockedYawAngle = Math.atan2(offset.z, offset.x);\n }\n }\n\n public getLockedAxis(): RotationLockAxis {\n return this.lockedAxis;\n }\n\n /**\n * Called each frame in render loop to apply auto-rotation and axis lock constraints\n */\n public updatePerFrame(): void {\n const target = this.controls.target;\n const offset = this.camera.position.clone().sub(target);\n const radius = offset.length();\n\n // 1. Auto-rotation logic\n if (this.isAutoRotating && radius > 0.001) {\n const dirSign = this.autoRotateDirection === 'clockwise' ? 1 : -1;\n const angle = 0.008 * this.autoRotateSpeed * dirSign;\n\n if (this.autoRotateAxis === 'x') {\n // Rotate around world X axis (1, 0, 0)\n const rotAxis = new THREE.Vector3(1, 0, 0);\n offset.applyAxisAngle(rotAxis, angle);\n this.camera.up.applyAxisAngle(rotAxis, angle);\n } else {\n // Rotate around world Y axis (0, 1, 0)\n const rotAxis = new THREE.Vector3(0, 1, 0);\n offset.applyAxisAngle(rotAxis, angle);\n this.camera.up.applyAxisAngle(rotAxis, angle);\n }\n\n this.camera.position.copy(target).add(offset);\n this.camera.lookAt(target);\n }\n\n // 2. Axis Rotation Lock Constraints\n if (this.lockedAxis && radius > 0.001) {\n const currentOffset = this.camera.position.clone().sub(target);\n const curRadius = currentOffset.length();\n\n if (this.lockedAxis === 'x') {\n // Lock X-rotation (Pitch locked, can only revolve horizontally around world Y)\n const hRadius = curRadius * Math.cos(this.lockedPitchAngle);\n const targetY = curRadius * Math.sin(this.lockedPitchAngle);\n const angleXZ = Math.atan2(currentOffset.z, currentOffset.x);\n currentOffset.x = hRadius * Math.cos(angleXZ);\n currentOffset.y = targetY;\n currentOffset.z = hRadius * Math.sin(angleXZ);\n this.camera.position.copy(target).add(currentOffset);\n } else if (this.lockedAxis === 'y') {\n // Lock Y-rotation (Yaw locked, can only pitch vertically in the locked azimuth plane)\n const curY = currentOffset.y;\n const hRadius = Math.sqrt(Math.max(0, curRadius * curRadius - curY * curY));\n currentOffset.x = hRadius * Math.cos(this.lockedYawAngle);\n currentOffset.z = hRadius * Math.sin(this.lockedYawAngle);\n this.camera.position.copy(target).add(currentOffset);\n } else if (this.lockedAxis === 'z') {\n // Lock Z-rotation (Roll locked, camera remains strictly upright without tilting)\n this.camera.up.set(0, 1, 0);\n }\n\n this.camera.lookAt(target);\n }\n }\n\n /**\n * Smoothly transitions the camera to one of the 8 standard view orientations\n */\n public setView(\n view: CameraView,\n targetObject: THREE.Object3D,\n duration: number = 600\n ): Promise<void> {\n return new Promise((resolve) => {\n // 1. Calculate bounding sphere of target\n const box = new THREE.Box3().setFromObject(targetObject);\n const sphere = new THREE.Sphere();\n box.getBoundingSphere(sphere);\n\n const center = sphere.center.clone();\n const radius = Math.max(sphere.radius, 1);\n\n // Adjust camera near and far to prevent clipping large/small models\n this.camera.near = Math.max(0.01, radius / 500);\n this.camera.far = Math.max(10000, radius * 50);\n this.camera.updateProjectionMatrix();\n\n // Fit distance based on FOV\n const fovRad = (this.camera.fov * Math.PI) / 180;\n const fitDistance = (radius / Math.sin(fovRad / 2)) * 1.15;\n\n // 2. Determine target direction vector and up vector for the view\n const dir = new THREE.Vector3();\n const up = new THREE.Vector3(0, 1, 0);\n\n switch (view) {\n case 'front': // Front (+Z)\n dir.set(0, 0, 1);\n break;\n case 'back': // Back (-Z)\n dir.set(0, 0, -1);\n break;\n case 'left': // Left (-X)\n dir.set(-1, 0, 0);\n break;\n case 'right': // Right (+X)\n dir.set(1, 0, 0);\n break;\n case 'top': // Top (+Y)\n dir.set(0, 1, 0.0001); // Slight offset to prevent gimbal lock\n up.set(0, 0, -1);\n break;\n case 'bottom': // Bottom (-Y)\n dir.set(0, -1, 0.0001);\n up.set(0, 0, 1);\n break;\n case 'isometric': // Isometric Front-Right-Top\n dir.set(1, 1, 1).normalize();\n break;\n case 'isometric-back': // Isometric Back-Left-Top\n dir.set(-1, 1, -1).normalize();\n break;\n }\n\n const targetCamPos = center.clone().add(dir.multiplyScalar(fitDistance));\n\n // 3. Stop any existing tween\n if (this.currentTween) {\n this.currentTween.stop();\n this.currentTween = null;\n }\n\n this.camera.up.copy(up);\n\n // If duration is 0, apply view immediately without animation\n if (duration <= 0) {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n resolve();\n return;\n }\n\n const startPos = this.camera.position.clone();\n const startTarget = this.controls.target.clone();\n\n // Interpolate both camera position and target\n const animData = { t: 0 };\n this.currentTween = new TWEEN.Tween(animData, this.tweenGroup)\n .to({ t: 1 }, duration)\n .easing(TWEEN.Easing.Cubic.Out)\n .onUpdate(() => {\n this.camera.position.lerpVectors(startPos, targetCamPos, animData.t);\n this.controls.target.lerpVectors(startTarget, center, animData.t);\n this.controls.update();\n })\n .onComplete(() => {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n this.currentTween = null;\n resolve();\n })\n .start();\n });\n }\n\n /**\n * Fits camera to view the whole object from current angle\n */\n public fitToObject(object: THREE.Object3D, duration: number = 500): Promise<void> {\n return new Promise((resolve) => {\n const box = new THREE.Box3().setFromObject(object);\n const sphere = new THREE.Sphere();\n box.getBoundingSphere(sphere);\n\n const center = sphere.center;\n const radius = Math.max(sphere.radius, 1);\n\n // Adjust camera near and far to prevent clipping large/small models\n this.camera.near = Math.max(0.01, radius / 500);\n this.camera.far = Math.max(10000, radius * 50);\n this.camera.updateProjectionMatrix();\n\n const fovRad = (this.camera.fov * Math.PI) / 180;\n const fitDistance = (radius / Math.sin(fovRad / 2)) * 1.15;\n\n const currentDir = this.camera.position.clone().sub(this.controls.target).normalize();\n const targetCamPos = center.clone().add(currentDir.multiplyScalar(fitDistance));\n\n const startPos = this.camera.position.clone();\n const startTarget = this.controls.target.clone();\n\n const animData = { t: 0 };\n new TWEEN.Tween(animData, this.tweenGroup)\n .to({ t: 1 }, duration)\n .easing(TWEEN.Easing.Cubic.Out)\n .onUpdate(() => {\n this.camera.position.lerpVectors(startPos, targetCamPos, animData.t);\n this.controls.target.lerpVectors(startTarget, center, animData.t);\n this.controls.update();\n })\n .onComplete(() => {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n resolve();\n })\n .start();\n });\n }\n}\n","import * as THREE from 'three';\n\nexport interface SnapResult {\n hit: boolean;\n point: THREE.Vector3 | null;\n normal: THREE.Vector3 | null;\n isVertexSnapped: boolean;\n distanceToVertex: number;\n}\n\n/**\n * Finds intersection on model and optionally snaps to the nearest vertex\n * of the intersected face if within the screen-space pixel threshold.\n */\nexport function findSnapPoint(\n mouse: THREE.Vector2, // Normalized Device Coordinates (-1 to 1)\n camera: THREE.Camera,\n modelMeshes: THREE.Mesh[],\n snapPixelThreshold: number = 15,\n viewportWidth: number = 800,\n viewportHeight: number = 600\n): SnapResult {\n if (!modelMeshes || modelMeshes.length === 0) {\n return { hit: false, point: null, normal: null, isVertexSnapped: false, distanceToVertex: Infinity };\n }\n\n const raycaster = new THREE.Raycaster();\n raycaster.setFromCamera(mouse, camera);\n\n // Set firstHitOnly for three-mesh-bvh if available\n (raycaster as unknown as { firstHitOnly: boolean }).firstHitOnly = true;\n\n const intersects = raycaster.intersectObjects(modelMeshes, true);\n\n if (intersects.length === 0) {\n return { hit: false, point: null, normal: null, isVertexSnapped: false, distanceToVertex: Infinity };\n }\n\n const hit = intersects[0];\n const hitPoint = hit.point.clone();\n const faceNormal = hit.face ? hit.face.normal.clone() : new THREE.Vector3(0, 1, 0);\n if (hit.object) {\n faceNormal.transformDirection(hit.object.matrixWorld);\n }\n\n // Check vertices of the hit face for vertex snapping\n let closestVertex: THREE.Vector3 | null = null;\n let minScreenDist = snapPixelThreshold;\n\n if (hit.face && hit.object && (hit.object as THREE.Mesh).geometry) {\n const mesh = hit.object as THREE.Mesh;\n const geometry = mesh.geometry;\n const posAttr = geometry.attributes.position;\n\n if (posAttr) {\n const vIndices = [hit.face.a, hit.face.b, hit.face.c];\n const vWorld = new THREE.Vector3();\n const vScreen = new THREE.Vector3();\n\n for (const idx of vIndices) {\n vWorld.fromBufferAttribute(posAttr, idx).applyMatrix4(mesh.matrixWorld);\n vScreen.copy(vWorld).project(camera);\n\n // Convert NDC to screen pixels\n const screenX = ((vScreen.x + 1) * 0.5) * viewportWidth;\n const screenY = ((-vScreen.y + 1) * 0.5) * viewportHeight;\n\n const mouseScreenX = ((mouse.x + 1) * 0.5) * viewportWidth;\n const mouseScreenY = ((-mouse.y + 1) * 0.5) * viewportHeight;\n\n const screenDist = Math.hypot(screenX - mouseScreenX, screenY - mouseScreenY);\n\n if (screenDist < minScreenDist) {\n minScreenDist = screenDist;\n closestVertex = vWorld.clone();\n }\n }\n }\n }\n\n if (closestVertex) {\n return {\n hit: true,\n point: closestVertex,\n normal: faceNormal,\n isVertexSnapped: true,\n distanceToVertex: minScreenDist,\n };\n }\n\n return {\n hit: true,\n point: hitPoint,\n normal: faceNormal,\n isVertexSnapped: false,\n distanceToVertex: Infinity,\n };\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { findSnapPoint } from '../utils/snapping';\nimport { AxisSectionState, ClippingAxis, SectioningState } from '../types';\nimport { EventEmitter } from '../core/EventEmitter';\n\ninterface PlaneWidget {\n axis: ClippingAxis;\n group: THREE.Group;\n mesh: THREE.Mesh;\n border: THREE.LineSegments;\n arrowGroup: THREE.Group;\n hitMeshes: THREE.Object3D[];\n label: CSS2DObject;\n labelText: HTMLDivElement;\n meshMat: THREE.MeshBasicMaterial;\n borderMat: THREE.LineBasicMaterial;\n arrowMat: THREE.MeshBasicMaterial;\n normal: THREE.Vector3;\n baseColor: number;\n borderColor: number;\n position: number;\n inverted: boolean;\n enabled: boolean;\n}\n\nexport class ClippingTool {\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private container: HTMLElement;\n private modelLoader: ModelLoader;\n private controls: OrbitControls | TrackballControls | null = null;\n private emitter: EventEmitter | null = null;\n\n // Active Three.js Clipping Planes\n private planeX: THREE.Plane;\n private planeY: THREE.Plane;\n private planeZ: THREE.Plane;\n private customPlane: THREE.Plane | null = null;\n\n // Sectioning States matching CAD / Digital3DCloud standard:\n // 1. isEnabled: Default FALSE. Only show when user turns on sectioning.\n private isEnabled: boolean = false;\n // 2. activeAxis: Recently focused axis for quick reference\n private activeAxis: ClippingAxis = 'x';\n // 3. helperVisible: Whether the translucent helper planes are visible\n private helperVisible: boolean = true;\n\n // Group containing the 3 plane widgets\n private planesGroup: THREE.Group;\n private interactive: boolean = false;\n private widgets: Partial<Record<ClippingAxis, PlaneWidget>> = {};\n\n // Dragging State with Screen-Projection Math (zero jitter, zero stickiness, no unwanted rotation)\n private isDraggingPlane: boolean = false;\n private draggedWidget: PlaneWidget | null = null;\n private dragStartPointer: THREE.Vector2 = new THREE.Vector2();\n private dragStartPosition: number = 0;\n private dragScreenAxis: THREE.Vector2 = new THREE.Vector2();\n private dragScreenAxisLenSq: number = 0;\n private raycaster: THREE.Raycaster = new THREE.Raycaster();\n private hoveredAxis: ClippingAxis | null = null;\n\n // Custom 2-point knife cutting state\n private isPickingCustomPoints: boolean = false;\n private customPoints: THREE.Vector3[] = [];\n private customMarkersGroup: THREE.Group;\n\n // Cached model bounding box\n private modelBox: THREE.Box3 = new THREE.Box3();\n\n // Bound event listeners\n private onPointerDownBound: (e: PointerEvent) => void;\n private onPointerMoveBound: (e: PointerEvent) => void;\n private onPointerMoveDragBound: (e: PointerEvent) => void;\n private onPointerUpDragBound: (e: PointerEvent) => void;\n private onDoubleClickBound: (e: MouseEvent) => void;\n\n constructor(\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n container: HTMLElement,\n modelLoader: ModelLoader,\n controls?: OrbitControls | TrackballControls,\n emitter?: EventEmitter\n ) {\n this.scene = scene;\n this.camera = camera;\n this.container = container;\n this.modelLoader = modelLoader;\n this.controls = controls || null;\n this.emitter = emitter || null;\n\n // Initialize inactive planes far away\n this.planeX = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 100000);\n this.planeY = new THREE.Plane(new THREE.Vector3(0, -1, 0), 100000);\n this.planeZ = new THREE.Plane(new THREE.Vector3(0, 0, -1), 100000);\n\n // Group for the cutting plane widgets (HIDDEN by default!)\n this.planesGroup = new THREE.Group();\n this.planesGroup.name = 'gmv-interactive-clipping-planes';\n this.planesGroup.visible = false;\n this.scene.add(this.planesGroup);\n\n // Custom 2-point marker group\n this.customMarkersGroup = new THREE.Group();\n this.customMarkersGroup.name = 'gmv-custom-clip-markers';\n this.scene.add(this.customMarkersGroup);\n\n // Bind event handlers\n this.onPointerDownBound = this.handlePointerDown.bind(this);\n this.onPointerMoveBound = this.handlePointerMove.bind(this);\n this.onPointerMoveDragBound = this.handlePointerMoveDrag.bind(this);\n this.onPointerUpDragBound = this.handlePointerUpDrag.bind(this);\n this.onDoubleClickBound = this.handleDoubleClick.bind(this);\n\n // Use CAPTURE phase on pointerdown to intercept clicks on clipping planes\n // BEFORE TrackballControls can start a rotation gesture!\n this.container.addEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.addEventListener('pointermove', this.onPointerMoveBound);\n this.container.addEventListener('dblclick', this.onDoubleClickBound, { capture: true });\n }\n\n public setControls(controls: OrbitControls | TrackballControls): void {\n this.controls = controls;\n }\n\n public setEmitter(emitter: EventEmitter): void {\n this.emitter = emitter;\n }\n\n public setInteractive(interactive: boolean): void {\n this.interactive = interactive && this.isEnabled;\n if (!this.interactive && this.container.style.cursor === 'grab') {\n this.container.style.cursor = 'default';\n }\n }\n\n /**\n * Initializes or updates bounds from loaded model\n */\n public setModelBounds(box: THREE.Box3): void {\n this.modelBox.copy(box);\n this.rebuildPlaneWidgets();\n this.resetAll();\n }\n\n /**\n * Builds the 3 interactive clipping planes strictly fitting inside the model's bounding box\n */\n private rebuildPlaneWidgets(): void {\n this.clearPlaneWidgets();\n\n const size = new THREE.Vector3(2, 2, 2);\n const center = new THREE.Vector3(0, 0, 0);\n if (!this.modelBox.isEmpty()) {\n this.modelBox.getSize(size);\n this.modelBox.getCenter(center);\n }\n\n // STRICT BOUNDING BOX DIMENSIONS:\n // Slicing planes MUST NEVER exceed the bounding box boundary!\n const sx = Math.max(0.001, size.x);\n const sy = Math.max(0.001, size.y);\n const sz = Math.max(0.001, size.z);\n\n // 1. X Plane (YZ cross section, perpendicular to X): width = sz, height = sy\n this.widgets.x = this.createPlaneWidget('x', 0xef4444, 0xf87171, sz, sy, center);\n\n // 2. Y Plane (XZ cross section, perpendicular to Y): width = sx, height = sz\n this.widgets.y = this.createPlaneWidget('y', 0x10b981, 0x34d399, sx, sz, center);\n\n // 3. Z Plane (XY cross section, perpendicular to Z): width = sx, height = sy\n this.widgets.z = this.createPlaneWidget('z', 0x3b82f6, 0x60a5fa, sx, sy, center);\n\n // Default: X axis is enabled when sectioning is turned on; Y and Z can be toggled on simultaneously\n if (this.widgets.x) this.widgets.x.enabled = true;\n if (this.widgets.y) this.widgets.y.enabled = false;\n if (this.widgets.z) this.widgets.z.enabled = false;\n\n // Apply visibility states\n this.updateWidgetsVisibility();\n }\n\n private createPlaneWidget(\n axis: ClippingAxis,\n baseColor: number,\n borderColor: number,\n w: number,\n h: number,\n center: THREE.Vector3\n ): PlaneWidget {\n const group = new THREE.Group();\n group.name = `gmv-clip-plane-${axis}`;\n\n // 1. Translucent Plane Mesh:\n // Exactly matches the cross section of the bounding box.\n const geom = new THREE.PlaneGeometry(w, h);\n const meshMat = new THREE.MeshBasicMaterial({\n color: baseColor,\n transparent: true,\n opacity: 0.25,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n const mesh = new THREE.Mesh(geom, meshMat);\n mesh.userData = { axis };\n mesh.renderOrder = 999;\n group.add(mesh);\n\n // 2. High-visibility outline frame strictly on the bounding box boundary\n const edges = new THREE.EdgesGeometry(geom);\n const borderMat = new THREE.LineBasicMaterial({\n color: borderColor,\n transparent: true,\n opacity: 0.85,\n });\n const border = new THREE.LineSegments(edges, borderMat);\n group.add(border);\n\n // 3. Center Sectioning Handle (compact, elegant, strictly inside bounding box)\n const arrowGroup = new THREE.Group();\n const handleScale = Math.min(w, h) * 0.045;\n const arrowMat = new THREE.MeshBasicMaterial({\n color: borderColor,\n transparent: true,\n opacity: 0.9,\n depthTest: false,\n });\n\n // Center Sphere\n const sphereGeom = new THREE.SphereGeometry(handleScale * 0.5, 16, 16);\n const centerSphere = new THREE.Mesh(sphereGeom, arrowMat);\n centerSphere.userData = { axis };\n arrowGroup.add(centerSphere);\n\n // Thin Shaft along normal\n const shaftGeom = new THREE.CylinderGeometry(handleScale * 0.12, handleScale * 0.12, handleScale * 1.8, 12);\n shaftGeom.rotateX(Math.PI / 2);\n const shaftMesh = new THREE.Mesh(shaftGeom, arrowMat);\n shaftMesh.userData = { axis };\n arrowGroup.add(shaftMesh);\n\n // Positive Arrow Cone\n const coneGeom1 = new THREE.ConeGeometry(handleScale * 0.35, handleScale * 0.65, 16);\n coneGeom1.rotateX(Math.PI / 2);\n coneGeom1.translate(0, 0, handleScale * 1.1);\n const coneMesh1 = new THREE.Mesh(coneGeom1, arrowMat);\n coneMesh1.userData = { axis };\n arrowGroup.add(coneMesh1);\n\n // Negative Arrow Cone\n const coneGeom2 = new THREE.ConeGeometry(handleScale * 0.35, handleScale * 0.65, 16);\n coneGeom2.rotateX(-Math.PI / 2);\n coneGeom2.translate(0, 0, -handleScale * 1.1);\n const coneMesh2 = new THREE.Mesh(coneGeom2, arrowMat);\n coneMesh2.userData = { axis };\n arrowGroup.add(coneMesh2);\n\n arrowGroup.renderOrder = 1000;\n group.add(arrowGroup);\n\n // All clickable meshes for raycasting\n const hitMeshes = [mesh, centerSphere, shaftMesh, coneMesh1, coneMesh2];\n\n // 4. Ergonomic Corner CSS2D badge positioned well inside the plane boundary\n const div = document.createElement('div');\n div.className = `gmv-plane-badge gmv-plane-badge-${axis}`;\n div.textContent = `${axis.toUpperCase()} 剖切面`;\n const label = new CSS2DObject(div);\n label.position.set((w / 2) * 0.68, (h / 2) * 0.68, 0);\n group.add(label);\n\n let normal = new THREE.Vector3(1, 0, 0);\n\n if (axis === 'x') {\n group.rotation.y = Math.PI / 2;\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(1, 0, 0);\n } else if (axis === 'y') {\n group.rotation.x = -Math.PI / 2;\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(0, 1, 0);\n } else {\n group.rotation.set(0, 0, 0);\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(0, 0, 1);\n }\n\n this.planesGroup.add(group);\n\n return {\n axis,\n group,\n mesh,\n border,\n arrowGroup,\n hitMeshes,\n label,\n labelText: div,\n meshMat,\n borderMat,\n arrowMat,\n normal,\n baseColor,\n borderColor,\n position: center[axis],\n inverted: false,\n enabled: false,\n };\n }\n\n private clearPlaneWidgets(): void {\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const widget = this.widgets[axis];\n if (widget) {\n widget.mesh.geometry.dispose();\n widget.meshMat.dispose();\n widget.border.geometry.dispose();\n widget.borderMat.dispose();\n widget.arrowMat.dispose();\n this.planesGroup.remove(widget.group);\n }\n });\n this.widgets = {};\n }\n\n // ==========================================\n // Visibility & Direction Controls (Multi-Axis Capable)\n // ==========================================\n\n private updateWidgetsVisibility(): void {\n this.planesGroup.visible = this.isEnabled && this.helperVisible;\n this.interactive = this.isEnabled;\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w) {\n // Multi-axis support: Any enabled axis will show its translucent helper plane\n w.group.visible = this.isEnabled && this.helperVisible && w.enabled;\n }\n });\n }\n\n /**\n * 1. 开启 / 关闭 剖切总开关 (Enable/Disable Sectioning)\n */\n public setSectioningEnabled(enabled: boolean): void {\n this.isEnabled = enabled;\n this.updateWidgetsVisibility();\n\n if (enabled) {\n // Ensure at least one axis is enabled if all were disabled\n const hasAnyAxis = (['x', 'y', 'z'] as ClippingAxis[]).some((a) => this.widgets[a]?.enabled);\n if (!hasAnyAxis) {\n const defaultW = this.widgets[this.activeAxis] || this.widgets.x;\n if (defaultW) defaultW.enabled = true;\n }\n\n // Re-apply positions for all active axes\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w && w.enabled) {\n this.setPlanePositionExplicit(axis, w.position);\n } else {\n this.disableAxisInternal(axis);\n }\n });\n } else {\n // Disabling sectioning clears all cuts from the model\n this.disableAllAxesPlanes();\n }\n\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public getSectioningEnabled(): boolean {\n return this.isEnabled;\n }\n\n /**\n * 2. 独立开启或关闭某个轴的剖切 (Multi-Axis Toggle)\n * 支持 X、Y、Z 任意多轴同时开启与同时剖切!\n */\n public setAxisEnabled(axis: ClippingAxis, enabled: boolean): void {\n const w = this.widgets[axis];\n if (!w) return;\n\n w.enabled = enabled;\n if (enabled) {\n this.activeAxis = axis;\n if (this.isEnabled) {\n this.setPlanePositionExplicit(axis, w.position);\n }\n } else {\n this.disableAxisInternal(axis);\n }\n\n this.updateWidgetsVisibility();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public toggleAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n const w = this.widgets[axis];\n const newState = enabled !== undefined ? enabled : !(w?.enabled);\n this.setAxisEnabled(axis, newState);\n return newState;\n }\n\n public isAxisEnabled(axis: ClippingAxis): boolean {\n return !!(this.widgets[axis]?.enabled);\n }\n\n /**\n * 设置激活/聚焦的轴向 (兼容已有调用)\n */\n public setSectioningAxis(axis: ClippingAxis): void {\n this.activeAxis = axis;\n const w = this.widgets[axis];\n if (w && !w.enabled) {\n w.enabled = true;\n if (this.isEnabled) {\n this.setPlanePositionExplicit(axis, w.position);\n }\n }\n this.updateWidgetsVisibility();\n this.emitSectioningState();\n }\n\n public getSectioningAxis(): ClippingAxis {\n return this.activeAxis;\n }\n\n /**\n * 3. 隐藏 / 显示 剖切辅助面 (Hide Sectional Plane while keeping the cut)\n */\n public setSectioningHelperVisible(visible: boolean): void {\n this.helperVisible = visible;\n this.updateWidgetsVisibility();\n this.emitSectioningState();\n }\n\n public getSectioningHelperVisible(): boolean {\n return this.helperVisible;\n }\n\n /**\n * 4. 独立反向某轴剖切 (Reverse Sectioning for a specific axis or active axis)\n */\n public setAxisReverse(axis: ClippingAxis, reverse: boolean): void {\n const widget = this.widgets[axis];\n if (widget) {\n widget.inverted = reverse;\n if (this.isEnabled && widget.enabled) {\n this.setPlanePositionExplicit(axis, widget.position);\n } else {\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (widget.position - min) / range : 0.5;\n widget.labelText.textContent = `${axis.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%${reverse ? ' (反向)' : ''}`;\n }\n }\n this.emitSectioningState();\n }\n\n public setSectioningReverse(reverse: boolean): void {\n this.setAxisReverse(this.activeAxis, reverse);\n }\n\n public getSectioningReverse(): boolean {\n return !!(this.widgets[this.activeAxis]?.inverted);\n }\n\n public invertAxis(axis: ClippingAxis): boolean {\n const w = this.widgets[axis];\n if (!w) return false;\n this.setAxisReverse(axis, !w.inverted);\n return w.inverted;\n }\n\n /**\n * 5. 重置剖切面至中心位置与默认正向状态\n */\n public resetSectioning(axis?: ClippingAxis): void {\n const center = new THREE.Vector3();\n const isEmpty = this.modelBox.isEmpty();\n if (!isEmpty) {\n this.modelBox.getCenter(center);\n } else {\n center.set(0, 0, 0);\n }\n\n // 1. 清除任何自定义 2 点切刀平面\n this.clearCustomPlane();\n\n // 2. 目标轴\n const targetAxes: ClippingAxis[] = axis ? [axis] : (['x', 'y', 'z'] as ClippingAxis[]);\n\n targetAxes.forEach((a) => {\n const widget = this.widgets[a];\n if (widget) {\n widget.position = center[a];\n widget.inverted = false;\n widget.group.position.set(center.x, center.y, center.z);\n if (!axis) {\n // 全局重置时,默认激活 X 轴,关闭 Y/Z 轴\n widget.enabled = a === 'x';\n }\n const min = isEmpty ? -1 : this.modelBox.min[a];\n const max = isEmpty ? 1 : this.modelBox.max[a];\n const range = max - min;\n const progress = range > 0 && !isNaN(widget.position) ? Math.max(0, Math.min(1, (widget.position - min) / range)) : 0.5;\n widget.labelText.textContent = `${a.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%`;\n }\n });\n\n this.activeAxis = axis || 'x';\n this.helperVisible = true;\n this.isEnabled = true; // 确保剖切开启,立即可见中心剖切面!\n\n // 3. 重新设置所有轴向的平面方程\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n const widget = this.widgets[a];\n if (widget && widget.enabled) {\n this.setPlanePositionExplicit(a, widget.position);\n } else {\n this.disableAxisInternal(a);\n }\n });\n\n this.updateWidgetsVisibility();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n /**\n * 获取多轴综合剖切状态\n */\n public getSectioningState(): SectioningState {\n const isEmpty = this.modelBox.isEmpty();\n const minX = isEmpty ? -1 : this.modelBox.min.x;\n const maxX = isEmpty ? 1 : this.modelBox.max.x;\n const minY = isEmpty ? -1 : this.modelBox.min.y;\n const maxY = isEmpty ? 1 : this.modelBox.max.y;\n const minZ = isEmpty ? -1 : this.modelBox.min.z;\n const maxZ = isEmpty ? 1 : this.modelBox.max.z;\n\n const calcProg = (pos: number, min: number, max: number) => {\n const range = max - min;\n if (isNaN(pos) || range <= 0) return 0.5;\n return Math.max(0, Math.min(1, (pos - min) / range));\n };\n\n const wx = this.widgets.x;\n const wy = this.widgets.y;\n const wz = this.widgets.z;\n\n const axes: Record<ClippingAxis, AxisSectionState> = {\n x: {\n enabled: wx?.enabled ?? false,\n inverted: wx?.inverted ?? false,\n position: wx?.position ?? 0,\n progress: calcProg(wx?.position ?? 0, minX, maxX),\n },\n y: {\n enabled: wy?.enabled ?? false,\n inverted: wy?.inverted ?? false,\n position: wy?.position ?? 0,\n progress: calcProg(wy?.position ?? 0, minY, maxY),\n },\n z: {\n enabled: wz?.enabled ?? false,\n inverted: wz?.inverted ?? false,\n position: wz?.position ?? 0,\n progress: calcProg(wz?.position ?? 0, minZ, maxZ),\n },\n };\n\n const activeW = this.widgets[this.activeAxis];\n const curAxisProg = axes[this.activeAxis]?.progress ?? 0.5;\n const curAxisPos = activeW?.position ?? 0;\n const curAxisInv = activeW?.inverted ?? false;\n\n return {\n enabled: this.isEnabled,\n axes,\n helperVisible: this.helperVisible,\n axis: this.activeAxis,\n isReverse: curAxisInv,\n progress: curAxisProg,\n position: curAxisPos,\n };\n }\n\n private emitSectioningState(): void {\n if (this.emitter) {\n this.emitter.emit('sectioning:change', this.getSectioningState());\n }\n }\n\n // ==========================================\n // Direct 3D Mouse Press & Drag Handling\n // Multi-Axis Pick: Pick and drag ANY active axis plane directly in 3D!\n // ==========================================\n\n private handlePointerDown(e: PointerEvent): void {\n if (e.button !== 0) return; // Only left mouse button\n\n if (this.isPickingCustomPoints) {\n this.handleCustomPointPick(e);\n e.stopImmediatePropagation();\n e.preventDefault();\n return;\n }\n\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n // Multi-Axis Pick: test against ALL currently enabled & visible plane widgets\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n if (candidateWidgets.length === 0) return;\n\n let closestHit: { widget: PlaneWidget; distance: number } | null = null;\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n const dist = intersects[0].distance;\n if (!closestHit || dist < closestHit.distance) {\n closestHit = { widget, distance: dist };\n }\n }\n }\n\n if (closestHit) {\n // CRITICAL: Stop event propagation in capture phase so TrackballControls NEVER starts rotating!\n e.stopImmediatePropagation();\n e.preventDefault();\n\n const activeWidget = closestHit.widget;\n this.activeAxis = activeWidget.axis;\n this.isDraggingPlane = true;\n this.draggedWidget = activeWidget;\n\n // Disable camera controls and clear any internal Trackball state immediately\n if (this.controls) {\n this.controls.enabled = false;\n if ('state' in this.controls) {\n (this.controls as any).state = -1;\n if ((this.controls as any)._moveCurr && (this.controls as any)._movePrev) {\n (this.controls as any)._moveCurr.copy((this.controls as any)._movePrev);\n }\n }\n }\n\n this.container.style.cursor = 'grabbing';\n activeWidget.meshMat.opacity = 0.65;\n activeWidget.arrowMat.opacity = 1.0;\n\n // Record start pointer client coordinates\n this.dragStartPointer.set(e.clientX, e.clientY);\n this.dragStartPosition = activeWidget.position;\n\n // Project the 3D unit axis vector into 2D screen space to enable smooth, jitter-free 1:1 dragging\n const axisVector = new THREE.Vector3();\n if (activeWidget.axis === 'x') axisVector.set(1, 0, 0);\n else if (activeWidget.axis === 'y') axisVector.set(0, 1, 0);\n else axisVector.set(0, 0, 1);\n\n const p0 = activeWidget.group.position.clone();\n const p1 = activeWidget.group.position.clone().add(axisVector);\n\n const ndc0 = p0.project(this.camera);\n const screen0 = new THREE.Vector2(\n ((ndc0.x + 1) * rect.width) / 2,\n ((-ndc0.y + 1) * rect.height) / 2\n );\n\n const ndc1 = p1.project(this.camera);\n const screen1 = new THREE.Vector2(\n ((ndc1.x + 1) * rect.width) / 2,\n ((-ndc1.y + 1) * rect.height) / 2\n );\n\n this.dragScreenAxis.subVectors(screen1, screen0);\n this.dragScreenAxisLenSq = this.dragScreenAxis.lengthSq();\n\n // Register global drag listeners in capture phase for buttery-smooth tracking\n window.addEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.addEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n }\n }\n\n private handlePointerMoveDrag(e: PointerEvent): void {\n if (!this.isDraggingPlane || !this.draggedWidget) return;\n\n e.stopImmediatePropagation();\n e.preventDefault();\n\n const deltaMouse = new THREE.Vector2(\n e.clientX - this.dragStartPointer.x,\n e.clientY - this.dragStartPointer.y\n );\n\n let deltaUnits = 0;\n if (this.dragScreenAxisLenSq > 25) {\n // Axis is tilted on screen: project mouse movement directly onto the screen axis direction\n deltaUnits = this.dragScreenAxis.dot(deltaMouse) / this.dragScreenAxisLenSq;\n } else {\n // Axis is viewed almost directly face-on: drag vertically to adjust depth\n const size = new THREE.Vector3();\n this.modelBox.getSize(size);\n const axisSpan = Math.max(0.1, size[this.draggedWidget.axis]);\n deltaUnits = (-deltaMouse.y / 280) * axisSpan;\n }\n\n const min = this.modelBox.min[this.draggedWidget.axis];\n const max = this.modelBox.max[this.draggedWidget.axis];\n\n let newPos = this.dragStartPosition + deltaUnits;\n newPos = Math.max(min, Math.min(max, newPos));\n\n this.setPlanePositionExplicit(this.draggedWidget.axis, newPos);\n }\n\n private handlePointerUpDrag(e: PointerEvent): void {\n if (!this.isDraggingPlane) return;\n\n e.stopImmediatePropagation();\n e.preventDefault();\n\n this.isDraggingPlane = false;\n\n if (this.draggedWidget) {\n this.draggedWidget.meshMat.opacity = 0.25;\n this.draggedWidget.arrowMat.opacity = 0.9;\n this.draggedWidget = null;\n }\n\n this.container.style.cursor = 'default';\n\n window.removeEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.removeEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n\n // CRITICAL: Restore camera controls in requestAnimationFrame to prevent lingering pointerup\n // from triggering unwanted camera momentum or spinning!\n requestAnimationFrame(() => {\n if (this.controls) {\n if ('state' in this.controls) {\n (this.controls as any).state = -1;\n if ((this.controls as any)._moveCurr && (this.controls as any)._movePrev) {\n (this.controls as any)._moveCurr.copy((this.controls as any)._movePrev);\n }\n }\n this.controls.enabled = true;\n }\n });\n }\n\n /**\n * Hover highlighting across all enabled planes\n */\n private handlePointerMove(e: PointerEvent): void {\n if (this.isDraggingPlane) return;\n if (this.isPickingCustomPoints) return;\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n let hoveredHitAxis: ClippingAxis | null = null;\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n hoveredHitAxis = widget.axis;\n break;\n }\n }\n\n this.setHoveredAxis(hoveredHitAxis);\n if (hoveredHitAxis) {\n this.container.style.cursor = 'grab';\n } else if (this.container.style.cursor === 'grab') {\n this.container.style.cursor = 'default';\n }\n }\n\n private setHoveredAxis(axis: ClippingAxis | null): void {\n if (this.hoveredAxis === axis) return;\n this.hoveredAxis = axis;\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n const w = this.widgets[a];\n if (w) {\n if (a === axis) {\n w.meshMat.opacity = 0.52;\n w.borderMat.opacity = 1.0;\n w.arrowMat.opacity = 1.0;\n } else {\n w.meshMat.opacity = 0.25;\n w.borderMat.opacity = 0.85;\n w.arrowMat.opacity = 0.9;\n }\n }\n });\n }\n\n /**\n * Double click on any plane quickly flips/inverts slicing direction of that specific plane\n */\n private handleDoubleClick(e: MouseEvent): void {\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n e.stopImmediatePropagation();\n e.preventDefault();\n this.invertAxis(widget.axis);\n break;\n }\n }\n }\n\n // ==========================================\n // Slicing Calculation & Updates\n // ==========================================\n\n public setPlanePositionExplicit(axis: ClippingAxis, pos: number): void {\n const widget = this.widgets[axis];\n if (!widget) return;\n\n widget.position = pos;\n\n if (axis === 'x') {\n widget.group.position.x = pos;\n if (widget.inverted) {\n this.planeX.set(new THREE.Vector3(1, 0, 0), -pos);\n } else {\n this.planeX.set(new THREE.Vector3(-1, 0, 0), pos);\n }\n } else if (axis === 'y') {\n widget.group.position.y = pos;\n if (widget.inverted) {\n this.planeY.set(new THREE.Vector3(0, 1, 0), -pos);\n } else {\n this.planeY.set(new THREE.Vector3(0, -1, 0), pos);\n }\n } else if (axis === 'z') {\n widget.group.position.z = pos;\n if (widget.inverted) {\n this.planeZ.set(new THREE.Vector3(0, 0, 1), -pos);\n } else {\n this.planeZ.set(new THREE.Vector3(0, 0, -1), pos);\n }\n }\n\n widget.enabled = true;\n this.applyPlanes();\n\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (pos - min) / range : 0.5;\n widget.labelText.textContent = `${axis.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%${widget.inverted ? ' (反向)' : ''}`;\n\n this.emitSectioningState();\n\n if (this.emitter) {\n this.emitter.emit('clipping:change', {\n axis,\n position: pos,\n progress,\n inverted: widget.inverted,\n enabled: true,\n });\n }\n }\n\n public setAxisProgress(axis: ClippingAxis, progress: number): void {\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const clampedProgress = Math.max(0, Math.min(1, progress));\n const pos = min + clampedProgress * (max - min);\n this.setPlanePositionExplicit(axis, pos);\n }\n\n public getAxisProgress(axis: ClippingAxis): number {\n const widget = this.widgets[axis];\n if (!widget) return 0.5;\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n return range > 0 ? Math.max(0, Math.min(1, (widget.position - min) / range)) : 0.5;\n }\n\n private disableAxisInternal(axis: ClippingAxis): void {\n if (axis === 'x') {\n this.planeX.set(new THREE.Vector3(-1, 0, 0), 100000);\n } else if (axis === 'y') {\n this.planeY.set(new THREE.Vector3(0, -1, 0), 100000);\n } else if (axis === 'z') {\n this.planeZ.set(new THREE.Vector3(0, 0, -1), 100000);\n }\n }\n\n private disableAllAxesPlanes(): void {\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n this.disableAxisInternal(a);\n });\n }\n\n // Backwards compatibility helper methods\n public setAxisClipping(axis: ClippingAxis, progress: number, inverted: boolean = false): void {\n this.activeAxis = axis;\n const widget = this.widgets[axis];\n if (widget) {\n widget.inverted = inverted;\n widget.enabled = true;\n }\n this.setSectioningEnabled(true);\n this.setAxisProgress(axis, progress);\n }\n\n public disableAxis(axis: ClippingAxis): void {\n this.setAxisEnabled(axis, false);\n }\n\n public getAxisState(axis: ClippingAxis): {\n enabled: boolean;\n progress: number;\n inverted: boolean;\n position: number;\n } {\n const widget = this.widgets[axis];\n if (!widget) {\n return { enabled: false, progress: 0.5, inverted: false, position: 0 };\n }\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (widget.position - min) / range : 0.5;\n return {\n enabled: this.isEnabled && widget.enabled,\n progress,\n inverted: widget.inverted,\n position: widget.position,\n };\n }\n\n public setPlanesVisible(visible: boolean): void {\n this.setSectioningHelperVisible(visible);\n }\n\n public getPlanesVisible(): boolean {\n return this.helperVisible;\n }\n\n // ==========================================\n // Custom 2-Point Plane Slicing (Knife Cut)\n // ==========================================\n\n public startCustomClip(): void {\n this.cancelCustomClip();\n this.isPickingCustomPoints = true;\n this.customPoints = [];\n this.container.style.cursor = 'crosshair';\n }\n\n public cancelCustomClip(): void {\n this.isPickingCustomPoints = false;\n this.container.style.cursor = 'default';\n this.clearCustomMarkers();\n }\n\n public clearCustomPlane(): void {\n this.customPlane = null;\n this.clearCustomMarkers();\n this.applyPlanes();\n }\n\n public invertCustomClip(): boolean {\n if (this.customPlane) {\n this.customPlane.negate();\n this.applyPlanes();\n return true;\n }\n return false;\n }\n\n public hasCustomPlane(): boolean {\n return this.customPlane !== null;\n }\n\n private handleCustomPointPick(e: PointerEvent): void {\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n // Only clicks on model surface\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n if (!snap.hit || !snap.point) {\n return;\n }\n\n this.addCustomPoint(snap.point);\n }\n\n private addCustomPoint(point: THREE.Vector3): void {\n this.customPoints.push(point.clone());\n\n const markerGeom = new THREE.SphereGeometry(1, 16, 16);\n const markerMat = new THREE.MeshBasicMaterial({ color: 0x38bdf8, depthTest: false });\n const marker = new THREE.Mesh(markerGeom, markerMat);\n marker.position.copy(point);\n marker.userData = { fixedScreenRadius: 5.5 };\n this.customMarkersGroup.add(marker);\n this.updateSingleMarkerScale(marker);\n\n if (this.customPoints.length === 2) {\n const p1 = this.customPoints[0];\n const p2 = this.customPoints[1];\n\n const cutLine = new THREE.Vector3().subVectors(p2, p1);\n if (cutLine.lengthSq() < 0.00001) {\n this.customPoints.pop();\n this.customMarkersGroup.remove(marker);\n return;\n }\n cutLine.normalize();\n\n const viewDir = new THREE.Vector3();\n this.camera.getWorldDirection(viewDir);\n\n let normal = new THREE.Vector3().crossVectors(cutLine, viewDir).normalize();\n if (normal.lengthSq() < 0.0001) {\n normal = new THREE.Vector3().crossVectors(cutLine, this.camera.up).normalize();\n }\n\n this.customPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, p1);\n\n this.clearCustomMarkers();\n this.cancelCustomClip();\n this.applyPlanes();\n }\n }\n\n /**\n * Updates custom knife markers to maintain constant pixel size on screen\n */\n public updatePerFrame(viewportHeight?: number): void {\n if (this.customMarkersGroup.children.length === 0) return;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n this.customMarkersGroup.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n }\n\n private updateSingleMarkerScale(obj: THREE.Object3D, viewportHeight?: number): void {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n }\n\n private clearCustomMarkers(): void {\n while (this.customMarkersGroup.children.length > 0) {\n const obj = this.customMarkersGroup.children[0] as THREE.Mesh;\n if (obj.geometry) obj.geometry.dispose();\n if (obj.material) (obj.material as THREE.Material).dispose();\n this.customMarkersGroup.remove(obj);\n }\n }\n\n /**\n * Applies all currently active clipping planes to model materials simultaneously!\n */\n private applyPlanes(): void {\n const activePlanes: THREE.Plane[] = [];\n\n if (this.isEnabled) {\n if (this.widgets.x?.enabled) activePlanes.push(this.planeX);\n if (this.widgets.y?.enabled) activePlanes.push(this.planeY);\n if (this.widgets.z?.enabled) activePlanes.push(this.planeZ);\n }\n\n if (this.customPlane) activePlanes.push(this.customPlane);\n\n this.modelLoader.applyClippingPlanes(activePlanes);\n }\n\n public resetAll(): void {\n this.isEnabled = false;\n this.activeAxis = 'x';\n this.helperVisible = true;\n\n this.disableAllAxesPlanes();\n\n const center = new THREE.Vector3();\n this.modelBox.getCenter(center);\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w) {\n // By default, X is primed as the first active axis once enabled\n w.enabled = axis === 'x';\n w.inverted = false;\n w.position = center[axis];\n w.group.position.set(center.x, center.y, center.z);\n w.labelText.textContent = `${axis.toUpperCase()} 剖切面`;\n }\n });\n\n this.updateWidgetsVisibility();\n this.clearCustomPlane();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public destroy(): void {\n this.cancelCustomClip();\n this.resetAll();\n this.clearPlaneWidgets();\n this.scene.remove(this.planesGroup);\n this.scene.remove(this.customMarkersGroup);\n\n this.container.removeEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.removeEventListener('pointermove', this.onPointerMoveBound);\n this.container.removeEventListener('dblclick', this.onDoubleClickBound, { capture: true });\n window.removeEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.removeEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n }\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { findSnapPoint } from '../utils/snapping';\nimport { compute3DPolygonArea, formatUnit } from '../utils/math';\nimport { EventEmitter } from '../core/EventEmitter';\nimport { MeasurementResult, MeasurementType } from '../types';\n\nexport class MeasurementTool {\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private container: HTMLElement;\n private modelLoader: ModelLoader;\n private emitter: EventEmitter;\n private controls: OrbitControls | TrackballControls | null = null;\n\n private currentMode: MeasurementType = 'none';\n private unit: string = 'm';\n\n // Measurements container\n private measurementGroup: THREE.Group;\n private results: MeasurementResult[] = [];\n\n // Active interaction points for point-to-point tools\n private activePoints: THREE.Vector3[] = [];\n private activePreviewGroup: THREE.Group;\n private activeMarkersGroup: THREE.Group;\n private activeLineGroup: THREE.Group;\n private snapCursorMesh: THREE.Mesh;\n\n // Event handlers\n private onPointerDownBound: (e: PointerEvent) => void;\n private onPointerMoveBound: (e: PointerEvent) => void;\n private onDoubleClickBound: (e: MouseEvent) => void;\n\n constructor(\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n container: HTMLElement,\n modelLoader: ModelLoader,\n emitter: EventEmitter,\n controls?: OrbitControls | TrackballControls\n ) {\n this.scene = scene;\n this.camera = camera;\n this.container = container;\n this.modelLoader = modelLoader;\n this.emitter = emitter;\n this.controls = controls || null;\n\n this.measurementGroup = new THREE.Group();\n this.measurementGroup.name = 'gmv-measurements-group';\n this.scene.add(this.measurementGroup);\n\n this.activePreviewGroup = new THREE.Group();\n this.activePreviewGroup.name = 'gmv-measure-preview-group';\n this.scene.add(this.activePreviewGroup);\n\n this.activeMarkersGroup = new THREE.Group();\n this.activeMarkersGroup.name = 'gmv-active-markers-group';\n this.activePreviewGroup.add(this.activeMarkersGroup);\n\n this.activeLineGroup = new THREE.Group();\n this.activeLineGroup.name = 'gmv-active-line-group';\n this.activePreviewGroup.add(this.activeLineGroup);\n\n // Glowing Snapping Cursor (Screen-space fixed size: never scales with model)\n const snapGeom = new THREE.SphereGeometry(1, 16, 16);\n const snapMat = new THREE.MeshBasicMaterial({\n color: 0x38bdf8,\n transparent: true,\n opacity: 0.9,\n depthTest: false,\n });\n this.snapCursorMesh = new THREE.Mesh(snapGeom, snapMat);\n this.snapCursorMesh.userData = { fixedScreenRadius: 5.5 };\n this.snapCursorMesh.visible = false;\n this.scene.add(this.snapCursorMesh);\n\n this.onPointerDownBound = this.handlePointerDown.bind(this);\n this.onPointerMoveBound = this.handlePointerMove.bind(this);\n this.onDoubleClickBound = this.handleDoubleClick.bind(this);\n }\n\n public setControls(controls: OrbitControls | TrackballControls): void {\n this.controls = controls;\n }\n\n public setUnit(unit: string): void {\n this.unit = unit;\n }\n\n public setMode(mode: MeasurementType): void {\n if (this.currentMode === mode) return;\n\n this.cancelActive();\n this.currentMode = mode;\n\n if (mode === 'none') {\n this.container.style.cursor = 'default';\n this.snapCursorMesh.visible = false;\n this.removeListeners();\n } else {\n this.container.style.cursor = 'crosshair';\n this.addListeners();\n this.emitter.emit('measure:start', mode);\n }\n }\n\n public getMode(): MeasurementType {\n return this.currentMode;\n }\n\n public getControls(): OrbitControls | TrackballControls | null {\n return this.controls;\n }\n\n private addListeners(): void {\n this.removeListeners();\n this.container.addEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.addEventListener('pointermove', this.onPointerMoveBound);\n this.container.addEventListener('dblclick', this.onDoubleClickBound);\n }\n\n private removeListeners(): void {\n this.container.removeEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.removeEventListener('pointermove', this.onPointerMoveBound);\n this.container.removeEventListener('dblclick', this.onDoubleClickBound);\n }\n\n private handlePointerMove(e: PointerEvent): void {\n if (this.currentMode === 'none') return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n // Rule: Click & Snapping strictly allowed on model only\n if (!snap.hit || !snap.point) {\n this.container.style.cursor = 'not-allowed';\n this.snapCursorMesh.visible = false;\n return;\n }\n\n this.container.style.cursor = 'crosshair';\n this.snapCursorMesh.visible = true;\n this.snapCursorMesh.position.copy(snap.point);\n\n if (snap.isVertexSnapped) {\n (this.snapCursorMesh.material as THREE.MeshBasicMaterial).color.setHex(0xf43f5e);\n this.snapCursorMesh.userData.fixedScreenRadius = 7.5;\n } else {\n (this.snapCursorMesh.material as THREE.MeshBasicMaterial).color.setHex(0x38bdf8);\n this.snapCursorMesh.userData.fixedScreenRadius = 5.5;\n }\n this.updateSingleMeshScale(this.snapCursorMesh);\n\n // Dynamic Line Preview while moving mouse\n if (this.activePoints.length > 0) {\n this.updateDynamicPreview(snap.point);\n }\n }\n\n private handlePointerDown(e: PointerEvent): void {\n if (this.currentMode === 'none') return;\n if (e.button !== 0) return; // Left mouse button only\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n // STRICT CHECK: Model outside clicks are completely ignored!\n if (!snap.hit || !snap.point) {\n return;\n }\n\n const clickedPoint = snap.point.clone();\n this.activePoints.push(clickedPoint);\n this.addActiveMarker(clickedPoint);\n this.emitter.emit('measure:change', [...this.activePoints]);\n\n // Check completion criteria for single-shot measurements\n if (this.currentMode === 'distance' && this.activePoints.length === 2) {\n this.finishDistanceMeasurement();\n } else if (this.currentMode === 'height' && this.activePoints.length === 2) {\n this.finishHeightMeasurement();\n } else if (this.currentMode === 'angle' && this.activePoints.length === 3) {\n this.finishAngleMeasurement();\n }\n }\n\n private addActiveMarker(pos: THREE.Vector3): void {\n const geom = new THREE.SphereGeometry(1, 16, 16);\n const mat = new THREE.MeshBasicMaterial({ color: 0x38bdf8, depthTest: false });\n const sphere = new THREE.Mesh(geom, mat);\n sphere.position.copy(pos);\n sphere.userData = { fixedScreenRadius: 5.5 };\n this.activeMarkersGroup.add(sphere);\n this.updateSingleMeshScale(sphere);\n }\n\n private handleDoubleClick(_e: MouseEvent): void {\n if (this.currentMode === 'area' && this.activePoints.length >= 3) {\n this.finishAreaMeasurement();\n }\n }\n\n // ==========================================\n // Dynamic Preview\n // ==========================================\n\n private updateDynamicPreview(currentPoint: THREE.Vector3): void {\n this.clearActiveLine();\n\n const points = [...this.activePoints, currentPoint];\n if (points.length < 2) return;\n\n const geom = new THREE.BufferGeometry().setFromPoints(points);\n const mat = new THREE.LineDashedMaterial({\n color: 0x38bdf8,\n dashSize: 0.2,\n gapSize: 0.1,\n depthTest: false,\n });\n const line = new THREE.Line(geom, mat);\n line.computeLineDistances();\n this.activeLineGroup.add(line);\n }\n\n // ==========================================\n // Measurements Execution (Single-Shot: Exit after each measurement)\n // ==========================================\n\n private finishDistanceMeasurement(): void {\n const p1 = this.activePoints[0];\n const p2 = this.activePoints[1];\n const distance = p1.distanceTo(p2);\n const midPoint = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5);\n\n const group = new THREE.Group();\n\n // 1. Line\n const geom = new THREE.BufferGeometry().setFromPoints([p1, p2]);\n const mat = new THREE.LineBasicMaterial({ color: 0x3b82f6, linewidth: 2, depthTest: false });\n const line = new THREE.Line(geom, mat);\n group.add(line);\n\n // 2. End Spheres\n this.addEndSphere(group, p1, 0x3b82f6);\n this.addEndSphere(group, p2, 0x3b82f6);\n\n // 3. CSS2D Label\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge distance';\n div.innerHTML = `<span>距离:</span> <strong>${formatUnit(distance, this.unit)}</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(midPoint);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `dist_${Date.now()}`,\n type: 'distance',\n value: distance,\n formattedValue: formatUnit(distance, this.unit),\n unit: this.unit,\n points: [p1, p2],\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishHeightMeasurement(): void {\n const p1 = this.activePoints[0];\n const p2 = this.activePoints[1];\n\n const verticalHeight = Math.abs(p2.y - p1.y);\n const horizontalDistance = new THREE.Vector2(p1.x, p1.z).distanceTo(new THREE.Vector2(p2.x, p2.z));\n const totalDistance = p1.distanceTo(p2);\n\n const group = new THREE.Group();\n\n // Direct connecting line\n const directGeom = new THREE.BufferGeometry().setFromPoints([p1, p2]);\n const directMat = new THREE.LineDashedMaterial({ color: 0x94a3b8, dashSize: 0.15, gapSize: 0.08, depthTest: false });\n const directLine = new THREE.Line(directGeom, directMat);\n directLine.computeLineDistances();\n group.add(directLine);\n\n // Orthogonal right triangle lines\n const corner = new THREE.Vector3(p2.x, p1.y, p2.z);\n\n const hGeom = new THREE.BufferGeometry().setFromPoints([p1, corner]);\n const hMat = new THREE.LineBasicMaterial({ color: 0x10b981, depthTest: false });\n const hLine = new THREE.Line(hGeom, hMat);\n group.add(hLine);\n\n const vGeom = new THREE.BufferGeometry().setFromPoints([corner, p2]);\n const vMat = new THREE.LineBasicMaterial({ color: 0xef4444, linewidth: 2, depthTest: false });\n const vLine = new THREE.Line(vGeom, vMat);\n group.add(vLine);\n\n this.addEndSphere(group, p1, 0x10b981);\n this.addEndSphere(group, p2, 0xef4444);\n this.addEndSphere(group, corner, 0xfacc15, 4.0);\n\n // Height badge\n const vMid = new THREE.Vector3().addVectors(corner, p2).multiplyScalar(0.5);\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge height';\n div.innerHTML = `\n <div><span>高差 (ΔH):</span> <strong>${formatUnit(verticalHeight, this.unit)}</strong></div>\n <div style=\"font-size:10px;opacity:0.85;\">水平: ${formatUnit(horizontalDistance, this.unit)} · 直线: ${formatUnit(totalDistance, this.unit)}</div>\n `;\n const label = new CSS2DObject(div);\n label.position.copy(vMid);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `height_${Date.now()}`,\n type: 'height',\n value: verticalHeight,\n formattedValue: formatUnit(verticalHeight, this.unit),\n unit: this.unit,\n points: [p1, p2],\n extra: {\n verticalHeight,\n horizontalDistance,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishAreaMeasurement(): void {\n if (this.activePoints.length < 3) return;\n\n const points = [...this.activePoints];\n const area = compute3DPolygonArea(points);\n\n const group = new THREE.Group();\n\n // Outline polygon\n const loopPoints = [...points, points[0]];\n const lineGeom = new THREE.BufferGeometry().setFromPoints(loopPoints);\n const lineMat = new THREE.LineBasicMaterial({ color: 0x8b5cf6, linewidth: 2, depthTest: false });\n const line = new THREE.Line(lineGeom, lineMat);\n group.add(line);\n\n points.forEach((p) => this.addEndSphere(group, p, 0x8b5cf6));\n\n // Centroid\n const centroid = new THREE.Vector3();\n points.forEach((p) => centroid.add(p));\n centroid.divideScalar(points.length);\n\n // CSS2D Label\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge area';\n div.innerHTML = `<span>面积:</span> <strong>${formatUnit(area, `${this.unit}²`)}</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(centroid);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `area_${Date.now()}`,\n type: 'area',\n value: area,\n formattedValue: formatUnit(area, `${this.unit}²`),\n unit: `${this.unit}²`,\n points,\n extra: {\n area,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishAngleMeasurement(): void {\n if (this.activePoints.length !== 3) return;\n\n const p1 = this.activePoints[0];\n const vertex = this.activePoints[1];\n const p2 = this.activePoints[2];\n\n const v1 = new THREE.Vector3().subVectors(p1, vertex).normalize();\n const v2 = new THREE.Vector3().subVectors(p2, vertex).normalize();\n\n let dot = v1.dot(v2);\n dot = Math.max(-1, Math.min(1, dot));\n const angleRad = Math.acos(dot);\n const angleDeg = THREE.MathUtils.radToDeg(angleRad);\n\n const group = new THREE.Group();\n\n // Lines\n const geom1 = new THREE.BufferGeometry().setFromPoints([vertex, p1]);\n const geom2 = new THREE.BufferGeometry().setFromPoints([vertex, p2]);\n const mat = new THREE.LineBasicMaterial({ color: 0xf59e0b, linewidth: 2, depthTest: false });\n group.add(new THREE.Line(geom1, mat));\n group.add(new THREE.Line(geom2, mat));\n\n this.addEndSphere(group, p1, 0xf59e0b);\n this.addEndSphere(group, p2, 0xf59e0b);\n this.addEndSphere(group, vertex, 0xef4444, 6.5);\n\n // Small Arc indication\n const arcRadius = Math.min(p1.distanceTo(vertex), p2.distanceTo(vertex)) * 0.35 || 0.4;\n const arcPoints: THREE.Vector3[] = [];\n const segments = 16;\n for (let i = 0; i <= segments; i++) {\n const t = i / segments;\n const arcVec = new THREE.Vector3().copy(v1).lerp(v2, t).normalize().multiplyScalar(arcRadius);\n arcPoints.push(new THREE.Vector3().addVectors(vertex, arcVec));\n }\n const arcGeom = new THREE.BufferGeometry().setFromPoints(arcPoints);\n const arcMat = new THREE.LineBasicMaterial({ color: 0xfbbf24, depthTest: false });\n group.add(new THREE.Line(arcGeom, arcMat));\n\n // Label position\n const midDir = new THREE.Vector3().addVectors(v1, v2).normalize();\n const labelPos = vertex.clone().add(midDir.multiplyScalar(arcRadius * 1.3));\n\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge angle';\n div.innerHTML = `<span>角度:</span> <strong>${angleDeg.toFixed(1)}°</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(labelPos);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `angle_${Date.now()}`,\n type: 'angle',\n value: angleDeg,\n formattedValue: `${angleDeg.toFixed(1)}°`,\n unit: '°',\n points: [p1, vertex, p2],\n extra: {\n angleDegree: angleDeg,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private addEndSphere(group: THREE.Group, pos: THREE.Vector3, color: number, pixelRadius: number = 5.5): void {\n const geom = new THREE.SphereGeometry(1, 16, 16);\n const mat = new THREE.MeshBasicMaterial({ color, depthTest: false });\n const sphere = new THREE.Mesh(geom, mat);\n sphere.position.copy(pos);\n sphere.userData = { fixedScreenRadius: pixelRadius };\n group.add(sphere);\n this.updateSingleMeshScale(sphere);\n }\n\n public undoLastPoint(): void {\n if (this.activePoints.length > 0) {\n this.activePoints.pop();\n if (this.activeMarkersGroup.children.length > 0) {\n const lastMarker = this.activeMarkersGroup.children[this.activeMarkersGroup.children.length - 1];\n this.disposeObjectHierarchy(lastMarker);\n this.activeMarkersGroup.remove(lastMarker);\n }\n this.clearActiveLine();\n this.emitter.emit('measure:change', [...this.activePoints]);\n }\n }\n\n public cancelActive(): void {\n this.activePoints = [];\n this.clearActiveLine();\n this.clearActiveMarkers();\n }\n\n private clearActiveLine(): void {\n while (this.activeLineGroup.children.length > 0) {\n const obj = this.activeLineGroup.children[0];\n this.disposeObjectHierarchy(obj);\n this.activeLineGroup.remove(obj);\n }\n }\n\n private clearActiveMarkers(): void {\n while (this.activeMarkersGroup.children.length > 0) {\n const obj = this.activeMarkersGroup.children[0];\n this.disposeObjectHierarchy(obj);\n this.activeMarkersGroup.remove(obj);\n }\n }\n\n /**\n * Updates scale of all marker spheres dynamically per frame so they maintain\n * a constant screen pixel size, completely independent of camera distance or model zoom!\n */\n public updatePerFrame(viewportHeight?: number): void {\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const updateMesh = (obj: THREE.Object3D) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n };\n\n if (this.snapCursorMesh && this.snapCursorMesh.visible) {\n updateMesh(this.snapCursorMesh);\n }\n\n this.activeMarkersGroup.traverse(updateMesh);\n this.measurementGroup.traverse(updateMesh);\n }\n\n private updateSingleMeshScale(obj: THREE.Object3D, viewportHeight?: number): void {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n }\n\n /**\n * Completely clears ALL measurements and CSS2D DOM elements\n */\n public clearAll(): void {\n this.cancelActive();\n\n while (this.measurementGroup.children.length > 0) {\n const group = this.measurementGroup.children[0];\n this.disposeObjectHierarchy(group);\n this.measurementGroup.remove(group);\n }\n\n this.snapCursorMesh.visible = false;\n this.results = [];\n this.emitter.emit('measure:clear');\n }\n\n private disposeObjectHierarchy(obj: THREE.Object3D): void {\n obj.traverse((child) => {\n // Clean up CSS2D HTML elements from DOM tree\n if ((child as any).isCSS2DObject && (child as CSS2DObject).element) {\n const el = (child as CSS2DObject).element;\n if (el && el.parentElement) {\n el.parentElement.removeChild(el);\n } else if (el) {\n el.remove();\n }\n }\n\n if ((child as THREE.Mesh).isMesh || (child as THREE.Line).isLine) {\n const item = child as THREE.Mesh;\n if (item.geometry) item.geometry.dispose();\n if (item.material) {\n if (Array.isArray(item.material)) item.material.forEach((m) => m.dispose());\n else item.material.dispose();\n }\n }\n });\n }\n\n public getResults(): MeasurementResult[] {\n return [...this.results];\n }\n\n public destroy(): void {\n this.removeListeners();\n this.clearAll();\n this.scene.remove(this.measurementGroup);\n this.scene.remove(this.activePreviewGroup);\n this.scene.remove(this.snapCursorMesh);\n }\n}\n","import * as THREE from 'three';\nimport { ExportImageOptions } from '../types';\n\nexport class ScreenshotTool {\n private renderer: THREE.WebGLRenderer;\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private getModel: () => THREE.Object3D | null;\n\n constructor(\n renderer: THREE.WebGLRenderer,\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n getModel?: () => THREE.Object3D | null\n ) {\n this.renderer = renderer;\n this.scene = scene;\n this.camera = camera;\n this.getModel = getModel || (() => null);\n }\n\n /**\n * Finds the maximum original texture resolution from meshes in the scene\n */\n public getSceneMaxTextureSize(): { width: number; height: number } | null {\n let maxWidth = 0;\n let maxHeight = 0;\n\n this.scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n const checkMat = (mat: THREE.Material) => {\n if ('map' in mat) {\n const m = mat as THREE.MeshStandardMaterial;\n if (m.map) {\n const img = (m.map.image || (m.map.source as unknown as { data?: { width?: number; height?: number } })?.data) as\n | { width?: number; height?: number }\n | undefined;\n if (img) {\n const w = img.width || 0;\n const h = img.height || 0;\n if (w > 0 && h > 0 && w * h > maxWidth * maxHeight) {\n maxWidth = w;\n maxHeight = h;\n }\n }\n }\n }\n };\n\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach(checkMat);\n } else if (mesh.material) {\n checkMat(mesh.material);\n }\n }\n });\n\n if (maxWidth > 0 && maxHeight > 0) {\n return { width: maxWidth, height: maxHeight };\n }\n return null;\n }\n\n /**\n * Exports high-definition screenshot of the current view.\n * Supports 'original' / originalQuality which aligns directly to the model's actual texture resolution!\n */\n public async exportImage(options: ExportImageOptions = {}): Promise<string> {\n const isOriginal = options.originalQuality === true || options.scale === 'original';\n const scale = isOriginal ? 1 : (typeof options.scale === 'number' ? options.scale : 2);\n const type = options.type ?? 'image/png';\n const quality = options.quality ?? (type === 'image/png' ? 1 : 0.95);\n const download = options.download ?? false;\n\n // 1. Record original dimensions\n const originalSize = new THREE.Vector2();\n this.renderer.getSize(originalSize);\n const originalPixelRatio = this.renderer.getPixelRatio();\n const originalBackground = this.scene.background;\n\n const aspect = originalSize.x / originalSize.y;\n let targetWidth: number;\n let targetHeight: number;\n\n if (options.targetSize) {\n targetWidth = options.targetSize.width;\n targetHeight = options.targetSize.height;\n } else if (isOriginal) {\n // 原画质导出:按照模型实际贴图的真实原始尺寸计算输出分辨率\n const texSize = this.getSceneMaxTextureSize();\n if (texSize && texSize.width > 0 && texSize.height > 0) {\n const maxDim = Math.max(texSize.width, texSize.height);\n if (aspect >= 1) {\n targetWidth = maxDim;\n targetHeight = Math.round(maxDim / aspect);\n } else {\n targetHeight = maxDim;\n targetWidth = Math.round(maxDim * aspect);\n }\n } else {\n // 若无贴图,回退至高质量分辨率 (至少 2048)\n const baseDim = Math.max(Math.round(originalSize.x * originalPixelRatio), 2048);\n if (aspect >= 1) {\n targetWidth = baseDim;\n targetHeight = Math.round(baseDim / aspect);\n } else {\n targetHeight = baseDim;\n targetWidth = Math.round(baseDim * aspect);\n }\n }\n } else {\n targetWidth = Math.round(originalSize.x * scale);\n targetHeight = Math.round(originalSize.y * scale);\n }\n\n const filename = options.filename ?? `model-${isOriginal ? `texture-original-${targetWidth}x${targetHeight}` : `${scale}x`}-${Date.now()}.${type === 'image/jpeg' ? 'jpg' : 'png'}`;\n const modelOnly = options.modelOnly ?? true;\n\n // Track objects hidden during export\n const hiddenObjects: { obj: THREE.Object3D; wasVisible: boolean }[] = [];\n\n // Apply custom background if specified\n if (options.background !== undefined) {\n if (options.background === null) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else {\n this.scene.background = new THREE.Color(options.background);\n this.renderer.setClearAlpha(1);\n }\n }\n\n try {\n // If modelOnly is requested (default: true), hide all auxiliary scene objects (grid, bbox, measurements, clipping planes/helpers)\n if (modelOnly) {\n const currentModel = this.getModel();\n const isKeep = (obj: THREE.Object3D): boolean => {\n // Keep all lights and light groups so model illumination is preserved\n if ((obj as any).isLight) return true;\n if (obj.name === 'gmv-lights-group') return true;\n if (obj.children && obj.children.some((c: any) => c.isLight)) return true;\n\n // Keep the current model\n if (currentModel && (obj === currentModel || obj.uuid === currentModel.uuid)) return true;\n if (obj.name === 'gmv-current-model') return true;\n\n // Hide all other scene-level helpers/measurements/grids/planes\n return false;\n };\n\n for (const child of this.scene.children) {\n if (!isKeep(child) && child.visible) {\n hiddenObjects.push({ obj: child, wasVisible: true });\n child.visible = false;\n }\n }\n }\n\n this.renderer.setPixelRatio(1);\n this.renderer.setSize(targetWidth, targetHeight, false);\n this.camera.aspect = targetWidth / targetHeight;\n this.camera.updateProjectionMatrix();\n\n // Scale any fixed-screen-size markers proportionally to the target high-res height\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n this.scene.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(targetHeight, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n\n // Render crisp frame\n this.renderer.render(this.scene, this.camera);\n\n // Extract data URL\n const dataUrl = this.renderer.domElement.toDataURL(type, quality);\n\n // 3. Trigger download if requested\n if (download) {\n const link = document.createElement('a');\n link.download = filename;\n link.href = dataUrl;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n\n return dataUrl;\n } finally {\n // Restore any auxiliary objects that were hidden for model-only export\n if (hiddenObjects.length > 0) {\n for (const { obj, wasVisible } of hiddenObjects) {\n obj.visible = wasVisible;\n }\n }\n\n // 4. Restore original renderer and camera setup\n this.scene.background = originalBackground;\n this.renderer.setPixelRatio(originalPixelRatio);\n this.renderer.setSize(originalSize.x, originalSize.y, true);\n this.camera.aspect = originalSize.x / originalSize.y;\n this.camera.updateProjectionMatrix();\n\n // Restore markers for original screen height\n const origH = originalSize.y;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n this.scene.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(origH, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n\n this.renderer.render(this.scene, this.camera);\n }\n }\n}\n","import * as THREE from 'three';\nimport { SceneManager } from './SceneManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { BoundingBoxTool } from '../tools/BoundingBoxTool';\nimport { CameraTool } from '../tools/CameraTool';\nimport { ClippingTool } from '../tools/ClippingTool';\nimport { MeasurementTool } from '../tools/MeasurementTool';\nimport { ScreenshotTool } from '../tools/ScreenshotTool';\nimport {\n ViewerOptions,\n ModelLoadResult,\n MeasurementType,\n MeasurementResult,\n ClippingAxis,\n CameraView,\n ExportImageOptions,\n BoundingBoxOptions,\n ViewerEvents,\n AutoRotateAxis,\n AutoRotateDirection,\n AutoRotateOptions,\n RotationLockAxis,\n ModelDisplayStyle,\n} from '../types';\n\nexport class Viewer extends EventEmitter {\n public sceneManager: SceneManager;\n public modelLoader: ModelLoader;\n public boundingBoxTool: BoundingBoxTool;\n public cameraTool: CameraTool;\n public clippingTool: ClippingTool;\n public measurementTool: MeasurementTool;\n public screenshotTool: ScreenshotTool;\n\n public currentModel: THREE.Group | null = null;\n public currentModelInfo: ModelLoadResult | null = null;\n private unit: string = 'm';\n private defaultShowBoundingBox: boolean = false;\n\n constructor(options: ViewerOptions) {\n super();\n\n this.unit = options.unit || 'm';\n this.defaultShowBoundingBox = options.showBoundingBox ?? false;\n\n // 1. Core Scene Manager\n this.sceneManager = new SceneManager(options);\n\n // 2. Model Loader\n this.modelLoader = new ModelLoader();\n\n // 3. Tools\n this.boundingBoxTool = new BoundingBoxTool(this.sceneManager.scene);\n this.cameraTool = new CameraTool(\n this.sceneManager.camera,\n this.sceneManager.controls,\n this.sceneManager.tweenGroup\n );\n this.clippingTool = new ClippingTool(\n this.sceneManager.scene,\n this.sceneManager.camera,\n this.sceneManager.container,\n this.modelLoader,\n this.sceneManager.controls,\n this\n );\n this.measurementTool = new MeasurementTool(\n this.sceneManager.scene,\n this.sceneManager.camera,\n this.sceneManager.container,\n this.modelLoader,\n this,\n this.sceneManager.controls\n );\n this.measurementTool.setUnit(this.unit);\n\n this.screenshotTool = new ScreenshotTool(\n this.sceneManager.renderer,\n this.sceneManager.scene,\n this.sceneManager.camera,\n () => this.currentModel\n );\n\n // Forward internal measurement events\n this.measurementTool.setUnit(this.unit);\n\n // Dynamic per-frame updates: track bounding box edges, fixed-size markers, and auto-rotation\n this.sceneManager.onBeforeRender = () => {\n this.boundingBoxTool.update(this.sceneManager.camera);\n this.cameraTool.updatePerFrame();\n this.measurementTool.updatePerFrame();\n this.clippingTool.updatePerFrame();\n };\n }\n\n // ==========================================\n // Model Loading\n // ==========================================\n\n /**\n * Load model from a ZIP file containing .obj, .mtl and textures\n */\n public async loadZip(source: File | Blob | ArrayBuffer | string): Promise<ModelLoadResult> {\n try {\n this.emit('load:start', typeof source === 'string' ? source : 'ZIP File');\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadZip(source, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Load model directly from OBJ URL and optional MTL URL\n */\n public async loadOBJ(objUrl: string, mtlUrl?: string): Promise<ModelLoadResult> {\n try {\n this.emit('load:start', objUrl);\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadOBJ(objUrl, mtlUrl, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Loads a built-in sample geometry (convenient for immediate testing and demo playground)\n */\n public loadSampleGeometry(type: 'mechanical' | 'building' | 'torusKnot' = 'mechanical'): ModelLoadResult {\n this.clearModel();\n const group = new THREE.Group();\n\n if (type === 'mechanical') {\n // Create a mechanical-like assembly\n const baseGeom = new THREE.BoxGeometry(6, 1.2, 4);\n const baseMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });\n const baseMesh = new THREE.Mesh(baseGeom, baseMat);\n baseMesh.position.y = 0.6;\n group.add(baseMesh);\n\n const cylinderGeom = new THREE.CylinderGeometry(1.2, 1.2, 3, 32);\n const cylinderMat = new THREE.MeshStandardMaterial({ color: 0x0284c7, metalness: 0.6, roughness: 0.2 });\n const cylMesh = new THREE.Mesh(cylinderGeom, cylinderMat);\n cylMesh.position.set(0, 2.7, 0);\n group.add(cylMesh);\n\n const ringGeom = new THREE.TorusGeometry(1.6, 0.25, 16, 40);\n const ringMat = new THREE.MeshStandardMaterial({ color: 0xe11d48, metalness: 0.7, roughness: 0.2 });\n const ringMesh = new THREE.Mesh(ringGeom, ringMat);\n ringMesh.rotation.x = Math.PI / 2;\n ringMesh.position.set(0, 3.2, 0);\n group.add(ringMesh);\n } else if (type === 'building') {\n const bldgGeom = new THREE.BoxGeometry(4, 10, 4);\n const bldgMat = new THREE.MeshStandardMaterial({ color: 0x334155, roughness: 0.4 });\n const bldg = new THREE.Mesh(bldgGeom, bldgMat);\n bldg.position.y = 5;\n group.add(bldg);\n\n const roofGeom = new THREE.ConeGeometry(3, 2.5, 4);\n const roofMat = new THREE.MeshStandardMaterial({ color: 0xb91c1c, roughness: 0.5 });\n const roof = new THREE.Mesh(roofGeom, roofMat);\n roof.position.y = 11.25;\n roof.rotation.y = Math.PI / 4;\n group.add(roof);\n } else {\n const torusGeom = new THREE.TorusKnotGeometry(2.5, 0.7, 100, 16);\n const torusMat = new THREE.MeshStandardMaterial({ color: 0x8b5cf6, metalness: 0.5, roughness: 0.2 });\n const mesh = new THREE.Mesh(torusGeom, torusMat);\n mesh.position.y = 3;\n group.add(mesh);\n }\n\n const postProcessed = this.modelLoader.postProcessModel(group);\n this.setupLoadedModel(postProcessed);\n this.emit('load:success', postProcessed);\n return postProcessed;\n }\n\n private setupLoadedModel(result: ModelLoadResult): void {\n this.currentModel = result.model;\n this.currentModel.name = 'gmv-current-model';\n this.currentModelInfo = result;\n\n this.sceneManager.scene.add(this.currentModel);\n\n // Attach dynamic dimensional bounding box (default: hidden)\n this.boundingBoxTool.attachToModel(this.currentModel, {\n visible: this.defaultShowBoundingBox,\n unit: this.unit,\n });\n\n // Feed bounds to clipping tool\n this.clippingTool.setModelBounds(result.box);\n\n // Default view when model is loaded: Front View (前视图)\n this.cameraTool.setView('front', this.currentModel, 0);\n }\n\n public clearModel(): void {\n this.measurementTool.clearAll();\n this.clippingTool.resetAll();\n this.boundingBoxTool.detach();\n\n if (this.currentModel) {\n this.sceneManager.scene.remove(this.currentModel);\n this.currentModel = null;\n }\n this.currentModelInfo = null;\n this.modelLoader.cleanup();\n }\n\n // ==========================================\n // Appearance & Background\n // ==========================================\n\n public setBackground(color: string | number | null, alpha?: boolean): void {\n this.sceneManager.setBackground(color, alpha);\n }\n\n /**\n * Set model display style: 'solid' (实体), 'wireframe' (网格线框)\n */\n public setModelDisplayStyle(style: ModelDisplayStyle): void {\n this.modelLoader.setDisplayStyle(style);\n this.emit('style:change', style);\n }\n\n public getModelDisplayStyle(): ModelDisplayStyle {\n return this.modelLoader.getDisplayStyle();\n }\n\n public toggleTexture(showTexture: boolean): void {\n this.modelLoader.toggleTexture(showTexture);\n this.emit('texture:toggle', showTexture);\n }\n\n public getTextureState(): boolean {\n return this.modelLoader.getTextureState();\n }\n\n /**\n * Set color for model when texture is turned off (defaults to Digital3DCloud CAD blue 0x4c8cd6)\n */\n public setPlainColor(color: THREE.ColorRepresentation): void {\n this.modelLoader.setPlainColor(color);\n }\n\n public getPlainColor(): number {\n return this.modelLoader.getPlainColor();\n }\n\n /**\n * Retrieves the maximum original texture resolution of the current model\n */\n public getModelTextureSize(): { width: number; height: number } | null {\n return this.modelLoader.getModelTextureSize();\n }\n\n public setGridVisible(visible: boolean): void {\n this.sceneManager.gridHelper.visible = visible;\n }\n\n // ==========================================\n // Bounding Box & Dimensions (L, W, H)\n // ==========================================\n\n public showBoundingBox(visible: boolean, options?: Partial<BoundingBoxOptions>): void {\n this.boundingBoxTool.setVisible(visible);\n if (options?.unit) {\n this.unit = options.unit;\n this.boundingBoxTool.setUnit(options.unit);\n this.measurementTool.setUnit(options.unit);\n }\n }\n\n public getBoundingBoxVisible(): boolean {\n return this.boundingBoxTool.getVisible();\n }\n\n // ==========================================\n // Slicing / Clipping Tools\n // ==========================================\n\n public setClipping(axis: ClippingAxis, progress: number, inverted: boolean = false): void {\n this.clippingTool.setAxisClipping(axis, progress, inverted);\n }\n\n public disableClippingAxis(axis: ClippingAxis): void {\n this.clippingTool.disableAxis(axis);\n }\n\n public toggleClippingAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n return this.clippingTool.toggleAxis(axis, enabled);\n }\n\n public invertClippingAxis(axis: ClippingAxis): boolean {\n return this.clippingTool.invertAxis(axis);\n }\n\n public getClippingAxisState(axis: ClippingAxis) {\n return this.clippingTool.getAxisState(axis);\n }\n\n public setClippingPlanesVisible(visible: boolean): void {\n this.clippingTool.setPlanesVisible(visible);\n }\n\n public getClippingPlanesVisible(): boolean {\n return this.clippingTool.getPlanesVisible();\n }\n\n // Digital3DCloud Standard Multi-Axis Sectioning API\n public setSectioningEnabled(enabled: boolean): void {\n this.clippingTool.setSectioningEnabled(enabled);\n }\n\n public getSectioningEnabled(): boolean {\n return this.clippingTool.getSectioningEnabled();\n }\n\n public setSectioningAxis(axis: ClippingAxis): void {\n this.clippingTool.setSectioningAxis(axis);\n }\n\n public getSectioningAxis(): ClippingAxis {\n return this.clippingTool.getSectioningAxis();\n }\n\n public setSectioningAxisEnabled(axis: ClippingAxis, enabled: boolean): void {\n this.clippingTool.setAxisEnabled(axis, enabled);\n }\n\n public toggleSectioningAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n return this.clippingTool.toggleAxis(axis, enabled);\n }\n\n public isSectioningAxisEnabled(axis: ClippingAxis): boolean {\n return this.clippingTool.isAxisEnabled(axis);\n }\n\n public setSectioningAxisReverse(axis: ClippingAxis, reverse: boolean): void {\n this.clippingTool.setAxisReverse(axis, reverse);\n }\n\n public invertSectioningAxis(axis: ClippingAxis): boolean {\n return this.clippingTool.invertAxis(axis);\n }\n\n public setSectioningAxisProgress(axis: ClippingAxis, progress: number): void {\n this.clippingTool.setAxisProgress(axis, progress);\n }\n\n public getSectioningAxisProgress(axis: ClippingAxis): number {\n return this.clippingTool.getAxisProgress(axis);\n }\n\n public setSectioningHelperVisible(visible: boolean): void {\n this.clippingTool.setSectioningHelperVisible(visible);\n }\n\n public getSectioningHelperVisible(): boolean {\n return this.clippingTool.getSectioningHelperVisible();\n }\n\n public setSectioningReverse(reverse: boolean): void {\n this.clippingTool.setSectioningReverse(reverse);\n }\n\n public getSectioningReverse(): boolean {\n return this.clippingTool.getSectioningReverse();\n }\n\n public resetSectioning(axis?: ClippingAxis): void {\n this.clippingTool.resetSectioning(axis);\n }\n\n public getSectioningState() {\n return this.clippingTool.getSectioningState();\n }\n\n public startCustomClip(): void {\n this.measurementTool.setMode('none'); // avoid mode collision\n this.clippingTool.startCustomClip();\n }\n\n public cancelCustomClip(): void {\n this.clippingTool.cancelCustomClip();\n }\n\n public clearCustomClip(): void {\n this.clippingTool.clearCustomPlane();\n }\n\n public invertCustomClip(): boolean {\n return this.clippingTool.invertCustomClip();\n }\n\n public hasCustomClip(): boolean {\n return this.clippingTool.hasCustomPlane();\n }\n\n public resetClipping(): void {\n this.clippingTool.resetAll();\n }\n\n // ==========================================\n // Measurement Tools\n // ==========================================\n\n public setMeasurementMode(mode: MeasurementType): void {\n if (mode !== 'none') {\n this.clippingTool.cancelCustomClip();\n this.clippingTool.setInteractive(false);\n } else {\n this.clippingTool.setInteractive(true);\n }\n this.measurementTool.setMode(mode);\n }\n\n public getMeasurementMode(): MeasurementType {\n return this.measurementTool.getMode();\n }\n\n public clearMeasurements(): void {\n this.measurementTool.clearAll();\n }\n\n public undoMeasurement(): void {\n this.measurementTool.undoLastPoint();\n }\n\n public getMeasurements(): MeasurementResult[] {\n return this.measurementTool.getResults();\n }\n\n // ==========================================\n // Camera & Views\n // ==========================================\n\n public async setView(view: CameraView, duration: number = 600): Promise<void> {\n if (!this.currentModel) return;\n await this.cameraTool.setView(view, this.currentModel, duration);\n this.emit('view:change', view);\n }\n\n public async fitView(duration: number = 500): Promise<void> {\n if (!this.currentModel) return;\n await this.cameraTool.fitToObject(this.currentModel, duration);\n }\n\n // ==========================================\n // Auto-Rotation & Axis Locking\n // ==========================================\n\n public setAutoRotate(\n enabled: boolean,\n axis?: AutoRotateAxis,\n direction?: AutoRotateDirection,\n speed?: number\n ): void {\n this.cameraTool.setAutoRotate(enabled, axis, direction, speed);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public toggleAutoRotate(): boolean {\n const state = this.cameraTool.toggleAutoRotate();\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n return state;\n }\n\n public setAutoRotateAxis(axis: AutoRotateAxis): void {\n this.cameraTool.setAutoRotateAxis(axis);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public setAutoRotateDirection(direction: AutoRotateDirection): void {\n this.cameraTool.setAutoRotateDirection(direction);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public setAutoRotateSpeed(speed: number): void {\n this.cameraTool.setAutoRotateSpeed(speed);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public getAutoRotateState(): AutoRotateOptions {\n return this.cameraTool.getAutoRotateState();\n }\n\n public setRotationLockedAxis(axis: RotationLockAxis): void {\n this.cameraTool.setLockedAxis(axis);\n this.emit('axislock:change', axis);\n }\n\n public getRotationLockedAxis(): RotationLockAxis {\n return this.cameraTool.getLockedAxis();\n }\n\n // ==========================================\n // High-Resolution Export\n // ==========================================\n\n public async exportImage(options: ExportImageOptions = {}): Promise<string> {\n return this.screenshotTool.exportImage(options);\n }\n\n // Typed EventEmitter helper\n public override on<K extends keyof ViewerEvents>(event: K, listener: ViewerEvents[K]): this;\n public override on(event: string, listener: (...args: unknown[]) => void): this {\n return super.on(event, listener as (...args: unknown[]) => void);\n }\n\n public override off<K extends keyof ViewerEvents>(event: K, listener: ViewerEvents[K]): this;\n public override off(event: string, listener: (...args: unknown[]) => void): this {\n return super.off(event, listener as (...args: unknown[]) => void);\n }\n\n // ==========================================\n // Cleanup\n // ==========================================\n\n public destroy(): void {\n this.clearModel();\n this.measurementTool.destroy();\n this.clippingTool.destroy();\n this.sceneManager.destroy();\n this.removeAllListeners();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["THREE","CSS2DRenderer","OrbitControls","TrackballControls","TWEEN","JSZip","THREE","computeBoundsTree","disposeBoundsTree","acceleratedRaycast","DRACOLoader","KTX2Loader","THREE","GLTFLoader","MeshoptDecoder","MTLLoader","OBJLoader","THREE","THREE","CSS2DObject","TWEEN","THREE","THREE","THREE","CSS2DObject","THREE","CSS2DObject","THREE","THREE","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","three","three_addons_controls_OrbitControls_js","three_addons_controls_TrackballControls_js","three_addons_renderers_CSS2DRenderer_js","_tweenjs_tween_js","three_addons_loaders_OBJLoader_js","three_addons_loaders_MTLLoader_js","three_addons_loaders_GLTFLoader_js","three_addons_loaders_DRACOLoader_js","three_addons_loaders_KTX2Loader_js","three_addons_libs_meshopt_decoder_module_js","three_mesh_bvh","jszip","SceneManager","options","width","height","orbit","tb","hemiLight","ambientLight","dirLight1","dirLight2","headlight","animate","alpha","color","EventEmitter","event","listener","listeners","index","args","err","ZipExtractor","arrayBuffer","onProgress","source","response","zip","blobUrls","glbZipEntry","gltfZipEntry","objZipEntry","mtlZipEntry","imageExtensions","files","totalFiles","filename","entry","lower","baseName","currentPercent","blob","objectUrl","ext","format","url","objContent","objFileName","mtlContent","mtlFileName","gltfContent","gltfFileName","loadingManager","cleanUrl","lowerBase","blobUrl","ModelLoader","optionsOrDracoPath","renderer","path","canvas","tempRenderer","gltfLoader","zipContent","gltfData","gltf","resolve","result","reject","error","materialsCreator","objLoader","text","modelGroup","isGlb","xhr","p","buffer","mtlUrl","objUrl","fileList","zipFile","f","glbFile","gltfFile","file","gltfText","objFile","mtlFile","vertexCount","faceCount","enhanceTexture","tex","isColor","ensureVisibleMaterial","mat","m","group","initialBox","origCenter","origSize","child","mesh","geom","plainMat","managedItem","rootGroup","animations","curMat","item","setMatProps","style","showTexture","maxWidth","maxHeight","inspectMat","img","w","h","planes","applyToMat","computeMeshVolume","geometry","position","totalVolume","p1","p2","p3","matrixWorld","indices","i1","i2","i3","signedVolumeOfTriangle","computeGroupVolume","meshVolume","root","bbox","size","bboxVolume","compute3DPolygonArea","points","normal","current","next","formatUnit","value","unit","decimals","BoundingBoxTool","scene","model","box","invModelMatrix","relMatrix","meshBox","min","max","edgeIndices","boxGeom","posBuffer","lineMaterial","divX","divY","divZ","line","matrix","posAttr","ptr","idx","v","bestCorner","minDistance","camPos","camera","dist","cornerV","neighborX","neighborY","neighborZ","pos","visible","CameraTool","controls","tweenGroup","enabled","axis","direction","speed","offset","radius","dirSign","angle","rotAxis","currentOffset","curRadius","hRadius","targetY","angleXZ","curY","targetObject","sphere","center","fovRad","fitDistance","dir","up","view","targetCamPos","duration","startPos","startTarget","animData","object","currentDir","findSnapPoint","modelMeshes","raycaster","mouse","intersects","hit","hitPoint","faceNormal","closestVertex","minScreenDist","snapPixelThreshold","vIndices","vWorld","vScreen","screenX","viewportWidth","screenY","viewportHeight","mouseScreenX","mouseScreenY","screenDist","ClippingTool","container","modelLoader","emitter","interactive","sx","sy","sz","meshMat","baseColor","edges","borderMat","borderColor","border","arrowGroup","handleScale","arrowMat","sphereGeom","centerSphere","shaftGeom","shaftMesh","coneGeom1","coneMesh1","coneGeom2","coneMesh2","hitMeshes","div","label","widget","a","defaultW","newState","reverse","range","progress","isEmpty","minX","maxX","minY","maxY","minZ","maxZ","calcProg","wx","wy","wz","axes","activeW","curAxisProg","curAxisPos","curAxisInv","rect","candidateWidgets","closestHit","activeWidget","axisVector","p0","ndc0","screen0","ndc1","screen1","deltaMouse","deltaUnits","axisSpan","newPos","hoveredHitAxis","inverted","snap","point","markerGeom","markerMat","marker","cutLine","viewDir","vFovRad","tanHalfFov","obj","radiusPx","worldPos","worldRadius","activePlanes","MeasurementTool","snapGeom","snapMat","mode","clickedPoint","currentPoint","distance","midPoint","verticalHeight","horizontalDistance","totalDistance","directGeom","directMat","directLine","corner","hGeom","hMat","hLine","vGeom","vMat","vLine","vMid","area","loopPoints","lineGeom","lineMat","centroid","vertex","v1","v2","dot","angleRad","angleDeg","geom1","geom2","arcRadius","arcPoints","t","arcVec","arcGeom","arcMat","midDir","labelPos","pixelRadius","lastMarker","updateMesh","el","ScreenshotTool","getModel","checkMat","isOriginal","scale","type","quality","download","originalSize","originalPixelRatio","originalBackground","aspect","targetWidth","targetHeight","texSize","maxDim","baseDim","modelOnly","hiddenObjects","currentModel","isKeep","c","dataUrl","link","wasVisible","origH","Viewer","delta","percent","message","name","mainName","baseGeom","baseMat","baseMesh","cylinderGeom","cylinderMat","cylMesh","ringGeom","ringMat","ringMesh","bldgGeom","bldgMat","bldg","roofGeom","roofMat","roof","torusGeom","torusMat","postProcessed","state","clip","targetClip","nameOrIndex","newAction"],"sources":["../src/core/SceneManager.ts","../src/core/EventEmitter.ts","../src/loaders/ZipExtractor.ts","../src/loaders/ModelLoader.ts","../src/utils/math.ts","../src/tools/BoundingBoxTool.ts","../src/tools/CameraTool.ts","../src/utils/snapping.ts","../src/tools/ClippingTool.ts","../src/tools/MeasurementTool.ts","../src/tools/ScreenshotTool.ts","../src/core/Viewer.ts"],"sourcesContent":["import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';\nimport * as TWEEN from '@tweenjs/tween.js';\nimport { ViewerOptions } from '../types';\n\nexport class SceneManager {\n public container: HTMLElement;\n public scene: THREE.Scene;\n public camera: THREE.PerspectiveCamera;\n public renderer: THREE.WebGLRenderer;\n public labelRenderer: CSS2DRenderer;\n public controls: TrackballControls | OrbitControls;\n public lightsGroup: THREE.Group;\n public gridHelper: THREE.GridHelper;\n public tweenGroup: TWEEN.Group;\n public onBeforeRender?: () => void;\n\n private animationFrameId: number | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private isDestroyed: boolean = false;\n\n constructor(options: ViewerOptions) {\n this.container = options.container;\n\n // 1. Scene\n this.scene = new THREE.Scene();\n if (options.background !== undefined && options.background !== null) {\n if (typeof options.background === 'string' || typeof options.background === 'number') {\n this.scene.background = new THREE.Color(options.background);\n }\n } else {\n this.scene.background = new THREE.Color(0x181c24); // Modern dark slate\n }\n\n // 2. Camera\n const width = this.container.clientWidth || 800;\n const height = this.container.clientHeight || 600;\n this.camera = new THREE.PerspectiveCamera(\n options.cameraFov ?? 45,\n width / height,\n options.cameraNear ?? 0.1,\n options.cameraFar ?? 5000\n );\n this.camera.position.set(10, 10, 10);\n\n // 3. WebGLRenderer\n this.renderer = new THREE.WebGLRenderer({\n antialias: options.antialias ?? true,\n alpha: options.alpha ?? false,\n preserveDrawingBuffer: true,\n powerPreference: 'high-performance',\n });\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n this.renderer.localClippingEnabled = true; // Crucial for slicing tool\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping;\n this.renderer.toneMappingExposure = 1.0;\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.domElement.style.display = 'block';\n this.renderer.domElement.style.width = '100%';\n this.renderer.domElement.style.height = '100%';\n this.container.appendChild(this.renderer.domElement);\n\n // 4. CSS2D Label Renderer (for measurements & dimensions)\n this.labelRenderer = new CSS2DRenderer();\n this.labelRenderer.setSize(width, height);\n this.labelRenderer.domElement.style.position = 'absolute';\n this.labelRenderer.domElement.style.top = '0';\n this.labelRenderer.domElement.style.left = '0';\n this.labelRenderer.domElement.style.pointerEvents = 'none';\n this.labelRenderer.domElement.style.width = '100%';\n this.labelRenderer.domElement.style.height = '100%';\n this.labelRenderer.domElement.className = 'gmv-css2d-container';\n this.container.appendChild(this.labelRenderer.domElement);\n\n // 5. Controls: TrackballControls gives infinite unconstrained 360° rotation without pole limits\n const controlsType = options.controlsType ?? 'trackball';\n if (controlsType === 'orbit') {\n const orbit = new OrbitControls(this.camera, this.renderer.domElement);\n orbit.enableDamping = true;\n orbit.dampingFactor = 0.05;\n orbit.screenSpacePanning = true;\n this.controls = orbit;\n } else {\n const tb = new TrackballControls(this.camera, this.renderer.domElement);\n tb.rotateSpeed = 2.5;\n tb.zoomSpeed = 1.2;\n tb.panSpeed = 0.8;\n tb.staticMoving = false;\n tb.dynamicDampingFactor = 0.15;\n this.controls = tb;\n }\n\n // 6. Tween Group\n this.tweenGroup = new TWEEN.Group();\n\n // 7. Lighting System\n this.lightsGroup = new THREE.Group();\n this.lightsGroup.name = 'gmv-lights-group';\n this.initLights();\n this.scene.add(this.lightsGroup);\n\n // 8. Grid Helper\n this.gridHelper = new THREE.GridHelper(50, 50, 0x4a5568, 0x2d3748);\n this.gridHelper.name = 'gmv-grid-helper';\n this.gridHelper.position.y = 0;\n this.gridHelper.visible = false;\n this.scene.add(this.gridHelper);\n\n // 9. Event listeners & Resize\n this.initResizeObserver();\n this.startAnimationLoop();\n\n // Ensure immediate crisp sizing once DOM layout settles\n requestAnimationFrame(() => this.handleResize());\n setTimeout(() => this.handleResize(), 50);\n }\n\n private initLights(): void {\n // 1. Hemisphere Light & Ambient Light for all-around illumination\n const hemiLight = new THREE.HemisphereLight(0xffffff, 0x475569, 1.0);\n hemiLight.position.set(0, 50, 0);\n this.lightsGroup.add(hemiLight);\n\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);\n this.lightsGroup.add(ambientLight);\n\n // 2. Multi-angle Key/Fill lights\n const dirLight1 = new THREE.DirectionalLight(0xffffff, 1.2);\n dirLight1.position.set(50, 100, 50);\n this.lightsGroup.add(dirLight1);\n\n const dirLight2 = new THREE.DirectionalLight(0xffffff, 0.7);\n dirLight2.position.set(-50, 50, -50);\n this.lightsGroup.add(dirLight2);\n\n // 3. Headlight attached to camera (always illuminates the view from camera!)\n const headlight = new THREE.DirectionalLight(0xffffff, 0.9);\n headlight.position.set(0, 0, 1);\n this.camera.add(headlight);\n this.scene.add(this.camera);\n }\n\n private initResizeObserver(): void {\n this.resizeObserver = new ResizeObserver(() => {\n this.handleResize();\n });\n this.resizeObserver.observe(this.container);\n }\n\n public handleResize(): void {\n if (this.isDestroyed || !this.container) return;\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n\n if (width === 0 || height === 0) return;\n\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n\n this.renderer.setSize(width, height);\n this.labelRenderer.setSize(width, height);\n\n if ('handleResize' in this.controls && typeof (this.controls as unknown as { handleResize: () => void }).handleResize === 'function') {\n (this.controls as unknown as { handleResize: () => void }).handleResize();\n }\n }\n\n private startAnimationLoop(): void {\n const animate = () => {\n if (this.isDestroyed) return;\n\n this.animationFrameId = requestAnimationFrame(animate);\n\n // Update Tween animations\n this.tweenGroup.update();\n\n // Update Controls\n this.controls.update();\n\n // Hook before render (e.g. dynamic bounding box edge selection)\n if (this.onBeforeRender) {\n this.onBeforeRender();\n }\n\n // Render 3D and 2D Label Scenes\n this.renderer.render(this.scene, this.camera);\n this.labelRenderer.render(this.scene, this.camera);\n };\n\n this.animationFrameId = requestAnimationFrame(animate);\n }\n\n public setBackground(color: string | number | null, alpha?: boolean): void {\n if (alpha) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else if (color === null || color === undefined) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else {\n this.scene.background = new THREE.Color(color);\n this.renderer.setClearAlpha(1);\n }\n }\n\n public destroy(): void {\n this.isDestroyed = true;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n }\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n this.controls.dispose();\n this.renderer.dispose();\n\n if (this.renderer.domElement.parentElement) {\n this.renderer.domElement.parentElement.removeChild(this.renderer.domElement);\n }\n if (this.labelRenderer.domElement.parentElement) {\n this.labelRenderer.domElement.parentElement.removeChild(this.labelRenderer.domElement);\n }\n }\n}\n","export type Listener<T = unknown> = (...args: T[]) => void;\n\nexport class EventEmitter {\n private events: Map<string, Listener[]>;\n\n constructor() {\n this.events = new Map();\n }\n\n public on(event: string, listener: Listener): this {\n if (!this.events.has(event)) {\n this.events.set(event, []);\n }\n this.events.get(event)!.push(listener);\n return this;\n }\n\n public off(event: string, listener: Listener): this {\n const listeners = this.events.get(event);\n if (!listeners) return this;\n\n const index = listeners.indexOf(listener);\n if (index !== -1) {\n listeners.splice(index, 1);\n }\n return this;\n }\n\n public emit(event: string, ...args: unknown[]): boolean {\n const listeners = this.events.get(event);\n if (!listeners || listeners.length === 0) return false;\n\n // Clone array in case listener removes itself\n [...listeners].forEach((listener) => {\n try {\n listener(...args);\n } catch (err) {\n console.error(`Error in event listener for \"${event}\":`, err);\n }\n });\n\n return true;\n }\n\n public removeAllListeners(event?: string): this {\n if (event) {\n this.events.delete(event);\n } else {\n this.events.clear();\n }\n return this;\n }\n}\n","import JSZip from 'jszip';\nimport * as THREE from 'three';\n\nexport interface ExtractedZipContent {\n format: 'obj' | 'gltf' | 'glb';\n objContent?: string | ArrayBuffer;\n objFileName?: string;\n mtlContent?: string | null;\n mtlFileName?: string | null;\n gltfContent?: string | ArrayBuffer;\n gltfFileName?: string;\n blobUrls: Map<string, string>;\n loadingManager: THREE.LoadingManager;\n dispose: () => void;\n}\n\nexport type ProgressCallback = (percent: number, message: string) => void;\n\nexport class ZipExtractor {\n public static async extract(\n source: File | Blob | ArrayBuffer | string,\n onProgress?: ProgressCallback\n ): Promise<ExtractedZipContent> {\n let arrayBuffer: ArrayBuffer;\n\n onProgress?.(5, '正在读取压缩包文件流...');\n\n if (typeof source === 'string') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch zip file: ${response.statusText}`);\n }\n arrayBuffer = await response.arrayBuffer();\n } else if (source instanceof Blob || source instanceof File) {\n arrayBuffer = await source.arrayBuffer();\n } else {\n arrayBuffer = source;\n }\n\n onProgress?.(15, '正在解压 ZIP 文件包...');\n const zip = await JSZip.loadAsync(arrayBuffer);\n const blobUrls = new Map<string, string>();\n\n let glbZipEntry: JSZip.JSZipObject | null = null;\n let gltfZipEntry: JSZip.JSZipObject | null = null;\n let objZipEntry: JSZip.JSZipObject | null = null;\n let mtlZipEntry: JSZip.JSZipObject | null = null;\n\n const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.bmp', '.tga', '.dds', '.hdr', '.svg', '.ktx2'];\n\n // 1. Identify files in ZIP\n const files = Object.keys(zip.files);\n const totalFiles = files.length || 1;\n\n for (let i = 0; i < files.length; i++) {\n const filename = files[i];\n const entry = zip.files[filename];\n if (entry.dir) continue;\n\n // Filter out Mac / system hidden files\n if (filename.includes('__MACOSX') || filename.split('/').pop()?.startsWith('._')) {\n continue;\n }\n\n const lower = filename.toLowerCase();\n const baseName = filename.split('/').pop() || filename;\n const currentPercent = Math.round(20 + (i / totalFiles) * 35); // 20% -> 55%\n\n // Model formats detection (GLB / GLTF / OBJ / MTL)\n if (lower.endsWith('.glb') && !glbZipEntry) {\n glbZipEntry = entry;\n onProgress?.(currentPercent, `找到 GLB 二进制模型: ${baseName}`);\n } else if (lower.endsWith('.gltf') && !gltfZipEntry) {\n gltfZipEntry = entry;\n onProgress?.(currentPercent, `找到 GLTF 场景定义: ${baseName}`);\n } else if (lower.endsWith('.obj') && !objZipEntry) {\n objZipEntry = entry;\n onProgress?.(currentPercent, `找到 OBJ 网格模型: ${baseName}`);\n } else if (lower.endsWith('.mtl') && !mtlZipEntry) {\n mtlZipEntry = entry;\n onProgress?.(currentPercent, `找到 MTL 材质定义: ${baseName}`);\n }\n\n // Buffer bin files for GLTF\n if (lower.endsWith('.bin')) {\n onProgress?.(currentPercent, `加载二进制缓冲区: ${baseName}`);\n const blob = await entry.async('blob');\n const objectUrl = URL.createObjectURL(blob);\n blobUrls.set(filename, objectUrl);\n blobUrls.set(baseName, objectUrl);\n blobUrls.set(baseName.toLowerCase(), objectUrl);\n }\n\n // Check for image textures\n const isImage = imageExtensions.some((ext) => lower.endsWith(ext));\n if (isImage) {\n onProgress?.(currentPercent, `加载贴图资源: ${baseName}`);\n const blob = await entry.async('blob');\n const objectUrl = URL.createObjectURL(blob);\n blobUrls.set(filename, objectUrl);\n blobUrls.set(baseName, objectUrl);\n blobUrls.set(baseName.toLowerCase(), objectUrl);\n }\n }\n\n onProgress?.(60, 'ZIP 资源索引提取完毕,准备解析三维模型网格...');\n\n // Priority: GLB > GLTF > OBJ\n let format: 'obj' | 'gltf' | 'glb';\n if (glbZipEntry) {\n format = 'glb';\n } else if (gltfZipEntry) {\n format = 'gltf';\n } else if (objZipEntry) {\n format = 'obj';\n } else {\n // Clean up created URLs if no model found\n blobUrls.forEach((url) => URL.revokeObjectURL(url));\n throw new Error('ZIP 压缩包中未检测到支持的 3D 模型文件(需要包含 .glb, .gltf 或 .obj 文件)');\n }\n\n let objContent: string | ArrayBuffer | undefined;\n let objFileName: string | undefined;\n let mtlContent: string | null = null;\n let mtlFileName: string | null = null;\n let gltfContent: string | ArrayBuffer | undefined;\n let gltfFileName: string | undefined;\n\n if (format === 'glb') {\n gltfContent = await glbZipEntry!.async('arraybuffer');\n gltfFileName = glbZipEntry!.name;\n } else if (format === 'gltf') {\n gltfContent = await gltfZipEntry!.async('string');\n gltfFileName = gltfZipEntry!.name;\n } else {\n objContent = await objZipEntry!.async('string');\n objFileName = objZipEntry!.name;\n if (mtlZipEntry) {\n mtlContent = await mtlZipEntry.async('string');\n mtlFileName = mtlZipEntry.name;\n }\n }\n\n // Setup Three.js LoadingManager to intercept and resolve binary/texture URLs\n const loadingManager = new THREE.LoadingManager();\n loadingManager.setURLModifier((url: string) => {\n const cleanUrl = decodeURIComponent(url).replace(/^\\.\\//, '');\n const baseName = cleanUrl.split('/').pop()?.split('\\\\').pop() || cleanUrl;\n\n if (blobUrls.has(url)) return blobUrls.get(url)!;\n if (blobUrls.has(cleanUrl)) return blobUrls.get(cleanUrl)!;\n if (blobUrls.has(baseName)) return blobUrls.get(baseName)!;\n\n // Case-insensitive fallback\n const lowerBase = baseName.toLowerCase();\n for (const [key, blobUrl] of blobUrls.entries()) {\n if (key.toLowerCase() === lowerBase || key.toLowerCase().endsWith('/' + lowerBase)) {\n return blobUrl;\n }\n }\n\n return url;\n });\n\n const dispose = () => {\n blobUrls.forEach((url) => {\n try {\n URL.revokeObjectURL(url);\n } catch {\n // ignore\n }\n });\n blobUrls.clear();\n };\n\n return {\n format,\n objContent,\n objFileName,\n mtlContent,\n mtlFileName,\n gltfContent,\n gltfFileName,\n blobUrls,\n loadingManager,\n dispose,\n };\n }\n}\n","import * as THREE from 'three';\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js';\nimport { MTLLoader } from 'three/addons/loaders/MTLLoader.js';\nimport { GLTFLoader, GLTF } from 'three/addons/loaders/GLTFLoader.js';\nimport { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';\nimport { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';\nimport { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';\nimport { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';\nimport { ZipExtractor, ExtractedZipContent } from './ZipExtractor';\nimport { ModelLoadResult, ModelDisplayStyle } from '../types';\n\n// Setup three-mesh-bvh acceleration\n(THREE.BufferGeometry.prototype as unknown as { computeBoundsTree: typeof computeBoundsTree }).computeBoundsTree = computeBoundsTree;\n(THREE.BufferGeometry.prototype as unknown as { disposeBoundsTree: typeof disposeBoundsTree }).disposeBoundsTree = disposeBoundsTree;\nTHREE.Mesh.prototype.raycast = acceleratedRaycast;\n\nexport interface ManagedMeshMaterial {\n mesh: THREE.Mesh;\n originalMaterial: THREE.Material | THREE.Material[];\n plainMaterial: THREE.Material;\n}\n\nexport interface ModelLoaderOptions {\n dracoDecoderPath?: string;\n ktx2TranscoderPath?: string;\n renderer?: THREE.WebGLRenderer;\n}\n\nexport class ModelLoader {\n private currentZipContent: ExtractedZipContent | null = null;\n private customBlobUrls: string[] = [];\n private managedMaterials: ManagedMeshMaterial[] = [];\n private isTextureEnabled: boolean = true;\n private displayStyle: ModelDisplayStyle = 'solid';\n private plainColor: number = 0x4c8cd6; // Digital3DCloud / 先临三维标准数模扫描蓝\n private currentClippingPlanes: THREE.Plane[] = [];\n\n // Draco & Meshopt Compression Configurations\n private dracoDecoderPath: string = 'https://www.gstatic.com/draco/versioned/decoders/1.5.7/';\n private dracoLoader: DRACOLoader | null = null;\n\n // KTX2 / Basis Universal Texture Compression Configuration\n private ktx2TranscoderPath: string = 'https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/libs/basis/';\n private ktx2Loader: KTX2Loader | null = null;\n private renderer: THREE.WebGLRenderer | null = null;\n\n constructor(optionsOrDracoPath?: string | ModelLoaderOptions, renderer?: THREE.WebGLRenderer) {\n if (typeof optionsOrDracoPath === 'string') {\n this.dracoDecoderPath = optionsOrDracoPath;\n if (renderer) this.renderer = renderer;\n } else if (typeof optionsOrDracoPath === 'object' && optionsOrDracoPath !== null) {\n if (optionsOrDracoPath.dracoDecoderPath) {\n this.dracoDecoderPath = optionsOrDracoPath.dracoDecoderPath;\n }\n if (optionsOrDracoPath.ktx2TranscoderPath) {\n this.ktx2TranscoderPath = optionsOrDracoPath.ktx2TranscoderPath;\n }\n this.renderer = optionsOrDracoPath.renderer || renderer || null;\n }\n }\n\n /**\n * Set WebGLRenderer instance to allow KTX2Loader to query supported compressed texture formats\n */\n public setRenderer(renderer: THREE.WebGLRenderer): void {\n this.renderer = renderer;\n if (this.ktx2Loader) {\n this.ktx2Loader.detectSupport(renderer);\n }\n }\n\n /**\n * Set custom Draco decoder directory URL\n */\n public setDracoDecoderPath(path: string): void {\n this.dracoDecoderPath = path;\n if (this.dracoLoader) {\n this.dracoLoader.setDecoderPath(path);\n }\n }\n\n /**\n * Set custom KTX2 / Basis transcoder directory URL\n */\n public setKTX2TranscoderPath(path: string): void {\n this.ktx2TranscoderPath = path;\n if (this.ktx2Loader) {\n this.ktx2Loader.setTranscoderPath(path);\n }\n }\n\n /**\n * Internal helper to create or retrieve cached DRACOLoader\n */\n private getDracoLoader(): DRACOLoader {\n if (!this.dracoLoader) {\n this.dracoLoader = new DRACOLoader();\n this.dracoLoader.setDecoderPath(this.dracoDecoderPath);\n }\n return this.dracoLoader;\n }\n\n /**\n * Internal helper to create or retrieve cached KTX2Loader\n */\n private getKTX2Loader(): KTX2Loader {\n if (!this.ktx2Loader) {\n this.ktx2Loader = new KTX2Loader();\n this.ktx2Loader.setTranscoderPath(this.ktx2TranscoderPath);\n if (this.renderer) {\n this.ktx2Loader.detectSupport(this.renderer);\n } else if (typeof document !== 'undefined') {\n try {\n const canvas = document.createElement('canvas');\n const tempRenderer = new THREE.WebGLRenderer({ canvas });\n this.ktx2Loader.detectSupport(tempRenderer);\n tempRenderer.dispose();\n } catch (e) {\n console.warn('KTX2Loader: Failed to auto-detect WebGL compressed texture support', e);\n }\n }\n }\n return this.ktx2Loader;\n }\n\n /**\n * Internal helper to construct configured GLTFLoader\n */\n private createGLTFLoader(loadingManager?: THREE.LoadingManager): GLTFLoader {\n const gltfLoader = new GLTFLoader(loadingManager);\n gltfLoader.setDRACOLoader(this.getDracoLoader());\n gltfLoader.setKTX2Loader(this.getKTX2Loader());\n gltfLoader.setMeshoptDecoder(MeshoptDecoder);\n return gltfLoader;\n }\n\n /**\n * Load model from a ZIP file containing .glb, .gltf, or .obj + textures\n */\n public async loadZip(\n source: File | Blob | ArrayBuffer | string,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n this.cleanup();\n\n const zipContent = await ZipExtractor.extract(source, onProgress);\n this.currentZipContent = zipContent;\n\n if (zipContent.format === 'glb' || zipContent.format === 'gltf') {\n onProgress?.(70, '正在解析 GLTF/GLB 网格结构与材质贴图...');\n const gltfLoader = this.createGLTFLoader(zipContent.loadingManager);\n const gltfData = zipContent.gltfContent!;\n\n const gltf: GLTF = await new Promise((resolve, reject) => {\n gltfLoader.parse(\n gltfData,\n '',\n (result) => resolve(result),\n (error) => reject(error)\n );\n });\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(gltf.scene, gltf.animations, zipContent.format);\n\n onProgress?.(100, '加载完成');\n return result;\n }\n\n // Default: OBJ format in ZIP\n const { objContent, mtlContent, loadingManager } = zipContent;\n\n onProgress?.(65, '正在解析材质与贴图数据...');\n let materialsCreator: MTLLoader.MaterialCreator | null = null;\n if (mtlContent) {\n const mtlLoader = new MTLLoader(loadingManager);\n materialsCreator = mtlLoader.parse(mtlContent, '');\n materialsCreator.preload();\n }\n\n onProgress?.(75, '正在解析 OBJ 网格几何数据...');\n const objLoader = new OBJLoader(loadingManager);\n if (materialsCreator) {\n objLoader.setMaterials(materialsCreator);\n }\n\n const text = typeof objContent === 'string' ? objContent : new TextDecoder().decode(objContent);\n const modelGroup = objLoader.parse(text);\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(modelGroup, [], 'obj');\n\n onProgress?.(100, '加载完成');\n return result;\n }\n\n /**\n * Load GLTF / GLB model from URL or ArrayBuffer\n */\n public async loadGLTF(\n source: string | ArrayBuffer,\n loadingManager?: THREE.LoadingManager,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n this.cleanup();\n const gltfLoader = this.createGLTFLoader(loadingManager);\n\n let gltf: GLTF;\n const isGlb = typeof source === 'string' ? /\\.glb(\\?.*)?$/i.test(source) : false;\n\n if (typeof source === 'string') {\n onProgress?.(30, '正在请求 GLTF/GLB 模型数据...');\n gltf = await gltfLoader.loadAsync(source, (xhr) => {\n if (xhr.lengthComputable && xhr.total > 0) {\n const p = Math.round(30 + (xhr.loaded / xhr.total) * 50);\n onProgress?.(p, `正在传输模型数据 (${Math.round((xhr.loaded / xhr.total) * 100)}%)...`);\n }\n });\n } else {\n onProgress?.(60, '正在解析 GLTF/GLB 二进制数据流...');\n gltf = await new Promise((resolve, reject) => {\n gltfLoader.parse(\n source,\n '',\n (result) => resolve(result),\n (error) => reject(error)\n );\n });\n }\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(gltf.scene, gltf.animations, isGlb ? 'glb' : 'gltf');\n onProgress?.(100, '加载完成');\n return result;\n }\n\n /**\n * Load standalone GLB binary model from File, Blob, ArrayBuffer, or online URL\n */\n public async loadGLB(\n source: string | ArrayBuffer | Blob | File,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n if (source instanceof Blob || source instanceof File) {\n const buffer = await source.arrayBuffer();\n return this.loadGLTF(buffer, undefined, onProgress);\n }\n return this.loadGLTF(source, undefined, onProgress);\n }\n\n /**\n * Load model directly from OBJ and optional MTL URLs\n */\n public async loadOBJ(\n objUrl: string,\n mtlUrl?: string,\n loadingManager?: THREE.LoadingManager,\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n this.cleanup();\n\n onProgress?.(20, '正在加载材质定义...');\n let materialsCreator: MTLLoader.MaterialCreator | null = null;\n if (mtlUrl) {\n const mtlLoader = new MTLLoader(loadingManager);\n materialsCreator = await mtlLoader.loadAsync(mtlUrl);\n materialsCreator.preload();\n }\n\n onProgress?.(50, '正在加载 OBJ 模型几何数据...');\n const objLoader = new OBJLoader(loadingManager);\n if (materialsCreator) {\n objLoader.setMaterials(materialsCreator);\n }\n\n const modelGroup = await objLoader.loadAsync(objUrl, (xhr) => {\n if (xhr.lengthComputable && xhr.total > 0) {\n const p = Math.round(50 + (xhr.loaded / xhr.total) * 35);\n onProgress?.(p, `正在传输模型数据 (${Math.round((xhr.loaded / xhr.total) * 100)}%)...`);\n }\n });\n\n onProgress?.(88, '正在建立空间 BVH 加速结构与几何居中...');\n const result = this.postProcessModel(modelGroup, [], 'obj');\n\n onProgress?.(100, '加载完成');\n return result;\n }\n\n /**\n * Intelligently load multiple user-selected files or drag-dropped files\n * (Supports .zip, .glb, .gltf + companion .bin / textures, .obj + .mtl / textures)\n */\n public async loadFiles(\n fileList: FileList | File[],\n onProgress?: (percent: number, message: string) => void\n ): Promise<ModelLoadResult> {\n const files = Array.from(fileList);\n if (files.length === 0) {\n throw new Error('未选择任何文件');\n }\n\n const zipFile = files.find((f) => f.name.toLowerCase().endsWith('.zip'));\n if (zipFile) {\n return this.loadZip(zipFile, onProgress);\n }\n\n const glbFile = files.find((f) => f.name.toLowerCase().endsWith('.glb'));\n if (glbFile) {\n return this.loadGLB(glbFile, onProgress);\n }\n\n const gltfFile = files.find((f) => f.name.toLowerCase().endsWith('.gltf'));\n if (gltfFile) {\n onProgress?.(15, '正在建立 GLTF 关联资源映射表...');\n const blobUrls = new Map<string, string>();\n for (const file of files) {\n const url = URL.createObjectURL(file);\n this.customBlobUrls.push(url);\n blobUrls.set(file.name, url);\n blobUrls.set(file.name.toLowerCase(), url);\n }\n\n const loadingManager = new THREE.LoadingManager();\n loadingManager.setURLModifier((url: string) => {\n const cleanUrl = decodeURIComponent(url).replace(/^\\.\\//, '');\n const baseName = cleanUrl.split('/').pop()?.split('\\\\').pop() || cleanUrl;\n if (blobUrls.has(url)) return blobUrls.get(url)!;\n if (blobUrls.has(cleanUrl)) return blobUrls.get(cleanUrl)!;\n if (blobUrls.has(baseName)) return blobUrls.get(baseName)!;\n\n const lowerBase = baseName.toLowerCase();\n for (const [key, blobUrl] of blobUrls.entries()) {\n if (key.toLowerCase() === lowerBase || key.toLowerCase().endsWith('/' + lowerBase)) {\n return blobUrl;\n }\n }\n return url;\n });\n\n const gltfText = await gltfFile.text();\n return this.loadGLTF(gltfText, loadingManager, onProgress);\n }\n\n const objFile = files.find((f) => f.name.toLowerCase().endsWith('.obj'));\n if (objFile) {\n const mtlFile = files.find((f) => f.name.toLowerCase().endsWith('.mtl'));\n const blobUrls = new Map<string, string>();\n for (const file of files) {\n const url = URL.createObjectURL(file);\n this.customBlobUrls.push(url);\n blobUrls.set(file.name, url);\n blobUrls.set(file.name.toLowerCase(), url);\n }\n\n const loadingManager = new THREE.LoadingManager();\n loadingManager.setURLModifier((url: string) => {\n const cleanUrl = decodeURIComponent(url).replace(/^\\.\\//, '');\n const baseName = cleanUrl.split('/').pop()?.split('\\\\').pop() || cleanUrl;\n if (blobUrls.has(url)) return blobUrls.get(url)!;\n if (blobUrls.has(cleanUrl)) return blobUrls.get(cleanUrl)!;\n if (blobUrls.has(baseName)) return blobUrls.get(baseName)!;\n\n const lowerBase = baseName.toLowerCase();\n for (const [key, blobUrl] of blobUrls.entries()) {\n if (key.toLowerCase() === lowerBase || key.toLowerCase().endsWith('/' + lowerBase)) {\n return blobUrl;\n }\n }\n return url;\n });\n\n const objUrl = blobUrls.get(objFile.name)!;\n const mtlUrl = mtlFile ? blobUrls.get(mtlFile.name) : undefined;\n return this.loadOBJ(objUrl, mtlUrl, loadingManager, onProgress);\n }\n\n throw new Error('未识别到支持的 3D 模型文件(请提供 .glb, .gltf, .obj 或 .zip 格式)');\n }\n\n /**\n * Post-process loaded model:\n * - Build BVH acceleration trees on all meshes\n * - Count vertices & faces\n * - Cache original materials and prepare clay/white-model materials\n * - Center model without breaking hierarchy/animations via a root group\n */\n public postProcessModel(\n group: THREE.Group,\n animations?: THREE.AnimationClip[],\n format: 'obj' | 'gltf' | 'glb' | 'sample' = 'obj'\n ): ModelLoadResult {\n let vertexCount = 0;\n let faceCount = 0;\n this.managedMaterials = [];\n\n const enhanceTexture = (tex: THREE.Texture | null, isColor: boolean = true) => {\n if (tex) {\n tex.anisotropy = 16;\n tex.generateMipmaps = true;\n tex.minFilter = THREE.LinearMipmapLinearFilter;\n tex.magFilter = THREE.LinearFilter;\n if (isColor && !tex.colorSpace) {\n tex.colorSpace = THREE.SRGBColorSpace;\n }\n tex.needsUpdate = true;\n }\n };\n\n // Helper to ensure materials are visible, sharp and two-sided\n const ensureVisibleMaterial = (mat: THREE.Material) => {\n mat.side = THREE.DoubleSide; // Avoid invisible back faces due to inverted normals\n mat.needsUpdate = true;\n\n if ('color' in mat) {\n const m = mat as THREE.MeshStandardMaterial;\n enhanceTexture(m.map, true);\n if ('emissiveMap' in m) enhanceTexture(m.emissiveMap, true);\n if ('normalMap' in m) enhanceTexture(m.normalMap, false);\n if ('bumpMap' in m) enhanceTexture(m.bumpMap, false);\n if ('roughnessMap' in m) enhanceTexture(m.roughnessMap, false);\n if ('metalnessMap' in m) enhanceTexture(m.metalnessMap, false);\n if ('aoMap' in m) enhanceTexture(m.aoMap, false);\n\n // If diffuse color is black or very dark without texture (common in OBJ/MTL missing illumination), brighten it\n if (format === 'obj' && !m.map && m.color && m.color.getHex() < 0x222222) {\n m.color.setHex(0xb0b8c4);\n }\n }\n };\n\n // 1. Initial pass: calculate original bounds and center in group's initial state\n group.updateMatrixWorld(true);\n const initialBox = new THREE.Box3().setFromObject(group);\n const origCenter = new THREE.Vector3();\n const origSize = new THREE.Vector3();\n initialBox.getCenter(origCenter);\n initialBox.getSize(origSize);\n\n // 2. Process meshes and ensure materials\n group.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n mesh.castShadow = true;\n mesh.receiveShadow = true;\n\n if (mesh.material) {\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach(ensureVisibleMaterial);\n } else {\n ensureVisibleMaterial(mesh.material);\n }\n } else {\n mesh.material = new THREE.MeshStandardMaterial({\n color: this.plainColor,\n roughness: 0.45,\n metalness: 0.12,\n side: THREE.DoubleSide,\n });\n }\n\n const geom = mesh.geometry;\n if (geom) {\n // Ensure normals exist\n if (!geom.attributes.normal) {\n geom.computeVertexNormals();\n }\n\n // Build BVH for raycasting acceleration and snapping\n if (geom.computeBoundsTree) {\n geom.computeBoundsTree();\n }\n\n // Vertex & face counting\n if (geom.attributes.position) {\n vertexCount += geom.attributes.position.count;\n }\n if (geom.index) {\n faceCount += geom.index.count / 3;\n } else if (geom.attributes.position) {\n faceCount += geom.attributes.position.count / 3;\n }\n\n // Create Digital3DCloud standard CAD/scan blue material for texture toggling\n const plainMat = new THREE.MeshStandardMaterial({\n color: this.plainColor,\n roughness: 0.45,\n metalness: 0.12,\n side: THREE.DoubleSide,\n clippingPlanes: this.currentClippingPlanes.length > 0 ? this.currentClippingPlanes : null,\n clipShadows: true,\n });\n\n const managedItem: ManagedMeshMaterial = {\n mesh,\n originalMaterial: mesh.material,\n plainMaterial: plainMat,\n };\n\n this.applyStyleToItem(managedItem);\n this.managedMaterials.push(managedItem);\n }\n }\n });\n\n // 3. Center model without destructively modifying vertex buffers (protects GLTF skinning & animations)\n const rootGroup = new THREE.Group();\n rootGroup.name = 'gmv-model-root';\n group.position.set(-origCenter.x, -origCenter.y, -origCenter.z);\n rootGroup.add(group);\n rootGroup.updateMatrixWorld(true);\n\n // Compute updated bounding box (centered at 0,0,0)\n const box = new THREE.Box3().setFromObject(rootGroup);\n\n return {\n model: rootGroup,\n box,\n size: origSize,\n center: origCenter,\n vertexCount,\n faceCount: Math.round(faceCount),\n animations: animations || [],\n format,\n };\n }\n\n /**\n * Internal helper to apply display style (solid/wireframe) to a managed mesh\n */\n private applyStyleToItem(item: ManagedMeshMaterial): void {\n const curMat = this.isTextureEnabled ? item.originalMaterial : item.plainMaterial;\n item.mesh.material = curMat;\n\n const setMatProps = (mat: THREE.Material) => {\n mat.visible = true;\n if ('wireframe' in mat) {\n (mat as THREE.MeshStandardMaterial).wireframe = (this.displayStyle === 'wireframe');\n }\n mat.needsUpdate = true;\n };\n\n if (Array.isArray(curMat)) {\n curMat.forEach(setMatProps);\n } else {\n setMatProps(curMat);\n }\n }\n\n /**\n * Set model display style: 'solid' (实体), 'wireframe' (线框)\n */\n public setDisplayStyle(style: ModelDisplayStyle): void {\n this.displayStyle = style;\n for (const item of this.managedMaterials) {\n this.applyStyleToItem(item);\n }\n }\n\n public getDisplayStyle(): ModelDisplayStyle {\n return this.displayStyle;\n }\n\n /**\n * Toggle texture on/off\n */\n public toggleTexture(showTexture: boolean): void {\n this.isTextureEnabled = showTexture;\n for (const item of this.managedMaterials) {\n this.applyStyleToItem(item);\n }\n }\n\n public getTextureState(): boolean {\n return this.isTextureEnabled;\n }\n\n /**\n * Set color for non-texture display (defaults to Digital3DCloud standard CAD blue 0x4c8cd6)\n */\n public setPlainColor(color: THREE.ColorRepresentation): void {\n this.plainColor = typeof color === 'string' ? new THREE.Color(color).getHex() : Number(color);\n for (const item of this.managedMaterials) {\n if (item.plainMaterial && 'color' in item.plainMaterial) {\n (item.plainMaterial as THREE.MeshStandardMaterial).color.set(this.plainColor);\n item.plainMaterial.needsUpdate = true;\n }\n }\n }\n\n public getPlainColor(): number {\n return this.plainColor;\n }\n\n /**\n * Retrieves the maximum original texture resolution (width & height) from the model's materials\n */\n public getModelTextureSize(): { width: number; height: number } | null {\n let maxWidth = 0;\n let maxHeight = 0;\n\n const inspectMat = (mat: THREE.Material) => {\n if ('map' in mat && (mat as THREE.MeshStandardMaterial).map) {\n const tex = (mat as THREE.MeshStandardMaterial).map;\n if (tex) {\n const img = (tex.image || (tex.source as unknown as { data?: { width?: number; height?: number } })?.data) as\n | { width?: number; height?: number }\n | undefined;\n if (img) {\n const w = img.width || 0;\n const h = img.height || 0;\n if (w > 0 && h > 0 && w * h > maxWidth * maxHeight) {\n maxWidth = w;\n maxHeight = h;\n }\n }\n }\n }\n };\n\n for (const item of this.managedMaterials) {\n if (Array.isArray(item.originalMaterial)) {\n item.originalMaterial.forEach(inspectMat);\n } else if (item.originalMaterial) {\n inspectMat(item.originalMaterial);\n }\n }\n\n if (maxWidth > 0 && maxHeight > 0) {\n return { width: maxWidth, height: maxHeight };\n }\n return null;\n }\n\n /**\n * Retrieve all meshes of the model for raycasting and snapping\n */\n public getMeshes(): THREE.Mesh[] {\n return this.managedMaterials.map((m) => m.mesh);\n }\n\n /**\n * Apply clipping planes to all materials\n */\n public applyClippingPlanes(planes: THREE.Plane[]): void {\n this.currentClippingPlanes = planes;\n for (const item of this.managedMaterials) {\n const applyToMat = (mat: THREE.Material) => {\n mat.clippingPlanes = planes.length > 0 ? planes : null;\n mat.clipShadows = true;\n mat.needsUpdate = true;\n };\n\n if (Array.isArray(item.originalMaterial)) {\n item.originalMaterial.forEach(applyToMat);\n } else {\n applyToMat(item.originalMaterial);\n }\n applyToMat(item.plainMaterial);\n }\n }\n\n /**\n * Clean up resources\n */\n public cleanup(): void {\n if (this.currentZipContent) {\n this.currentZipContent.dispose();\n this.currentZipContent = null;\n }\n\n if (this.customBlobUrls.length > 0) {\n this.customBlobUrls.forEach((url) => {\n try {\n URL.revokeObjectURL(url);\n } catch {\n // ignore\n }\n });\n this.customBlobUrls = [];\n }\n\n for (const item of this.managedMaterials) {\n if (item.plainMaterial) {\n item.plainMaterial.dispose();\n }\n if (item.mesh.geometry && item.mesh.geometry.disposeBoundsTree) {\n item.mesh.geometry.disposeBoundsTree();\n }\n }\n this.managedMaterials = [];\n }\n\n /**\n * Complete destruction of loader and transcoders\n */\n public destroy(): void {\n this.cleanup();\n if (this.dracoLoader) {\n this.dracoLoader.dispose();\n this.dracoLoader = null;\n }\n if (this.ktx2Loader) {\n this.ktx2Loader.dispose();\n this.ktx2Loader = null;\n }\n }\n}\n","import * as THREE from 'three';\n\n/**\n * Calculates the exact signed volume of a 3D Mesh using divergence theorem.\n * Works accurately for closed (watertight) meshes.\n */\nexport function computeMeshVolume(mesh: THREE.Mesh): number {\n const geometry = mesh.geometry;\n if (!geometry) return 0;\n\n const position = geometry.attributes.position;\n if (!position) return 0;\n\n let totalVolume = 0;\n const p1 = new THREE.Vector3();\n const p2 = new THREE.Vector3();\n const p3 = new THREE.Vector3();\n\n mesh.updateMatrixWorld(true);\n const matrixWorld = mesh.matrixWorld;\n\n if (geometry.index) {\n const indices = geometry.index;\n for (let i = 0; i < indices.count; i += 3) {\n const i1 = indices.getX(i);\n const i2 = indices.getX(i + 1);\n const i3 = indices.getX(i + 2);\n\n p1.fromBufferAttribute(position, i1).applyMatrix4(matrixWorld);\n p2.fromBufferAttribute(position, i2).applyMatrix4(matrixWorld);\n p3.fromBufferAttribute(position, i3).applyMatrix4(matrixWorld);\n\n totalVolume += signedVolumeOfTriangle(p1, p2, p3);\n }\n } else {\n for (let i = 0; i < position.count; i += 3) {\n p1.fromBufferAttribute(position, i).applyMatrix4(matrixWorld);\n p2.fromBufferAttribute(position, i + 1).applyMatrix4(matrixWorld);\n p3.fromBufferAttribute(position, i + 2).applyMatrix4(matrixWorld);\n\n totalVolume += signedVolumeOfTriangle(p1, p2, p3);\n }\n }\n\n return Math.abs(totalVolume);\n}\n\n/**\n * Computes the total volume of all meshes inside a Group or Object3D\n */\nexport function computeGroupVolume(root: THREE.Object3D): { meshVolume: number; bboxVolume: number } {\n let meshVolume = 0;\n root.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n meshVolume += computeMeshVolume(child as THREE.Mesh);\n }\n });\n\n const bbox = new THREE.Box3().setFromObject(root);\n const size = new THREE.Vector3();\n bbox.getSize(size);\n const bboxVolume = size.x * size.y * size.z;\n\n return { meshVolume, bboxVolume };\n}\n\nfunction signedVolumeOfTriangle(p1: THREE.Vector3, p2: THREE.Vector3, p3: THREE.Vector3): number {\n return p1.dot(p2.clone().cross(p3)) / 6.0;\n}\n\n/**\n * Calculates the area of a 3D polygon defined by an array of 3D vertices using Newell's method.\n */\nexport function compute3DPolygonArea(points: THREE.Vector3[]): number {\n const n = points.length;\n if (n < 3) return 0;\n\n const normal = new THREE.Vector3(0, 0, 0);\n for (let i = 0; i < n; i++) {\n const current = points[i];\n const next = points[(i + 1) % n];\n\n normal.x += (current.y - next.y) * (current.z + next.z);\n normal.y += (current.z - next.z) * (current.x + next.x);\n normal.z += (current.x - next.x) * (current.y + next.y);\n }\n\n return normal.length() * 0.5;\n}\n\n/**\n * Formats numeric value with unit\n */\nexport function formatUnit(value: number, unit: string = 'm', decimals: number = 2): string {\n if (Math.abs(value) < 0.001) {\n return `0.00 ${unit}`;\n }\n return `${value.toFixed(decimals)} ${unit}`;\n}\n","import * as THREE from 'three';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { formatUnit } from '../utils/math';\nimport { BoundingBoxOptions } from '../types';\n\nexport class BoundingBoxTool {\n private scene: THREE.Scene;\n private boxGroup: THREE.Group;\n private wireframeBox: THREE.LineSegments | null = null;\n\n // 3 Dynamic active dimension lines\n private lineX: THREE.Line | null = null;\n private lineY: THREE.Line | null = null;\n private lineZ: THREE.Line | null = null;\n\n // 3 Dynamic CSS2D labels\n private labelX: CSS2DObject | null = null;\n private labelY: CSS2DObject | null = null;\n private labelZ: CSS2DObject | null = null;\n\n private currentModel: THREE.Object3D | null = null;\n private isVisible: boolean = false;\n private unit: string = 'm';\n\n // Cached local 8 corner vertices of the model's bounding box\n private localVertices: THREE.Vector3[] = [];\n private worldVertices: THREE.Vector3[] = [\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n new THREE.Vector3(),\n ];\n\n // Cached model dimensions\n private modelSize: THREE.Vector3 = new THREE.Vector3();\n\n constructor(scene: THREE.Scene) {\n this.scene = scene;\n this.boxGroup = new THREE.Group();\n this.boxGroup.name = 'gmv-bounding-box-group';\n this.scene.add(this.boxGroup);\n }\n\n /**\n * Attaches bounding box to a model and initializes dynamic dimensional lines\n */\n public attachToModel(model: THREE.Object3D, options?: Partial<BoundingBoxOptions>): void {\n this.detach();\n this.currentModel = model;\n this.unit = options?.unit || 'm';\n\n // 1. Compute model's local bounding box accurately across hierarchies\n model.updateMatrixWorld(true);\n const box = new THREE.Box3();\n const invModelMatrix = model.matrixWorld.clone().invert();\n\n // Traverse all meshes in model to compute local bounds\n model.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n if (mesh.geometry) {\n if (!mesh.geometry.boundingBox) {\n mesh.geometry.computeBoundingBox();\n }\n if (mesh.geometry.boundingBox) {\n const relMatrix = mesh.matrixWorld.clone().premultiply(invModelMatrix);\n const meshBox = mesh.geometry.boundingBox.clone().applyMatrix4(relMatrix);\n box.union(meshBox);\n }\n }\n }\n });\n\n if (box.isEmpty()) {\n box.setFromObject(model);\n }\n\n const min = box.min;\n const max = box.max;\n box.getSize(this.modelSize);\n\n // 2. Generate 8 local corners\n // Index encoding: x (bit 0), y (bit 1), z (bit 2)\n this.localVertices = [\n new THREE.Vector3(min.x, min.y, min.z), // 0: 000\n new THREE.Vector3(max.x, min.y, min.z), // 1: 001 (x)\n new THREE.Vector3(min.x, max.y, min.z), // 2: 010 (y)\n new THREE.Vector3(max.x, max.y, min.z), // 3: 011 (xy)\n new THREE.Vector3(min.x, min.y, max.z), // 4: 100 (z)\n new THREE.Vector3(max.x, min.y, max.z), // 5: 101 (xz)\n new THREE.Vector3(min.x, max.y, max.z), // 6: 110 (yz)\n new THREE.Vector3(max.x, max.y, max.z), // 7: 111 (xyz)\n ];\n\n // 3. Create full 12-edge wireframe box\n // 12 edges indices:\n // Along X: (0,1), (2,3), (4,5), (6,7)\n // Along Y: (0,2), (1,3), (4,6), (5,7)\n // Along Z: (0,4), (1,5), (2,6), (3,7)\n const edgeIndices = [\n 0, 1, 2, 3, 4, 5, 6, 7, // X edges\n 0, 2, 1, 3, 4, 6, 5, 7, // Y edges\n 0, 4, 1, 5, 2, 6, 3, 7 // Z edges\n ];\n\n const boxGeom = new THREE.BufferGeometry();\n const posBuffer = new Float32Array(edgeIndices.length * 3);\n boxGeom.setAttribute('position', new THREE.BufferAttribute(posBuffer, 3));\n\n const lineMaterial = new THREE.LineBasicMaterial({\n color: options?.color ? new THREE.Color(options.color) : 0x00d2ff,\n linewidth: 1,\n transparent: true,\n opacity: 0.6,\n depthTest: true,\n });\n this.wireframeBox = new THREE.LineSegments(boxGeom, lineMaterial);\n this.boxGroup.add(this.wireframeBox);\n\n // 4. Create 3 Dynamic Colored Active Dimension Lines\n // X (Length): Red, Y (Height): Green, Z (Width): Blue\n this.lineX = this.createDynamicLine(0xef4444);\n this.lineY = this.createDynamicLine(0x10b981);\n this.lineZ = this.createDynamicLine(0x3b82f6);\n\n // 5. Create CSS2D Labels\n const divX = document.createElement('div');\n divX.className = 'gmv-dimension-badge dim-x';\n divX.innerHTML = `<span>长 (X):</span> <strong>${formatUnit(this.modelSize.x, this.unit)}</strong>`;\n this.labelX = new CSS2DObject(divX);\n this.boxGroup.add(this.labelX);\n\n const divY = document.createElement('div');\n divY.className = 'gmv-dimension-badge dim-y';\n divY.innerHTML = `<span>高 (Y):</span> <strong>${formatUnit(this.modelSize.y, this.unit)}</strong>`;\n this.labelY = new CSS2DObject(divY);\n this.boxGroup.add(this.labelY);\n\n const divZ = document.createElement('div');\n divZ.className = 'gmv-dimension-badge dim-z';\n divZ.innerHTML = `<span>宽 (Z):</span> <strong>${formatUnit(this.modelSize.z, this.unit)}</strong>`;\n this.labelZ = new CSS2DObject(divZ);\n this.boxGroup.add(this.labelZ);\n\n this.setVisible(options?.visible ?? false);\n }\n\n private createDynamicLine(color: number): THREE.Line {\n const geom = new THREE.BufferGeometry().setFromPoints([\n new THREE.Vector3(),\n new THREE.Vector3(),\n ]);\n const mat = new THREE.LineBasicMaterial({\n color,\n linewidth: 3,\n depthTest: false,\n });\n const line = new THREE.Line(geom, mat);\n this.boxGroup.add(line);\n return line;\n }\n\n /**\n * Updates bounding box and selects the CURRENT front-facing 3 edges dynamically\n * based on the model's current rotation and camera perspective!\n */\n public update(camera: THREE.Camera): void {\n if (!this.isVisible || !this.currentModel || this.localVertices.length < 8) return;\n\n this.currentModel.updateMatrixWorld(true);\n const matrix = this.currentModel.matrixWorld;\n\n // 1. Transform all 8 local corners to world coordinates\n for (let i = 0; i < 8; i++) {\n this.worldVertices[i].copy(this.localVertices[i]).applyMatrix4(matrix);\n }\n\n // 2. Update 12-edges wireframe box positions\n if (this.wireframeBox) {\n const edgeIndices = [\n 0, 1, 2, 3, 4, 5, 6, 7, // X edges\n 0, 2, 1, 3, 4, 6, 5, 7, // Y edges\n 0, 4, 1, 5, 2, 6, 3, 7 // Z edges\n ];\n const posAttr = this.wireframeBox.geometry.attributes.position;\n let ptr = 0;\n for (const idx of edgeIndices) {\n const v = this.worldVertices[idx];\n posAttr.setXYZ(ptr++, v.x, v.y, v.z);\n }\n posAttr.needsUpdate = true;\n }\n\n // 3. Find the corner vertex CLOSEST to the camera in world space\n // Out of the 8 corners, the closest corner faces the user!\n let bestCorner = 0;\n let minDistance = Infinity;\n const camPos = camera.position;\n\n for (let i = 0; i < 8; i++) {\n const dist = this.worldVertices[i].distanceTo(camPos);\n if (dist < minDistance) {\n minDistance = dist;\n bestCorner = i;\n }\n }\n\n // 4. From bestCorner, the 3 connected edges along X, Y, Z are:\n // Neighbor along X: bestCorner ^ 1 (flips bit 0)\n // Neighbor along Y: bestCorner ^ 2 (flips bit 1)\n // Neighbor along Z: bestCorner ^ 4 (flips bit 2)\n const cornerV = this.worldVertices[bestCorner];\n const neighborX = this.worldVertices[bestCorner ^ 1];\n const neighborY = this.worldVertices[bestCorner ^ 2];\n const neighborZ = this.worldVertices[bestCorner ^ 4];\n\n // 5. Update the 3 active dimension lines\n this.updateLine(this.lineX, cornerV, neighborX);\n this.updateLine(this.lineY, cornerV, neighborY);\n this.updateLine(this.lineZ, cornerV, neighborZ);\n\n // 6. Update CSS2D label positions to midpoints of the current 3 front edges\n if (this.labelX) {\n this.labelX.position.copy(cornerV).add(neighborX).multiplyScalar(0.5);\n }\n if (this.labelY) {\n this.labelY.position.copy(cornerV).add(neighborY).multiplyScalar(0.5);\n }\n if (this.labelZ) {\n this.labelZ.position.copy(cornerV).add(neighborZ).multiplyScalar(0.5);\n }\n }\n\n private updateLine(line: THREE.Line | null, p1: THREE.Vector3, p2: THREE.Vector3): void {\n if (!line) return;\n const pos = line.geometry.attributes.position;\n pos.setXYZ(0, p1.x, p1.y, p1.z);\n pos.setXYZ(1, p2.x, p2.y, p2.z);\n pos.needsUpdate = true;\n }\n\n public setVisible(visible: boolean): void {\n this.isVisible = visible;\n this.boxGroup.visible = visible;\n }\n\n public getVisible(): boolean {\n return this.isVisible;\n }\n\n public setUnit(unit: string): void {\n this.unit = unit;\n if (this.currentModel) {\n this.attachToModel(this.currentModel, { visible: this.isVisible, unit });\n }\n }\n\n public detach(): void {\n if (this.wireframeBox) {\n this.wireframeBox.geometry.dispose();\n (this.wireframeBox.material as THREE.Material).dispose();\n this.boxGroup.remove(this.wireframeBox);\n this.wireframeBox = null;\n }\n\n if (this.lineX) {\n this.lineX.geometry.dispose();\n (this.lineX.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineX);\n this.lineX = null;\n }\n if (this.lineY) {\n this.lineY.geometry.dispose();\n (this.lineY.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineY);\n this.lineY = null;\n }\n if (this.lineZ) {\n this.lineZ.geometry.dispose();\n (this.lineZ.material as THREE.Material).dispose();\n this.boxGroup.remove(this.lineZ);\n this.lineZ = null;\n }\n\n if (this.labelX) {\n this.boxGroup.remove(this.labelX);\n this.labelX = null;\n }\n if (this.labelY) {\n this.boxGroup.remove(this.labelY);\n this.labelY = null;\n }\n if (this.labelZ) {\n this.boxGroup.remove(this.labelZ);\n this.labelZ = null;\n }\n\n this.localVertices = [];\n this.currentModel = null;\n }\n\n public destroy(): void {\n this.detach();\n this.scene.remove(this.boxGroup);\n }\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport * as TWEEN from '@tweenjs/tween.js';\nimport { AutoRotateAxis, AutoRotateDirection, AutoRotateOptions, CameraView, RotationLockAxis } from '../types';\n\nexport class CameraTool {\n private camera: THREE.PerspectiveCamera;\n private controls: OrbitControls | TrackballControls;\n private tweenGroup: TWEEN.Group;\n private currentTween: TWEEN.Tween<{ t: number }> | null = null;\n\n // Auto-Rotation State (Default: X-axis rotation, clockwise)\n private isAutoRotating: boolean = false;\n private autoRotateAxis: AutoRotateAxis = 'x';\n private autoRotateDirection: AutoRotateDirection = 'clockwise';\n private autoRotateSpeed: number = 1.0;\n\n // Axis Rotation Lock State\n private lockedAxis: RotationLockAxis = null;\n private lockedPitchAngle: number = 0;\n private lockedYawAngle: number = 0;\n\n constructor(\n camera: THREE.PerspectiveCamera,\n controls: OrbitControls | TrackballControls,\n tweenGroup?: TWEEN.Group\n ) {\n this.camera = camera;\n this.controls = controls;\n this.tweenGroup = tweenGroup || new TWEEN.Group();\n }\n\n // ==========================================\n // Auto-Rotation Controls\n // ==========================================\n\n public setAutoRotate(\n enabled: boolean,\n axis?: AutoRotateAxis,\n direction?: AutoRotateDirection,\n speed?: number\n ): void {\n this.isAutoRotating = enabled;\n if (axis !== undefined) this.autoRotateAxis = axis;\n if (direction !== undefined) this.autoRotateDirection = direction;\n if (speed !== undefined) this.autoRotateSpeed = speed;\n }\n\n public toggleAutoRotate(): boolean {\n this.isAutoRotating = !this.isAutoRotating;\n return this.isAutoRotating;\n }\n\n public setAutoRotateAxis(axis: AutoRotateAxis): void {\n this.autoRotateAxis = axis;\n }\n\n public setAutoRotateDirection(direction: AutoRotateDirection): void {\n this.autoRotateDirection = direction;\n }\n\n public setAutoRotateSpeed(speed: number): void {\n this.autoRotateSpeed = Math.max(0.1, Math.min(10, speed));\n }\n\n public getAutoRotateState(): AutoRotateOptions {\n return {\n enabled: this.isAutoRotating,\n axis: this.autoRotateAxis,\n direction: this.autoRotateDirection,\n speed: this.autoRotateSpeed,\n };\n }\n\n // ==========================================\n // Axis Rotation Lock Controls (X / Y / Z Lock)\n // ==========================================\n\n public setLockedAxis(axis: RotationLockAxis): void {\n this.lockedAxis = axis;\n const offset = this.camera.position.clone().sub(this.controls.target);\n const radius = offset.length();\n\n if (radius > 0.0001) {\n this.lockedPitchAngle = Math.asin(Math.max(-1, Math.min(1, offset.y / radius)));\n this.lockedYawAngle = Math.atan2(offset.z, offset.x);\n }\n }\n\n public getLockedAxis(): RotationLockAxis {\n return this.lockedAxis;\n }\n\n /**\n * Called each frame in render loop to apply auto-rotation and axis lock constraints\n */\n public updatePerFrame(): void {\n const target = this.controls.target;\n const offset = this.camera.position.clone().sub(target);\n const radius = offset.length();\n\n // 1. Auto-rotation logic\n if (this.isAutoRotating && radius > 0.001) {\n const dirSign = this.autoRotateDirection === 'clockwise' ? 1 : -1;\n const angle = 0.008 * this.autoRotateSpeed * dirSign;\n\n if (this.autoRotateAxis === 'x') {\n // Rotate around world X axis (1, 0, 0)\n const rotAxis = new THREE.Vector3(1, 0, 0);\n offset.applyAxisAngle(rotAxis, angle);\n this.camera.up.applyAxisAngle(rotAxis, angle);\n } else {\n // Rotate around world Y axis (0, 1, 0)\n const rotAxis = new THREE.Vector3(0, 1, 0);\n offset.applyAxisAngle(rotAxis, angle);\n this.camera.up.applyAxisAngle(rotAxis, angle);\n }\n\n this.camera.position.copy(target).add(offset);\n this.camera.lookAt(target);\n }\n\n // 2. Axis Rotation Lock Constraints\n if (this.lockedAxis && radius > 0.001) {\n const currentOffset = this.camera.position.clone().sub(target);\n const curRadius = currentOffset.length();\n\n if (this.lockedAxis === 'x') {\n // Lock X-rotation (Pitch locked, can only revolve horizontally around world Y)\n const hRadius = curRadius * Math.cos(this.lockedPitchAngle);\n const targetY = curRadius * Math.sin(this.lockedPitchAngle);\n const angleXZ = Math.atan2(currentOffset.z, currentOffset.x);\n currentOffset.x = hRadius * Math.cos(angleXZ);\n currentOffset.y = targetY;\n currentOffset.z = hRadius * Math.sin(angleXZ);\n this.camera.position.copy(target).add(currentOffset);\n } else if (this.lockedAxis === 'y') {\n // Lock Y-rotation (Yaw locked, can only pitch vertically in the locked azimuth plane)\n const curY = currentOffset.y;\n const hRadius = Math.sqrt(Math.max(0, curRadius * curRadius - curY * curY));\n currentOffset.x = hRadius * Math.cos(this.lockedYawAngle);\n currentOffset.z = hRadius * Math.sin(this.lockedYawAngle);\n this.camera.position.copy(target).add(currentOffset);\n } else if (this.lockedAxis === 'z') {\n // Lock Z-rotation (Roll locked, camera remains strictly upright without tilting)\n this.camera.up.set(0, 1, 0);\n }\n\n this.camera.lookAt(target);\n }\n }\n\n /**\n * Smoothly transitions the camera to one of the 8 standard view orientations\n */\n public setView(\n view: CameraView,\n targetObject: THREE.Object3D,\n duration: number = 600\n ): Promise<void> {\n return new Promise((resolve) => {\n // 1. Calculate bounding sphere of target\n const box = new THREE.Box3().setFromObject(targetObject);\n const sphere = new THREE.Sphere();\n box.getBoundingSphere(sphere);\n\n const center = sphere.center.clone();\n const radius = Math.max(sphere.radius, 1);\n\n // Adjust camera near and far to prevent clipping large/small models\n this.camera.near = Math.max(0.01, radius / 500);\n this.camera.far = Math.max(10000, radius * 50);\n this.camera.updateProjectionMatrix();\n\n // Fit distance based on FOV\n const fovRad = (this.camera.fov * Math.PI) / 180;\n const fitDistance = (radius / Math.sin(fovRad / 2)) * 1.15;\n\n // 2. Determine target direction vector and up vector for the view\n const dir = new THREE.Vector3();\n const up = new THREE.Vector3(0, 1, 0);\n\n switch (view) {\n case 'front': // Front (+Z)\n dir.set(0, 0, 1);\n break;\n case 'back': // Back (-Z)\n dir.set(0, 0, -1);\n break;\n case 'left': // Left (-X)\n dir.set(-1, 0, 0);\n break;\n case 'right': // Right (+X)\n dir.set(1, 0, 0);\n break;\n case 'top': // Top (+Y)\n dir.set(0, 1, 0.0001); // Slight offset to prevent gimbal lock\n up.set(0, 0, -1);\n break;\n case 'bottom': // Bottom (-Y)\n dir.set(0, -1, 0.0001);\n up.set(0, 0, 1);\n break;\n case 'isometric': // Isometric Front-Right-Top\n dir.set(1, 1, 1).normalize();\n break;\n case 'isometric-back': // Isometric Back-Left-Top\n dir.set(-1, 1, -1).normalize();\n break;\n }\n\n const targetCamPos = center.clone().add(dir.multiplyScalar(fitDistance));\n\n // 3. Stop any existing tween\n if (this.currentTween) {\n this.currentTween.stop();\n this.currentTween = null;\n }\n\n this.camera.up.copy(up);\n\n // If duration is 0, apply view immediately without animation\n if (duration <= 0) {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n resolve();\n return;\n }\n\n const startPos = this.camera.position.clone();\n const startTarget = this.controls.target.clone();\n\n // Interpolate both camera position and target\n const animData = { t: 0 };\n this.currentTween = new TWEEN.Tween(animData, this.tweenGroup)\n .to({ t: 1 }, duration)\n .easing(TWEEN.Easing.Cubic.Out)\n .onUpdate(() => {\n this.camera.position.lerpVectors(startPos, targetCamPos, animData.t);\n this.controls.target.lerpVectors(startTarget, center, animData.t);\n this.controls.update();\n })\n .onComplete(() => {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n this.currentTween = null;\n resolve();\n })\n .start();\n });\n }\n\n /**\n * Fits camera to view the whole object from current angle\n */\n public fitToObject(object: THREE.Object3D, duration: number = 500): Promise<void> {\n return new Promise((resolve) => {\n const box = new THREE.Box3().setFromObject(object);\n const sphere = new THREE.Sphere();\n box.getBoundingSphere(sphere);\n\n const center = sphere.center;\n const radius = Math.max(sphere.radius, 1);\n\n // Adjust camera near and far to prevent clipping large/small models\n this.camera.near = Math.max(0.01, radius / 500);\n this.camera.far = Math.max(10000, radius * 50);\n this.camera.updateProjectionMatrix();\n\n const fovRad = (this.camera.fov * Math.PI) / 180;\n const fitDistance = (radius / Math.sin(fovRad / 2)) * 1.15;\n\n const currentDir = this.camera.position.clone().sub(this.controls.target).normalize();\n const targetCamPos = center.clone().add(currentDir.multiplyScalar(fitDistance));\n\n const startPos = this.camera.position.clone();\n const startTarget = this.controls.target.clone();\n\n const animData = { t: 0 };\n new TWEEN.Tween(animData, this.tweenGroup)\n .to({ t: 1 }, duration)\n .easing(TWEEN.Easing.Cubic.Out)\n .onUpdate(() => {\n this.camera.position.lerpVectors(startPos, targetCamPos, animData.t);\n this.controls.target.lerpVectors(startTarget, center, animData.t);\n this.controls.update();\n })\n .onComplete(() => {\n this.camera.position.copy(targetCamPos);\n this.controls.target.copy(center);\n this.controls.update();\n resolve();\n })\n .start();\n });\n }\n}\n","import * as THREE from 'three';\n\nexport interface SnapResult {\n hit: boolean;\n point: THREE.Vector3 | null;\n normal: THREE.Vector3 | null;\n isVertexSnapped: boolean;\n distanceToVertex: number;\n}\n\n/**\n * Finds intersection on model and optionally snaps to the nearest vertex\n * of the intersected face if within the screen-space pixel threshold.\n */\nexport function findSnapPoint(\n mouse: THREE.Vector2, // Normalized Device Coordinates (-1 to 1)\n camera: THREE.Camera,\n modelMeshes: THREE.Mesh[],\n snapPixelThreshold: number = 15,\n viewportWidth: number = 800,\n viewportHeight: number = 600\n): SnapResult {\n if (!modelMeshes || modelMeshes.length === 0) {\n return { hit: false, point: null, normal: null, isVertexSnapped: false, distanceToVertex: Infinity };\n }\n\n const raycaster = new THREE.Raycaster();\n raycaster.setFromCamera(mouse, camera);\n\n // Set firstHitOnly for three-mesh-bvh if available\n (raycaster as unknown as { firstHitOnly: boolean }).firstHitOnly = true;\n\n const intersects = raycaster.intersectObjects(modelMeshes, true);\n\n if (intersects.length === 0) {\n return { hit: false, point: null, normal: null, isVertexSnapped: false, distanceToVertex: Infinity };\n }\n\n const hit = intersects[0];\n const hitPoint = hit.point.clone();\n const faceNormal = hit.face ? hit.face.normal.clone() : new THREE.Vector3(0, 1, 0);\n if (hit.object) {\n faceNormal.transformDirection(hit.object.matrixWorld);\n }\n\n // Check vertices of the hit face for vertex snapping\n let closestVertex: THREE.Vector3 | null = null;\n let minScreenDist = snapPixelThreshold;\n\n if (hit.face && hit.object && (hit.object as THREE.Mesh).geometry) {\n const mesh = hit.object as THREE.Mesh;\n const geometry = mesh.geometry;\n const posAttr = geometry.attributes.position;\n\n if (posAttr) {\n const vIndices = [hit.face.a, hit.face.b, hit.face.c];\n const vWorld = new THREE.Vector3();\n const vScreen = new THREE.Vector3();\n\n for (const idx of vIndices) {\n vWorld.fromBufferAttribute(posAttr, idx).applyMatrix4(mesh.matrixWorld);\n vScreen.copy(vWorld).project(camera);\n\n // Convert NDC to screen pixels\n const screenX = ((vScreen.x + 1) * 0.5) * viewportWidth;\n const screenY = ((-vScreen.y + 1) * 0.5) * viewportHeight;\n\n const mouseScreenX = ((mouse.x + 1) * 0.5) * viewportWidth;\n const mouseScreenY = ((-mouse.y + 1) * 0.5) * viewportHeight;\n\n const screenDist = Math.hypot(screenX - mouseScreenX, screenY - mouseScreenY);\n\n if (screenDist < minScreenDist) {\n minScreenDist = screenDist;\n closestVertex = vWorld.clone();\n }\n }\n }\n }\n\n if (closestVertex) {\n return {\n hit: true,\n point: closestVertex,\n normal: faceNormal,\n isVertexSnapped: true,\n distanceToVertex: minScreenDist,\n };\n }\n\n return {\n hit: true,\n point: hitPoint,\n normal: faceNormal,\n isVertexSnapped: false,\n distanceToVertex: Infinity,\n };\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { findSnapPoint } from '../utils/snapping';\nimport { AxisSectionState, ClippingAxis, SectioningState } from '../types';\nimport { EventEmitter } from '../core/EventEmitter';\n\ninterface PlaneWidget {\n axis: ClippingAxis;\n group: THREE.Group;\n mesh: THREE.Mesh;\n border: THREE.LineSegments;\n arrowGroup: THREE.Group;\n hitMeshes: THREE.Object3D[];\n label: CSS2DObject;\n labelText: HTMLDivElement;\n meshMat: THREE.MeshBasicMaterial;\n borderMat: THREE.LineBasicMaterial;\n arrowMat: THREE.MeshBasicMaterial;\n normal: THREE.Vector3;\n baseColor: number;\n borderColor: number;\n position: number;\n inverted: boolean;\n enabled: boolean;\n}\n\nexport class ClippingTool {\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private container: HTMLElement;\n private modelLoader: ModelLoader;\n private controls: OrbitControls | TrackballControls | null = null;\n private emitter: EventEmitter | null = null;\n\n // Active Three.js Clipping Planes\n private planeX: THREE.Plane;\n private planeY: THREE.Plane;\n private planeZ: THREE.Plane;\n private customPlane: THREE.Plane | null = null;\n\n // Sectioning States matching CAD / Digital3DCloud standard:\n // 1. isEnabled: Default FALSE. Only show when user turns on sectioning.\n private isEnabled: boolean = false;\n // 2. activeAxis: Recently focused axis for quick reference\n private activeAxis: ClippingAxis = 'x';\n // 3. helperVisible: Whether the translucent helper planes are visible\n private helperVisible: boolean = true;\n\n // Group containing the 3 plane widgets\n private planesGroup: THREE.Group;\n private interactive: boolean = false;\n private widgets: Partial<Record<ClippingAxis, PlaneWidget>> = {};\n\n // Dragging State with Screen-Projection Math (zero jitter, zero stickiness, no unwanted rotation)\n private isDraggingPlane: boolean = false;\n private draggedWidget: PlaneWidget | null = null;\n private dragStartPointer: THREE.Vector2 = new THREE.Vector2();\n private dragStartPosition: number = 0;\n private dragScreenAxis: THREE.Vector2 = new THREE.Vector2();\n private dragScreenAxisLenSq: number = 0;\n private raycaster: THREE.Raycaster = new THREE.Raycaster();\n private hoveredAxis: ClippingAxis | null = null;\n\n // Custom 2-point knife cutting state\n private isPickingCustomPoints: boolean = false;\n private customPoints: THREE.Vector3[] = [];\n private customMarkersGroup: THREE.Group;\n\n // Cached model bounding box\n private modelBox: THREE.Box3 = new THREE.Box3();\n\n // Bound event listeners\n private onPointerDownBound: (e: PointerEvent) => void;\n private onPointerMoveBound: (e: PointerEvent) => void;\n private onPointerMoveDragBound: (e: PointerEvent) => void;\n private onPointerUpDragBound: (e: PointerEvent) => void;\n private onDoubleClickBound: (e: MouseEvent) => void;\n\n constructor(\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n container: HTMLElement,\n modelLoader: ModelLoader,\n controls?: OrbitControls | TrackballControls,\n emitter?: EventEmitter\n ) {\n this.scene = scene;\n this.camera = camera;\n this.container = container;\n this.modelLoader = modelLoader;\n this.controls = controls || null;\n this.emitter = emitter || null;\n\n // Initialize inactive planes far away\n this.planeX = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 100000);\n this.planeY = new THREE.Plane(new THREE.Vector3(0, -1, 0), 100000);\n this.planeZ = new THREE.Plane(new THREE.Vector3(0, 0, -1), 100000);\n\n // Group for the cutting plane widgets (HIDDEN by default!)\n this.planesGroup = new THREE.Group();\n this.planesGroup.name = 'gmv-interactive-clipping-planes';\n this.planesGroup.visible = false;\n this.scene.add(this.planesGroup);\n\n // Custom 2-point marker group\n this.customMarkersGroup = new THREE.Group();\n this.customMarkersGroup.name = 'gmv-custom-clip-markers';\n this.scene.add(this.customMarkersGroup);\n\n // Bind event handlers\n this.onPointerDownBound = this.handlePointerDown.bind(this);\n this.onPointerMoveBound = this.handlePointerMove.bind(this);\n this.onPointerMoveDragBound = this.handlePointerMoveDrag.bind(this);\n this.onPointerUpDragBound = this.handlePointerUpDrag.bind(this);\n this.onDoubleClickBound = this.handleDoubleClick.bind(this);\n\n // Use CAPTURE phase on pointerdown to intercept clicks on clipping planes\n // BEFORE TrackballControls can start a rotation gesture!\n this.container.addEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.addEventListener('pointermove', this.onPointerMoveBound);\n this.container.addEventListener('dblclick', this.onDoubleClickBound, { capture: true });\n }\n\n public setControls(controls: OrbitControls | TrackballControls): void {\n this.controls = controls;\n }\n\n public setEmitter(emitter: EventEmitter): void {\n this.emitter = emitter;\n }\n\n public setInteractive(interactive: boolean): void {\n this.interactive = interactive && this.isEnabled;\n if (!this.interactive && this.container.style.cursor === 'grab') {\n this.container.style.cursor = 'default';\n }\n }\n\n /**\n * Initializes or updates bounds from loaded model\n */\n public setModelBounds(box: THREE.Box3): void {\n this.modelBox.copy(box);\n this.rebuildPlaneWidgets();\n this.resetAll();\n }\n\n /**\n * Builds the 3 interactive clipping planes strictly fitting inside the model's bounding box\n */\n private rebuildPlaneWidgets(): void {\n this.clearPlaneWidgets();\n\n const size = new THREE.Vector3(2, 2, 2);\n const center = new THREE.Vector3(0, 0, 0);\n if (!this.modelBox.isEmpty()) {\n this.modelBox.getSize(size);\n this.modelBox.getCenter(center);\n }\n\n // STRICT BOUNDING BOX DIMENSIONS:\n // Slicing planes MUST NEVER exceed the bounding box boundary!\n const sx = Math.max(0.001, size.x);\n const sy = Math.max(0.001, size.y);\n const sz = Math.max(0.001, size.z);\n\n // 1. X Plane (YZ cross section, perpendicular to X): width = sz, height = sy\n this.widgets.x = this.createPlaneWidget('x', 0xef4444, 0xf87171, sz, sy, center);\n\n // 2. Y Plane (XZ cross section, perpendicular to Y): width = sx, height = sz\n this.widgets.y = this.createPlaneWidget('y', 0x10b981, 0x34d399, sx, sz, center);\n\n // 3. Z Plane (XY cross section, perpendicular to Z): width = sx, height = sy\n this.widgets.z = this.createPlaneWidget('z', 0x3b82f6, 0x60a5fa, sx, sy, center);\n\n // Default: X axis is enabled when sectioning is turned on; Y and Z can be toggled on simultaneously\n if (this.widgets.x) this.widgets.x.enabled = true;\n if (this.widgets.y) this.widgets.y.enabled = false;\n if (this.widgets.z) this.widgets.z.enabled = false;\n\n // Apply visibility states\n this.updateWidgetsVisibility();\n }\n\n private createPlaneWidget(\n axis: ClippingAxis,\n baseColor: number,\n borderColor: number,\n w: number,\n h: number,\n center: THREE.Vector3\n ): PlaneWidget {\n const group = new THREE.Group();\n group.name = `gmv-clip-plane-${axis}`;\n\n // 1. Translucent Plane Mesh:\n // Exactly matches the cross section of the bounding box.\n const geom = new THREE.PlaneGeometry(w, h);\n const meshMat = new THREE.MeshBasicMaterial({\n color: baseColor,\n transparent: true,\n opacity: 0.25,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n const mesh = new THREE.Mesh(geom, meshMat);\n mesh.userData = { axis };\n mesh.renderOrder = 999;\n group.add(mesh);\n\n // 2. High-visibility outline frame strictly on the bounding box boundary\n const edges = new THREE.EdgesGeometry(geom);\n const borderMat = new THREE.LineBasicMaterial({\n color: borderColor,\n transparent: true,\n opacity: 0.85,\n });\n const border = new THREE.LineSegments(edges, borderMat);\n group.add(border);\n\n // 3. Center Sectioning Handle (compact, elegant, strictly inside bounding box)\n const arrowGroup = new THREE.Group();\n const handleScale = Math.min(w, h) * 0.045;\n const arrowMat = new THREE.MeshBasicMaterial({\n color: borderColor,\n transparent: true,\n opacity: 0.9,\n depthTest: false,\n });\n\n // Center Sphere\n const sphereGeom = new THREE.SphereGeometry(handleScale * 0.5, 16, 16);\n const centerSphere = new THREE.Mesh(sphereGeom, arrowMat);\n centerSphere.userData = { axis };\n arrowGroup.add(centerSphere);\n\n // Thin Shaft along normal\n const shaftGeom = new THREE.CylinderGeometry(handleScale * 0.12, handleScale * 0.12, handleScale * 1.8, 12);\n shaftGeom.rotateX(Math.PI / 2);\n const shaftMesh = new THREE.Mesh(shaftGeom, arrowMat);\n shaftMesh.userData = { axis };\n arrowGroup.add(shaftMesh);\n\n // Positive Arrow Cone\n const coneGeom1 = new THREE.ConeGeometry(handleScale * 0.35, handleScale * 0.65, 16);\n coneGeom1.rotateX(Math.PI / 2);\n coneGeom1.translate(0, 0, handleScale * 1.1);\n const coneMesh1 = new THREE.Mesh(coneGeom1, arrowMat);\n coneMesh1.userData = { axis };\n arrowGroup.add(coneMesh1);\n\n // Negative Arrow Cone\n const coneGeom2 = new THREE.ConeGeometry(handleScale * 0.35, handleScale * 0.65, 16);\n coneGeom2.rotateX(-Math.PI / 2);\n coneGeom2.translate(0, 0, -handleScale * 1.1);\n const coneMesh2 = new THREE.Mesh(coneGeom2, arrowMat);\n coneMesh2.userData = { axis };\n arrowGroup.add(coneMesh2);\n\n arrowGroup.renderOrder = 1000;\n group.add(arrowGroup);\n\n // All clickable meshes for raycasting\n const hitMeshes = [mesh, centerSphere, shaftMesh, coneMesh1, coneMesh2];\n\n // 4. Ergonomic Corner CSS2D badge positioned well inside the plane boundary\n const div = document.createElement('div');\n div.className = `gmv-plane-badge gmv-plane-badge-${axis}`;\n div.textContent = `${axis.toUpperCase()} 剖切面`;\n const label = new CSS2DObject(div);\n label.position.set((w / 2) * 0.68, (h / 2) * 0.68, 0);\n group.add(label);\n\n let normal = new THREE.Vector3(1, 0, 0);\n\n if (axis === 'x') {\n group.rotation.y = Math.PI / 2;\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(1, 0, 0);\n } else if (axis === 'y') {\n group.rotation.x = -Math.PI / 2;\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(0, 1, 0);\n } else {\n group.rotation.set(0, 0, 0);\n group.position.set(center.x, center.y, center.z);\n normal = new THREE.Vector3(0, 0, 1);\n }\n\n this.planesGroup.add(group);\n\n return {\n axis,\n group,\n mesh,\n border,\n arrowGroup,\n hitMeshes,\n label,\n labelText: div,\n meshMat,\n borderMat,\n arrowMat,\n normal,\n baseColor,\n borderColor,\n position: center[axis],\n inverted: false,\n enabled: false,\n };\n }\n\n private clearPlaneWidgets(): void {\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const widget = this.widgets[axis];\n if (widget) {\n widget.mesh.geometry.dispose();\n widget.meshMat.dispose();\n widget.border.geometry.dispose();\n widget.borderMat.dispose();\n widget.arrowMat.dispose();\n this.planesGroup.remove(widget.group);\n }\n });\n this.widgets = {};\n }\n\n // ==========================================\n // Visibility & Direction Controls (Multi-Axis Capable)\n // ==========================================\n\n private updateWidgetsVisibility(): void {\n this.planesGroup.visible = this.isEnabled && this.helperVisible;\n this.interactive = this.isEnabled;\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w) {\n // Multi-axis support: Any enabled axis will show its translucent helper plane\n w.group.visible = this.isEnabled && this.helperVisible && w.enabled;\n }\n });\n }\n\n /**\n * 1. 开启 / 关闭 剖切总开关 (Enable/Disable Sectioning)\n */\n public setSectioningEnabled(enabled: boolean): void {\n this.isEnabled = enabled;\n this.updateWidgetsVisibility();\n\n if (enabled) {\n // Ensure at least one axis is enabled if all were disabled\n const hasAnyAxis = (['x', 'y', 'z'] as ClippingAxis[]).some((a) => this.widgets[a]?.enabled);\n if (!hasAnyAxis) {\n const defaultW = this.widgets[this.activeAxis] || this.widgets.x;\n if (defaultW) defaultW.enabled = true;\n }\n\n // Re-apply positions for all active axes\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w && w.enabled) {\n this.setPlanePositionExplicit(axis, w.position);\n } else {\n this.disableAxisInternal(axis);\n }\n });\n } else {\n // Disabling sectioning clears all cuts from the model\n this.disableAllAxesPlanes();\n }\n\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public getSectioningEnabled(): boolean {\n return this.isEnabled;\n }\n\n /**\n * 2. 独立开启或关闭某个轴的剖切 (Multi-Axis Toggle)\n * 支持 X、Y、Z 任意多轴同时开启与同时剖切!\n */\n public setAxisEnabled(axis: ClippingAxis, enabled: boolean): void {\n const w = this.widgets[axis];\n if (!w) return;\n\n w.enabled = enabled;\n if (enabled) {\n this.activeAxis = axis;\n if (this.isEnabled) {\n this.setPlanePositionExplicit(axis, w.position);\n }\n } else {\n this.disableAxisInternal(axis);\n }\n\n this.updateWidgetsVisibility();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public toggleAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n const w = this.widgets[axis];\n const newState = enabled !== undefined ? enabled : !(w?.enabled);\n this.setAxisEnabled(axis, newState);\n return newState;\n }\n\n public isAxisEnabled(axis: ClippingAxis): boolean {\n return !!(this.widgets[axis]?.enabled);\n }\n\n /**\n * 设置激活/聚焦的轴向 (兼容已有调用)\n */\n public setSectioningAxis(axis: ClippingAxis): void {\n this.activeAxis = axis;\n const w = this.widgets[axis];\n if (w && !w.enabled) {\n w.enabled = true;\n if (this.isEnabled) {\n this.setPlanePositionExplicit(axis, w.position);\n }\n }\n this.updateWidgetsVisibility();\n this.emitSectioningState();\n }\n\n public getSectioningAxis(): ClippingAxis {\n return this.activeAxis;\n }\n\n /**\n * 3. 隐藏 / 显示 剖切辅助面 (Hide Sectional Plane while keeping the cut)\n */\n public setSectioningHelperVisible(visible: boolean): void {\n this.helperVisible = visible;\n this.updateWidgetsVisibility();\n this.emitSectioningState();\n }\n\n public getSectioningHelperVisible(): boolean {\n return this.helperVisible;\n }\n\n /**\n * 4. 独立反向某轴剖切 (Reverse Sectioning for a specific axis or active axis)\n */\n public setAxisReverse(axis: ClippingAxis, reverse: boolean): void {\n const widget = this.widgets[axis];\n if (widget) {\n widget.inverted = reverse;\n if (this.isEnabled && widget.enabled) {\n this.setPlanePositionExplicit(axis, widget.position);\n } else {\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (widget.position - min) / range : 0.5;\n widget.labelText.textContent = `${axis.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%${reverse ? ' (反向)' : ''}`;\n }\n }\n this.emitSectioningState();\n }\n\n public setSectioningReverse(reverse: boolean): void {\n this.setAxisReverse(this.activeAxis, reverse);\n }\n\n public getSectioningReverse(): boolean {\n return !!(this.widgets[this.activeAxis]?.inverted);\n }\n\n public invertAxis(axis: ClippingAxis): boolean {\n const w = this.widgets[axis];\n if (!w) return false;\n this.setAxisReverse(axis, !w.inverted);\n return w.inverted;\n }\n\n /**\n * 5. 重置剖切面至中心位置与默认正向状态\n */\n public resetSectioning(axis?: ClippingAxis): void {\n const center = new THREE.Vector3();\n const isEmpty = this.modelBox.isEmpty();\n if (!isEmpty) {\n this.modelBox.getCenter(center);\n } else {\n center.set(0, 0, 0);\n }\n\n // 1. 清除任何自定义 2 点切刀平面\n this.clearCustomPlane();\n\n // 2. 目标轴\n const targetAxes: ClippingAxis[] = axis ? [axis] : (['x', 'y', 'z'] as ClippingAxis[]);\n\n targetAxes.forEach((a) => {\n const widget = this.widgets[a];\n if (widget) {\n widget.position = center[a];\n widget.inverted = false;\n widget.group.position.set(center.x, center.y, center.z);\n if (!axis) {\n // 全局重置时,默认激活 X 轴,关闭 Y/Z 轴\n widget.enabled = a === 'x';\n }\n const min = isEmpty ? -1 : this.modelBox.min[a];\n const max = isEmpty ? 1 : this.modelBox.max[a];\n const range = max - min;\n const progress = range > 0 && !isNaN(widget.position) ? Math.max(0, Math.min(1, (widget.position - min) / range)) : 0.5;\n widget.labelText.textContent = `${a.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%`;\n }\n });\n\n this.activeAxis = axis || 'x';\n this.helperVisible = true;\n this.isEnabled = true; // 确保剖切开启,立即可见中心剖切面!\n\n // 3. 重新设置所有轴向的平面方程\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n const widget = this.widgets[a];\n if (widget && widget.enabled) {\n this.setPlanePositionExplicit(a, widget.position);\n } else {\n this.disableAxisInternal(a);\n }\n });\n\n this.updateWidgetsVisibility();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n /**\n * 获取多轴综合剖切状态\n */\n public getSectioningState(): SectioningState {\n const isEmpty = this.modelBox.isEmpty();\n const minX = isEmpty ? -1 : this.modelBox.min.x;\n const maxX = isEmpty ? 1 : this.modelBox.max.x;\n const minY = isEmpty ? -1 : this.modelBox.min.y;\n const maxY = isEmpty ? 1 : this.modelBox.max.y;\n const minZ = isEmpty ? -1 : this.modelBox.min.z;\n const maxZ = isEmpty ? 1 : this.modelBox.max.z;\n\n const calcProg = (pos: number, min: number, max: number) => {\n const range = max - min;\n if (isNaN(pos) || range <= 0) return 0.5;\n return Math.max(0, Math.min(1, (pos - min) / range));\n };\n\n const wx = this.widgets.x;\n const wy = this.widgets.y;\n const wz = this.widgets.z;\n\n const axes: Record<ClippingAxis, AxisSectionState> = {\n x: {\n enabled: wx?.enabled ?? false,\n inverted: wx?.inverted ?? false,\n position: wx?.position ?? 0,\n progress: calcProg(wx?.position ?? 0, minX, maxX),\n },\n y: {\n enabled: wy?.enabled ?? false,\n inverted: wy?.inverted ?? false,\n position: wy?.position ?? 0,\n progress: calcProg(wy?.position ?? 0, minY, maxY),\n },\n z: {\n enabled: wz?.enabled ?? false,\n inverted: wz?.inverted ?? false,\n position: wz?.position ?? 0,\n progress: calcProg(wz?.position ?? 0, minZ, maxZ),\n },\n };\n\n const activeW = this.widgets[this.activeAxis];\n const curAxisProg = axes[this.activeAxis]?.progress ?? 0.5;\n const curAxisPos = activeW?.position ?? 0;\n const curAxisInv = activeW?.inverted ?? false;\n\n return {\n enabled: this.isEnabled,\n axes,\n helperVisible: this.helperVisible,\n axis: this.activeAxis,\n isReverse: curAxisInv,\n progress: curAxisProg,\n position: curAxisPos,\n };\n }\n\n private emitSectioningState(): void {\n if (this.emitter) {\n this.emitter.emit('sectioning:change', this.getSectioningState());\n }\n }\n\n // ==========================================\n // Direct 3D Mouse Press & Drag Handling\n // Multi-Axis Pick: Pick and drag ANY active axis plane directly in 3D!\n // ==========================================\n\n private handlePointerDown(e: PointerEvent): void {\n if (e.button !== 0) return; // Only left mouse button\n\n if (this.isPickingCustomPoints) {\n this.handleCustomPointPick(e);\n e.stopImmediatePropagation();\n e.preventDefault();\n return;\n }\n\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n // Multi-Axis Pick: test against ALL currently enabled & visible plane widgets\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n if (candidateWidgets.length === 0) return;\n\n let closestHit: { widget: PlaneWidget; distance: number } | null = null;\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n const dist = intersects[0].distance;\n if (!closestHit || dist < closestHit.distance) {\n closestHit = { widget, distance: dist };\n }\n }\n }\n\n if (closestHit) {\n // CRITICAL: Stop event propagation in capture phase so TrackballControls NEVER starts rotating!\n e.stopImmediatePropagation();\n e.preventDefault();\n\n const activeWidget = closestHit.widget;\n this.activeAxis = activeWidget.axis;\n this.isDraggingPlane = true;\n this.draggedWidget = activeWidget;\n\n // Disable camera controls and clear any internal Trackball state immediately\n if (this.controls) {\n this.controls.enabled = false;\n if ('state' in this.controls) {\n (this.controls as any).state = -1;\n if ((this.controls as any)._moveCurr && (this.controls as any)._movePrev) {\n (this.controls as any)._moveCurr.copy((this.controls as any)._movePrev);\n }\n }\n }\n\n this.container.style.cursor = 'grabbing';\n activeWidget.meshMat.opacity = 0.65;\n activeWidget.arrowMat.opacity = 1.0;\n\n // Record start pointer client coordinates\n this.dragStartPointer.set(e.clientX, e.clientY);\n this.dragStartPosition = activeWidget.position;\n\n // Project the 3D unit axis vector into 2D screen space to enable smooth, jitter-free 1:1 dragging\n const axisVector = new THREE.Vector3();\n if (activeWidget.axis === 'x') axisVector.set(1, 0, 0);\n else if (activeWidget.axis === 'y') axisVector.set(0, 1, 0);\n else axisVector.set(0, 0, 1);\n\n const p0 = activeWidget.group.position.clone();\n const p1 = activeWidget.group.position.clone().add(axisVector);\n\n const ndc0 = p0.project(this.camera);\n const screen0 = new THREE.Vector2(\n ((ndc0.x + 1) * rect.width) / 2,\n ((-ndc0.y + 1) * rect.height) / 2\n );\n\n const ndc1 = p1.project(this.camera);\n const screen1 = new THREE.Vector2(\n ((ndc1.x + 1) * rect.width) / 2,\n ((-ndc1.y + 1) * rect.height) / 2\n );\n\n this.dragScreenAxis.subVectors(screen1, screen0);\n this.dragScreenAxisLenSq = this.dragScreenAxis.lengthSq();\n\n // Register global drag listeners in capture phase for buttery-smooth tracking\n window.addEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.addEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n }\n }\n\n private handlePointerMoveDrag(e: PointerEvent): void {\n if (!this.isDraggingPlane || !this.draggedWidget) return;\n\n e.stopImmediatePropagation();\n e.preventDefault();\n\n const deltaMouse = new THREE.Vector2(\n e.clientX - this.dragStartPointer.x,\n e.clientY - this.dragStartPointer.y\n );\n\n let deltaUnits = 0;\n if (this.dragScreenAxisLenSq > 25) {\n // Axis is tilted on screen: project mouse movement directly onto the screen axis direction\n deltaUnits = this.dragScreenAxis.dot(deltaMouse) / this.dragScreenAxisLenSq;\n } else {\n // Axis is viewed almost directly face-on: drag vertically to adjust depth\n const size = new THREE.Vector3();\n this.modelBox.getSize(size);\n const axisSpan = Math.max(0.1, size[this.draggedWidget.axis]);\n deltaUnits = (-deltaMouse.y / 280) * axisSpan;\n }\n\n const min = this.modelBox.min[this.draggedWidget.axis];\n const max = this.modelBox.max[this.draggedWidget.axis];\n\n let newPos = this.dragStartPosition + deltaUnits;\n newPos = Math.max(min, Math.min(max, newPos));\n\n this.setPlanePositionExplicit(this.draggedWidget.axis, newPos);\n }\n\n private handlePointerUpDrag(e: PointerEvent): void {\n if (!this.isDraggingPlane) return;\n\n e.stopImmediatePropagation();\n e.preventDefault();\n\n this.isDraggingPlane = false;\n\n if (this.draggedWidget) {\n this.draggedWidget.meshMat.opacity = 0.25;\n this.draggedWidget.arrowMat.opacity = 0.9;\n this.draggedWidget = null;\n }\n\n this.container.style.cursor = 'default';\n\n window.removeEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.removeEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n\n // CRITICAL: Restore camera controls in requestAnimationFrame to prevent lingering pointerup\n // from triggering unwanted camera momentum or spinning!\n requestAnimationFrame(() => {\n if (this.controls) {\n if ('state' in this.controls) {\n (this.controls as any).state = -1;\n if ((this.controls as any)._moveCurr && (this.controls as any)._movePrev) {\n (this.controls as any)._moveCurr.copy((this.controls as any)._movePrev);\n }\n }\n this.controls.enabled = true;\n }\n });\n }\n\n /**\n * Hover highlighting across all enabled planes\n */\n private handlePointerMove(e: PointerEvent): void {\n if (this.isDraggingPlane) return;\n if (this.isPickingCustomPoints) return;\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n let hoveredHitAxis: ClippingAxis | null = null;\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n hoveredHitAxis = widget.axis;\n break;\n }\n }\n\n this.setHoveredAxis(hoveredHitAxis);\n if (hoveredHitAxis) {\n this.container.style.cursor = 'grab';\n } else if (this.container.style.cursor === 'grab') {\n this.container.style.cursor = 'default';\n }\n }\n\n private setHoveredAxis(axis: ClippingAxis | null): void {\n if (this.hoveredAxis === axis) return;\n this.hoveredAxis = axis;\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n const w = this.widgets[a];\n if (w) {\n if (a === axis) {\n w.meshMat.opacity = 0.52;\n w.borderMat.opacity = 1.0;\n w.arrowMat.opacity = 1.0;\n } else {\n w.meshMat.opacity = 0.25;\n w.borderMat.opacity = 0.85;\n w.arrowMat.opacity = 0.9;\n }\n }\n });\n }\n\n /**\n * Double click on any plane quickly flips/inverts slicing direction of that specific plane\n */\n private handleDoubleClick(e: MouseEvent): void {\n if (!this.interactive || !this.isEnabled || !this.helperVisible) return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n this.raycaster.setFromCamera(mouse, this.camera);\n\n const candidateWidgets = (['x', 'y', 'z'] as ClippingAxis[])\n .map((axis) => this.widgets[axis])\n .filter((w): w is PlaneWidget => !!(w && w.enabled && w.group.visible));\n\n for (const widget of candidateWidgets) {\n const intersects = this.raycaster.intersectObjects(widget.hitMeshes, true);\n if (intersects.length > 0) {\n e.stopImmediatePropagation();\n e.preventDefault();\n this.invertAxis(widget.axis);\n break;\n }\n }\n }\n\n // ==========================================\n // Slicing Calculation & Updates\n // ==========================================\n\n public setPlanePositionExplicit(axis: ClippingAxis, pos: number): void {\n const widget = this.widgets[axis];\n if (!widget) return;\n\n widget.position = pos;\n\n if (axis === 'x') {\n widget.group.position.x = pos;\n if (widget.inverted) {\n this.planeX.set(new THREE.Vector3(1, 0, 0), -pos);\n } else {\n this.planeX.set(new THREE.Vector3(-1, 0, 0), pos);\n }\n } else if (axis === 'y') {\n widget.group.position.y = pos;\n if (widget.inverted) {\n this.planeY.set(new THREE.Vector3(0, 1, 0), -pos);\n } else {\n this.planeY.set(new THREE.Vector3(0, -1, 0), pos);\n }\n } else if (axis === 'z') {\n widget.group.position.z = pos;\n if (widget.inverted) {\n this.planeZ.set(new THREE.Vector3(0, 0, 1), -pos);\n } else {\n this.planeZ.set(new THREE.Vector3(0, 0, -1), pos);\n }\n }\n\n widget.enabled = true;\n this.applyPlanes();\n\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (pos - min) / range : 0.5;\n widget.labelText.textContent = `${axis.toUpperCase()} 剖切面 ${(progress * 100).toFixed(0)}%${widget.inverted ? ' (反向)' : ''}`;\n\n this.emitSectioningState();\n\n if (this.emitter) {\n this.emitter.emit('clipping:change', {\n axis,\n position: pos,\n progress,\n inverted: widget.inverted,\n enabled: true,\n });\n }\n }\n\n public setAxisProgress(axis: ClippingAxis, progress: number): void {\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const clampedProgress = Math.max(0, Math.min(1, progress));\n const pos = min + clampedProgress * (max - min);\n this.setPlanePositionExplicit(axis, pos);\n }\n\n public getAxisProgress(axis: ClippingAxis): number {\n const widget = this.widgets[axis];\n if (!widget) return 0.5;\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n return range > 0 ? Math.max(0, Math.min(1, (widget.position - min) / range)) : 0.5;\n }\n\n private disableAxisInternal(axis: ClippingAxis): void {\n if (axis === 'x') {\n this.planeX.set(new THREE.Vector3(-1, 0, 0), 100000);\n } else if (axis === 'y') {\n this.planeY.set(new THREE.Vector3(0, -1, 0), 100000);\n } else if (axis === 'z') {\n this.planeZ.set(new THREE.Vector3(0, 0, -1), 100000);\n }\n }\n\n private disableAllAxesPlanes(): void {\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((a) => {\n this.disableAxisInternal(a);\n });\n }\n\n // Backwards compatibility helper methods\n public setAxisClipping(axis: ClippingAxis, progress: number, inverted: boolean = false): void {\n this.activeAxis = axis;\n const widget = this.widgets[axis];\n if (widget) {\n widget.inverted = inverted;\n widget.enabled = true;\n }\n this.setSectioningEnabled(true);\n this.setAxisProgress(axis, progress);\n }\n\n public disableAxis(axis: ClippingAxis): void {\n this.setAxisEnabled(axis, false);\n }\n\n public getAxisState(axis: ClippingAxis): {\n enabled: boolean;\n progress: number;\n inverted: boolean;\n position: number;\n } {\n const widget = this.widgets[axis];\n if (!widget) {\n return { enabled: false, progress: 0.5, inverted: false, position: 0 };\n }\n const min = this.modelBox.min[axis];\n const max = this.modelBox.max[axis];\n const range = max - min;\n const progress = range > 0 ? (widget.position - min) / range : 0.5;\n return {\n enabled: this.isEnabled && widget.enabled,\n progress,\n inverted: widget.inverted,\n position: widget.position,\n };\n }\n\n public setPlanesVisible(visible: boolean): void {\n this.setSectioningHelperVisible(visible);\n }\n\n public getPlanesVisible(): boolean {\n return this.helperVisible;\n }\n\n // ==========================================\n // Custom 2-Point Plane Slicing (Knife Cut)\n // ==========================================\n\n public startCustomClip(): void {\n this.cancelCustomClip();\n this.isPickingCustomPoints = true;\n this.customPoints = [];\n this.container.style.cursor = 'crosshair';\n }\n\n public cancelCustomClip(): void {\n this.isPickingCustomPoints = false;\n this.container.style.cursor = 'default';\n this.clearCustomMarkers();\n }\n\n public clearCustomPlane(): void {\n this.customPlane = null;\n this.clearCustomMarkers();\n this.applyPlanes();\n }\n\n public invertCustomClip(): boolean {\n if (this.customPlane) {\n this.customPlane.negate();\n this.applyPlanes();\n return true;\n }\n return false;\n }\n\n public hasCustomPlane(): boolean {\n return this.customPlane !== null;\n }\n\n private handleCustomPointPick(e: PointerEvent): void {\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n // Only clicks on model surface\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n if (!snap.hit || !snap.point) {\n return;\n }\n\n this.addCustomPoint(snap.point);\n }\n\n private addCustomPoint(point: THREE.Vector3): void {\n this.customPoints.push(point.clone());\n\n const markerGeom = new THREE.SphereGeometry(1, 16, 16);\n const markerMat = new THREE.MeshBasicMaterial({ color: 0x38bdf8, depthTest: false });\n const marker = new THREE.Mesh(markerGeom, markerMat);\n marker.position.copy(point);\n marker.userData = { fixedScreenRadius: 5.5 };\n this.customMarkersGroup.add(marker);\n this.updateSingleMarkerScale(marker);\n\n if (this.customPoints.length === 2) {\n const p1 = this.customPoints[0];\n const p2 = this.customPoints[1];\n\n const cutLine = new THREE.Vector3().subVectors(p2, p1);\n if (cutLine.lengthSq() < 0.00001) {\n this.customPoints.pop();\n this.customMarkersGroup.remove(marker);\n return;\n }\n cutLine.normalize();\n\n const viewDir = new THREE.Vector3();\n this.camera.getWorldDirection(viewDir);\n\n let normal = new THREE.Vector3().crossVectors(cutLine, viewDir).normalize();\n if (normal.lengthSq() < 0.0001) {\n normal = new THREE.Vector3().crossVectors(cutLine, this.camera.up).normalize();\n }\n\n this.customPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, p1);\n\n this.clearCustomMarkers();\n this.cancelCustomClip();\n this.applyPlanes();\n }\n }\n\n /**\n * Updates custom knife markers to maintain constant pixel size on screen\n */\n public updatePerFrame(viewportHeight?: number): void {\n if (this.customMarkersGroup.children.length === 0) return;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n this.customMarkersGroup.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n }\n\n private updateSingleMarkerScale(obj: THREE.Object3D, viewportHeight?: number): void {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n }\n\n private clearCustomMarkers(): void {\n while (this.customMarkersGroup.children.length > 0) {\n const obj = this.customMarkersGroup.children[0] as THREE.Mesh;\n if (obj.geometry) obj.geometry.dispose();\n if (obj.material) (obj.material as THREE.Material).dispose();\n this.customMarkersGroup.remove(obj);\n }\n }\n\n /**\n * Applies all currently active clipping planes to model materials simultaneously!\n */\n private applyPlanes(): void {\n const activePlanes: THREE.Plane[] = [];\n\n if (this.isEnabled) {\n if (this.widgets.x?.enabled) activePlanes.push(this.planeX);\n if (this.widgets.y?.enabled) activePlanes.push(this.planeY);\n if (this.widgets.z?.enabled) activePlanes.push(this.planeZ);\n }\n\n if (this.customPlane) activePlanes.push(this.customPlane);\n\n this.modelLoader.applyClippingPlanes(activePlanes);\n }\n\n public resetAll(): void {\n this.isEnabled = false;\n this.activeAxis = 'x';\n this.helperVisible = true;\n\n this.disableAllAxesPlanes();\n\n const center = new THREE.Vector3();\n this.modelBox.getCenter(center);\n\n (['x', 'y', 'z'] as ClippingAxis[]).forEach((axis) => {\n const w = this.widgets[axis];\n if (w) {\n // By default, X is primed as the first active axis once enabled\n w.enabled = axis === 'x';\n w.inverted = false;\n w.position = center[axis];\n w.group.position.set(center.x, center.y, center.z);\n w.labelText.textContent = `${axis.toUpperCase()} 剖切面`;\n }\n });\n\n this.updateWidgetsVisibility();\n this.clearCustomPlane();\n this.applyPlanes();\n this.emitSectioningState();\n }\n\n public destroy(): void {\n this.cancelCustomClip();\n this.resetAll();\n this.clearPlaneWidgets();\n this.scene.remove(this.planesGroup);\n this.scene.remove(this.customMarkersGroup);\n\n this.container.removeEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.removeEventListener('pointermove', this.onPointerMoveBound);\n this.container.removeEventListener('dblclick', this.onDoubleClickBound, { capture: true });\n window.removeEventListener('pointermove', this.onPointerMoveDragBound, { capture: true });\n window.removeEventListener('pointerup', this.onPointerUpDragBound, { capture: true });\n }\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';\nimport { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { findSnapPoint } from '../utils/snapping';\nimport { compute3DPolygonArea, formatUnit } from '../utils/math';\nimport { EventEmitter } from '../core/EventEmitter';\nimport { MeasurementResult, MeasurementType } from '../types';\n\nexport class MeasurementTool {\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private container: HTMLElement;\n private modelLoader: ModelLoader;\n private emitter: EventEmitter;\n private controls: OrbitControls | TrackballControls | null = null;\n\n private currentMode: MeasurementType = 'none';\n private unit: string = 'm';\n\n // Measurements container\n private measurementGroup: THREE.Group;\n private results: MeasurementResult[] = [];\n\n // Active interaction points for point-to-point tools\n private activePoints: THREE.Vector3[] = [];\n private activePreviewGroup: THREE.Group;\n private activeMarkersGroup: THREE.Group;\n private activeLineGroup: THREE.Group;\n private snapCursorMesh: THREE.Mesh;\n\n // Event handlers\n private onPointerDownBound: (e: PointerEvent) => void;\n private onPointerMoveBound: (e: PointerEvent) => void;\n private onDoubleClickBound: (e: MouseEvent) => void;\n\n constructor(\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n container: HTMLElement,\n modelLoader: ModelLoader,\n emitter: EventEmitter,\n controls?: OrbitControls | TrackballControls\n ) {\n this.scene = scene;\n this.camera = camera;\n this.container = container;\n this.modelLoader = modelLoader;\n this.emitter = emitter;\n this.controls = controls || null;\n\n this.measurementGroup = new THREE.Group();\n this.measurementGroup.name = 'gmv-measurements-group';\n this.scene.add(this.measurementGroup);\n\n this.activePreviewGroup = new THREE.Group();\n this.activePreviewGroup.name = 'gmv-measure-preview-group';\n this.scene.add(this.activePreviewGroup);\n\n this.activeMarkersGroup = new THREE.Group();\n this.activeMarkersGroup.name = 'gmv-active-markers-group';\n this.activePreviewGroup.add(this.activeMarkersGroup);\n\n this.activeLineGroup = new THREE.Group();\n this.activeLineGroup.name = 'gmv-active-line-group';\n this.activePreviewGroup.add(this.activeLineGroup);\n\n // Glowing Snapping Cursor (Screen-space fixed size: never scales with model)\n const snapGeom = new THREE.SphereGeometry(1, 16, 16);\n const snapMat = new THREE.MeshBasicMaterial({\n color: 0x38bdf8,\n transparent: true,\n opacity: 0.9,\n depthTest: false,\n });\n this.snapCursorMesh = new THREE.Mesh(snapGeom, snapMat);\n this.snapCursorMesh.userData = { fixedScreenRadius: 5.5 };\n this.snapCursorMesh.visible = false;\n this.scene.add(this.snapCursorMesh);\n\n this.onPointerDownBound = this.handlePointerDown.bind(this);\n this.onPointerMoveBound = this.handlePointerMove.bind(this);\n this.onDoubleClickBound = this.handleDoubleClick.bind(this);\n }\n\n public setControls(controls: OrbitControls | TrackballControls): void {\n this.controls = controls;\n }\n\n public setUnit(unit: string): void {\n this.unit = unit;\n }\n\n public setMode(mode: MeasurementType): void {\n if (this.currentMode === mode) return;\n\n this.cancelActive();\n this.currentMode = mode;\n\n if (mode === 'none') {\n this.container.style.cursor = 'default';\n this.snapCursorMesh.visible = false;\n this.removeListeners();\n } else {\n this.container.style.cursor = 'crosshair';\n this.addListeners();\n this.emitter.emit('measure:start', mode);\n }\n }\n\n public getMode(): MeasurementType {\n return this.currentMode;\n }\n\n public getControls(): OrbitControls | TrackballControls | null {\n return this.controls;\n }\n\n private addListeners(): void {\n this.removeListeners();\n this.container.addEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.addEventListener('pointermove', this.onPointerMoveBound);\n this.container.addEventListener('dblclick', this.onDoubleClickBound);\n }\n\n private removeListeners(): void {\n this.container.removeEventListener('pointerdown', this.onPointerDownBound, { capture: true });\n this.container.removeEventListener('pointermove', this.onPointerMoveBound);\n this.container.removeEventListener('dblclick', this.onDoubleClickBound);\n }\n\n private handlePointerMove(e: PointerEvent): void {\n if (this.currentMode === 'none') return;\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n // Rule: Click & Snapping strictly allowed on model only\n if (!snap.hit || !snap.point) {\n this.container.style.cursor = 'not-allowed';\n this.snapCursorMesh.visible = false;\n return;\n }\n\n this.container.style.cursor = 'crosshair';\n this.snapCursorMesh.visible = true;\n this.snapCursorMesh.position.copy(snap.point);\n\n if (snap.isVertexSnapped) {\n (this.snapCursorMesh.material as THREE.MeshBasicMaterial).color.setHex(0xf43f5e);\n this.snapCursorMesh.userData.fixedScreenRadius = 7.5;\n } else {\n (this.snapCursorMesh.material as THREE.MeshBasicMaterial).color.setHex(0x38bdf8);\n this.snapCursorMesh.userData.fixedScreenRadius = 5.5;\n }\n this.updateSingleMeshScale(this.snapCursorMesh);\n\n // Dynamic Line Preview while moving mouse\n if (this.activePoints.length > 0) {\n this.updateDynamicPreview(snap.point);\n }\n }\n\n private handlePointerDown(e: PointerEvent): void {\n if (this.currentMode === 'none') return;\n if (e.button !== 0) return; // Left mouse button only\n\n const rect = this.container.getBoundingClientRect();\n const mouse = new THREE.Vector2(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1\n );\n\n const snap = findSnapPoint(\n mouse,\n this.camera,\n this.modelLoader.getMeshes(),\n 15,\n rect.width,\n rect.height\n );\n\n // STRICT CHECK: Model outside clicks are completely ignored!\n if (!snap.hit || !snap.point) {\n return;\n }\n\n const clickedPoint = snap.point.clone();\n this.activePoints.push(clickedPoint);\n this.addActiveMarker(clickedPoint);\n this.emitter.emit('measure:change', [...this.activePoints]);\n\n // Check completion criteria for single-shot measurements\n if (this.currentMode === 'distance' && this.activePoints.length === 2) {\n this.finishDistanceMeasurement();\n } else if (this.currentMode === 'height' && this.activePoints.length === 2) {\n this.finishHeightMeasurement();\n } else if (this.currentMode === 'angle' && this.activePoints.length === 3) {\n this.finishAngleMeasurement();\n }\n }\n\n private addActiveMarker(pos: THREE.Vector3): void {\n const geom = new THREE.SphereGeometry(1, 16, 16);\n const mat = new THREE.MeshBasicMaterial({ color: 0x38bdf8, depthTest: false });\n const sphere = new THREE.Mesh(geom, mat);\n sphere.position.copy(pos);\n sphere.userData = { fixedScreenRadius: 5.5 };\n this.activeMarkersGroup.add(sphere);\n this.updateSingleMeshScale(sphere);\n }\n\n private handleDoubleClick(_e: MouseEvent): void {\n if (this.currentMode === 'area' && this.activePoints.length >= 3) {\n this.finishAreaMeasurement();\n }\n }\n\n // ==========================================\n // Dynamic Preview\n // ==========================================\n\n private updateDynamicPreview(currentPoint: THREE.Vector3): void {\n this.clearActiveLine();\n\n const points = [...this.activePoints, currentPoint];\n if (points.length < 2) return;\n\n const geom = new THREE.BufferGeometry().setFromPoints(points);\n const mat = new THREE.LineDashedMaterial({\n color: 0x38bdf8,\n dashSize: 0.2,\n gapSize: 0.1,\n depthTest: false,\n });\n const line = new THREE.Line(geom, mat);\n line.computeLineDistances();\n this.activeLineGroup.add(line);\n }\n\n // ==========================================\n // Measurements Execution (Single-Shot: Exit after each measurement)\n // ==========================================\n\n private finishDistanceMeasurement(): void {\n const p1 = this.activePoints[0];\n const p2 = this.activePoints[1];\n const distance = p1.distanceTo(p2);\n const midPoint = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5);\n\n const group = new THREE.Group();\n\n // 1. Line\n const geom = new THREE.BufferGeometry().setFromPoints([p1, p2]);\n const mat = new THREE.LineBasicMaterial({ color: 0x3b82f6, linewidth: 2, depthTest: false });\n const line = new THREE.Line(geom, mat);\n group.add(line);\n\n // 2. End Spheres\n this.addEndSphere(group, p1, 0x3b82f6);\n this.addEndSphere(group, p2, 0x3b82f6);\n\n // 3. CSS2D Label\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge distance';\n div.innerHTML = `<span>距离:</span> <strong>${formatUnit(distance, this.unit)}</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(midPoint);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `dist_${Date.now()}`,\n type: 'distance',\n value: distance,\n formattedValue: formatUnit(distance, this.unit),\n unit: this.unit,\n points: [p1, p2],\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishHeightMeasurement(): void {\n const p1 = this.activePoints[0];\n const p2 = this.activePoints[1];\n\n const verticalHeight = Math.abs(p2.y - p1.y);\n const horizontalDistance = new THREE.Vector2(p1.x, p1.z).distanceTo(new THREE.Vector2(p2.x, p2.z));\n const totalDistance = p1.distanceTo(p2);\n\n const group = new THREE.Group();\n\n // Direct connecting line\n const directGeom = new THREE.BufferGeometry().setFromPoints([p1, p2]);\n const directMat = new THREE.LineDashedMaterial({ color: 0x94a3b8, dashSize: 0.15, gapSize: 0.08, depthTest: false });\n const directLine = new THREE.Line(directGeom, directMat);\n directLine.computeLineDistances();\n group.add(directLine);\n\n // Orthogonal right triangle lines\n const corner = new THREE.Vector3(p2.x, p1.y, p2.z);\n\n const hGeom = new THREE.BufferGeometry().setFromPoints([p1, corner]);\n const hMat = new THREE.LineBasicMaterial({ color: 0x10b981, depthTest: false });\n const hLine = new THREE.Line(hGeom, hMat);\n group.add(hLine);\n\n const vGeom = new THREE.BufferGeometry().setFromPoints([corner, p2]);\n const vMat = new THREE.LineBasicMaterial({ color: 0xef4444, linewidth: 2, depthTest: false });\n const vLine = new THREE.Line(vGeom, vMat);\n group.add(vLine);\n\n this.addEndSphere(group, p1, 0x10b981);\n this.addEndSphere(group, p2, 0xef4444);\n this.addEndSphere(group, corner, 0xfacc15, 4.0);\n\n // Height badge\n const vMid = new THREE.Vector3().addVectors(corner, p2).multiplyScalar(0.5);\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge height';\n div.innerHTML = `\n <div><span>高差 (ΔH):</span> <strong>${formatUnit(verticalHeight, this.unit)}</strong></div>\n <div style=\"font-size:10px;opacity:0.85;\">水平: ${formatUnit(horizontalDistance, this.unit)} · 直线: ${formatUnit(totalDistance, this.unit)}</div>\n `;\n const label = new CSS2DObject(div);\n label.position.copy(vMid);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `height_${Date.now()}`,\n type: 'height',\n value: verticalHeight,\n formattedValue: formatUnit(verticalHeight, this.unit),\n unit: this.unit,\n points: [p1, p2],\n extra: {\n verticalHeight,\n horizontalDistance,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishAreaMeasurement(): void {\n if (this.activePoints.length < 3) return;\n\n const points = [...this.activePoints];\n const area = compute3DPolygonArea(points);\n\n const group = new THREE.Group();\n\n // Outline polygon\n const loopPoints = [...points, points[0]];\n const lineGeom = new THREE.BufferGeometry().setFromPoints(loopPoints);\n const lineMat = new THREE.LineBasicMaterial({ color: 0x8b5cf6, linewidth: 2, depthTest: false });\n const line = new THREE.Line(lineGeom, lineMat);\n group.add(line);\n\n points.forEach((p) => this.addEndSphere(group, p, 0x8b5cf6));\n\n // Centroid\n const centroid = new THREE.Vector3();\n points.forEach((p) => centroid.add(p));\n centroid.divideScalar(points.length);\n\n // CSS2D Label\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge area';\n div.innerHTML = `<span>面积:</span> <strong>${formatUnit(area, `${this.unit}²`)}</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(centroid);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `area_${Date.now()}`,\n type: 'area',\n value: area,\n formattedValue: formatUnit(area, `${this.unit}²`),\n unit: `${this.unit}²`,\n points,\n extra: {\n area,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private finishAngleMeasurement(): void {\n if (this.activePoints.length !== 3) return;\n\n const p1 = this.activePoints[0];\n const vertex = this.activePoints[1];\n const p2 = this.activePoints[2];\n\n const v1 = new THREE.Vector3().subVectors(p1, vertex).normalize();\n const v2 = new THREE.Vector3().subVectors(p2, vertex).normalize();\n\n let dot = v1.dot(v2);\n dot = Math.max(-1, Math.min(1, dot));\n const angleRad = Math.acos(dot);\n const angleDeg = THREE.MathUtils.radToDeg(angleRad);\n\n const group = new THREE.Group();\n\n // Lines\n const geom1 = new THREE.BufferGeometry().setFromPoints([vertex, p1]);\n const geom2 = new THREE.BufferGeometry().setFromPoints([vertex, p2]);\n const mat = new THREE.LineBasicMaterial({ color: 0xf59e0b, linewidth: 2, depthTest: false });\n group.add(new THREE.Line(geom1, mat));\n group.add(new THREE.Line(geom2, mat));\n\n this.addEndSphere(group, p1, 0xf59e0b);\n this.addEndSphere(group, p2, 0xf59e0b);\n this.addEndSphere(group, vertex, 0xef4444, 6.5);\n\n // Small Arc indication\n const arcRadius = Math.min(p1.distanceTo(vertex), p2.distanceTo(vertex)) * 0.35 || 0.4;\n const arcPoints: THREE.Vector3[] = [];\n const segments = 16;\n for (let i = 0; i <= segments; i++) {\n const t = i / segments;\n const arcVec = new THREE.Vector3().copy(v1).lerp(v2, t).normalize().multiplyScalar(arcRadius);\n arcPoints.push(new THREE.Vector3().addVectors(vertex, arcVec));\n }\n const arcGeom = new THREE.BufferGeometry().setFromPoints(arcPoints);\n const arcMat = new THREE.LineBasicMaterial({ color: 0xfbbf24, depthTest: false });\n group.add(new THREE.Line(arcGeom, arcMat));\n\n // Label position\n const midDir = new THREE.Vector3().addVectors(v1, v2).normalize();\n const labelPos = vertex.clone().add(midDir.multiplyScalar(arcRadius * 1.3));\n\n const div = document.createElement('div');\n div.className = 'gmv-measure-badge angle';\n div.innerHTML = `<span>角度:</span> <strong>${angleDeg.toFixed(1)}°</strong>`;\n const label = new CSS2DObject(div);\n label.position.copy(labelPos);\n group.add(label);\n\n this.measurementGroup.add(group);\n\n const result: MeasurementResult = {\n id: `angle_${Date.now()}`,\n type: 'angle',\n value: angleDeg,\n formattedValue: `${angleDeg.toFixed(1)}°`,\n unit: '°',\n points: [p1, vertex, p2],\n extra: {\n angleDegree: angleDeg,\n },\n };\n\n this.results.push(result);\n this.emitter.emit('measure:complete', result);\n this.cancelActive();\n this.setMode('none'); // Single-shot: automatically exit mode\n }\n\n private addEndSphere(group: THREE.Group, pos: THREE.Vector3, color: number, pixelRadius: number = 5.5): void {\n const geom = new THREE.SphereGeometry(1, 16, 16);\n const mat = new THREE.MeshBasicMaterial({ color, depthTest: false });\n const sphere = new THREE.Mesh(geom, mat);\n sphere.position.copy(pos);\n sphere.userData = { fixedScreenRadius: pixelRadius };\n group.add(sphere);\n this.updateSingleMeshScale(sphere);\n }\n\n public undoLastPoint(): void {\n if (this.activePoints.length > 0) {\n this.activePoints.pop();\n if (this.activeMarkersGroup.children.length > 0) {\n const lastMarker = this.activeMarkersGroup.children[this.activeMarkersGroup.children.length - 1];\n this.disposeObjectHierarchy(lastMarker);\n this.activeMarkersGroup.remove(lastMarker);\n }\n this.clearActiveLine();\n this.emitter.emit('measure:change', [...this.activePoints]);\n }\n }\n\n public cancelActive(): void {\n this.activePoints = [];\n this.clearActiveLine();\n this.clearActiveMarkers();\n }\n\n private clearActiveLine(): void {\n while (this.activeLineGroup.children.length > 0) {\n const obj = this.activeLineGroup.children[0];\n this.disposeObjectHierarchy(obj);\n this.activeLineGroup.remove(obj);\n }\n }\n\n private clearActiveMarkers(): void {\n while (this.activeMarkersGroup.children.length > 0) {\n const obj = this.activeMarkersGroup.children[0];\n this.disposeObjectHierarchy(obj);\n this.activeMarkersGroup.remove(obj);\n }\n }\n\n /**\n * Updates scale of all marker spheres dynamically per frame so they maintain\n * a constant screen pixel size, completely independent of camera distance or model zoom!\n */\n public updatePerFrame(viewportHeight?: number): void {\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const updateMesh = (obj: THREE.Object3D) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n };\n\n if (this.snapCursorMesh && this.snapCursorMesh.visible) {\n updateMesh(this.snapCursorMesh);\n }\n\n this.activeMarkersGroup.traverse(updateMesh);\n this.measurementGroup.traverse(updateMesh);\n }\n\n private updateSingleMeshScale(obj: THREE.Object3D, viewportHeight?: number): void {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const h = viewportHeight || this.container.clientHeight || window.innerHeight || 800;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(h, 1);\n obj.scale.setScalar(worldRadius);\n }\n }\n\n /**\n * Completely clears ALL measurements and CSS2D DOM elements\n */\n public clearAll(): void {\n this.cancelActive();\n\n while (this.measurementGroup.children.length > 0) {\n const group = this.measurementGroup.children[0];\n this.disposeObjectHierarchy(group);\n this.measurementGroup.remove(group);\n }\n\n this.snapCursorMesh.visible = false;\n this.results = [];\n this.emitter.emit('measure:clear');\n }\n\n private disposeObjectHierarchy(obj: THREE.Object3D): void {\n obj.traverse((child) => {\n // Clean up CSS2D HTML elements from DOM tree\n if ((child as any).isCSS2DObject && (child as CSS2DObject).element) {\n const el = (child as CSS2DObject).element;\n if (el && el.parentElement) {\n el.parentElement.removeChild(el);\n } else if (el) {\n el.remove();\n }\n }\n\n if ((child as THREE.Mesh).isMesh || (child as THREE.Line).isLine) {\n const item = child as THREE.Mesh;\n if (item.geometry) item.geometry.dispose();\n if (item.material) {\n if (Array.isArray(item.material)) item.material.forEach((m) => m.dispose());\n else item.material.dispose();\n }\n }\n });\n }\n\n public getResults(): MeasurementResult[] {\n return [...this.results];\n }\n\n public destroy(): void {\n this.removeListeners();\n this.clearAll();\n this.scene.remove(this.measurementGroup);\n this.scene.remove(this.activePreviewGroup);\n this.scene.remove(this.snapCursorMesh);\n }\n}\n","import * as THREE from 'three';\nimport { ExportImageOptions } from '../types';\n\nexport class ScreenshotTool {\n private renderer: THREE.WebGLRenderer;\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private getModel: () => THREE.Object3D | null;\n\n constructor(\n renderer: THREE.WebGLRenderer,\n scene: THREE.Scene,\n camera: THREE.PerspectiveCamera,\n getModel?: () => THREE.Object3D | null\n ) {\n this.renderer = renderer;\n this.scene = scene;\n this.camera = camera;\n this.getModel = getModel || (() => null);\n }\n\n /**\n * Finds the maximum original texture resolution from meshes in the scene\n */\n public getSceneMaxTextureSize(): { width: number; height: number } | null {\n let maxWidth = 0;\n let maxHeight = 0;\n\n this.scene.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n const mesh = child as THREE.Mesh;\n const checkMat = (mat: THREE.Material) => {\n if ('map' in mat) {\n const m = mat as THREE.MeshStandardMaterial;\n if (m.map) {\n const img = (m.map.image || (m.map.source as unknown as { data?: { width?: number; height?: number } })?.data) as\n | { width?: number; height?: number }\n | undefined;\n if (img) {\n const w = img.width || 0;\n const h = img.height || 0;\n if (w > 0 && h > 0 && w * h > maxWidth * maxHeight) {\n maxWidth = w;\n maxHeight = h;\n }\n }\n }\n }\n };\n\n if (Array.isArray(mesh.material)) {\n mesh.material.forEach(checkMat);\n } else if (mesh.material) {\n checkMat(mesh.material);\n }\n }\n });\n\n if (maxWidth > 0 && maxHeight > 0) {\n return { width: maxWidth, height: maxHeight };\n }\n return null;\n }\n\n /**\n * Exports high-definition screenshot of the current view.\n * Supports 'original' / originalQuality which aligns directly to the model's actual texture resolution!\n */\n public async exportImage(options: ExportImageOptions = {}): Promise<string> {\n const isOriginal = options.originalQuality === true || options.scale === 'original';\n const scale = isOriginal ? 1 : (typeof options.scale === 'number' ? options.scale : 2);\n const type = options.type ?? 'image/png';\n const quality = options.quality ?? (type === 'image/png' ? 1 : 0.95);\n const download = options.download ?? false;\n\n // 1. Record original dimensions\n const originalSize = new THREE.Vector2();\n this.renderer.getSize(originalSize);\n const originalPixelRatio = this.renderer.getPixelRatio();\n const originalBackground = this.scene.background;\n\n const aspect = originalSize.x / originalSize.y;\n let targetWidth: number;\n let targetHeight: number;\n\n if (options.targetSize) {\n targetWidth = options.targetSize.width;\n targetHeight = options.targetSize.height;\n } else if (isOriginal) {\n // 原画质导出:按照模型实际贴图的真实原始尺寸计算输出分辨率\n const texSize = this.getSceneMaxTextureSize();\n if (texSize && texSize.width > 0 && texSize.height > 0) {\n const maxDim = Math.max(texSize.width, texSize.height);\n if (aspect >= 1) {\n targetWidth = maxDim;\n targetHeight = Math.round(maxDim / aspect);\n } else {\n targetHeight = maxDim;\n targetWidth = Math.round(maxDim * aspect);\n }\n } else {\n // 若无贴图,回退至高质量分辨率 (至少 2048)\n const baseDim = Math.max(Math.round(originalSize.x * originalPixelRatio), 2048);\n if (aspect >= 1) {\n targetWidth = baseDim;\n targetHeight = Math.round(baseDim / aspect);\n } else {\n targetHeight = baseDim;\n targetWidth = Math.round(baseDim * aspect);\n }\n }\n } else {\n targetWidth = Math.round(originalSize.x * scale);\n targetHeight = Math.round(originalSize.y * scale);\n }\n\n const filename = options.filename ?? `model-${isOriginal ? `texture-original-${targetWidth}x${targetHeight}` : `${scale}x`}-${Date.now()}.${type === 'image/jpeg' ? 'jpg' : 'png'}`;\n const modelOnly = options.modelOnly ?? true;\n\n // Track objects hidden during export\n const hiddenObjects: { obj: THREE.Object3D; wasVisible: boolean }[] = [];\n\n // Apply custom background if specified\n if (options.background !== undefined) {\n if (options.background === null) {\n this.scene.background = null;\n this.renderer.setClearAlpha(0);\n } else {\n this.scene.background = new THREE.Color(options.background);\n this.renderer.setClearAlpha(1);\n }\n }\n\n try {\n // If modelOnly is requested (default: true), hide all auxiliary scene objects (grid, bbox, measurements, clipping planes/helpers)\n if (modelOnly) {\n const currentModel = this.getModel();\n const isKeep = (obj: THREE.Object3D): boolean => {\n // Keep all lights and light groups so model illumination is preserved\n if ((obj as any).isLight) return true;\n if (obj.name === 'gmv-lights-group') return true;\n if (obj.children && obj.children.some((c: any) => c.isLight)) return true;\n\n // Keep the current model\n if (currentModel && (obj === currentModel || obj.uuid === currentModel.uuid)) return true;\n if (obj.name === 'gmv-current-model') return true;\n\n // Hide all other scene-level helpers/measurements/grids/planes\n return false;\n };\n\n for (const child of this.scene.children) {\n if (!isKeep(child) && child.visible) {\n hiddenObjects.push({ obj: child, wasVisible: true });\n child.visible = false;\n }\n }\n }\n\n this.renderer.setPixelRatio(1);\n this.renderer.setSize(targetWidth, targetHeight, false);\n this.camera.aspect = targetWidth / targetHeight;\n this.camera.updateProjectionMatrix();\n\n // Scale any fixed-screen-size markers proportionally to the target high-res height\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n this.scene.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(targetHeight, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n\n // Render crisp frame\n this.renderer.render(this.scene, this.camera);\n\n // Extract data URL\n const dataUrl = this.renderer.domElement.toDataURL(type, quality);\n\n // 3. Trigger download if requested\n if (download) {\n const link = document.createElement('a');\n link.download = filename;\n link.href = dataUrl;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n\n return dataUrl;\n } finally {\n // Restore any auxiliary objects that were hidden for model-only export\n if (hiddenObjects.length > 0) {\n for (const { obj, wasVisible } of hiddenObjects) {\n obj.visible = wasVisible;\n }\n }\n\n // 4. Restore original renderer and camera setup\n this.scene.background = originalBackground;\n this.renderer.setPixelRatio(originalPixelRatio);\n this.renderer.setSize(originalSize.x, originalSize.y, true);\n this.camera.aspect = originalSize.x / originalSize.y;\n this.camera.updateProjectionMatrix();\n\n // Restore markers for original screen height\n const origH = originalSize.y;\n const vFovRad = (this.camera.fov * Math.PI) / 180;\n const camPos = this.camera.position;\n const tanHalfFov = Math.tan(vFovRad / 2);\n this.scene.traverse((obj) => {\n if (obj.userData && typeof obj.userData.fixedScreenRadius === 'number') {\n const radiusPx = obj.userData.fixedScreenRadius;\n const worldPos = new THREE.Vector3();\n obj.getWorldPosition(worldPos);\n const dist = worldPos.distanceTo(camPos);\n const worldRadius = (2 * dist * tanHalfFov * radiusPx) / Math.max(origH, 1);\n obj.scale.setScalar(worldRadius);\n }\n });\n\n this.renderer.render(this.scene, this.camera);\n }\n }\n}\n","import * as THREE from 'three';\nimport { SceneManager } from './SceneManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ModelLoader } from '../loaders/ModelLoader';\nimport { BoundingBoxTool } from '../tools/BoundingBoxTool';\nimport { CameraTool } from '../tools/CameraTool';\nimport { ClippingTool } from '../tools/ClippingTool';\nimport { MeasurementTool } from '../tools/MeasurementTool';\nimport { ScreenshotTool } from '../tools/ScreenshotTool';\nimport {\n ViewerOptions,\n ModelLoadResult,\n MeasurementType,\n MeasurementResult,\n ClippingAxis,\n CameraView,\n ExportImageOptions,\n BoundingBoxOptions,\n ViewerEvents,\n AutoRotateAxis,\n AutoRotateDirection,\n AutoRotateOptions,\n RotationLockAxis,\n ModelDisplayStyle,\n} from '../types';\n\nexport class Viewer extends EventEmitter {\n public sceneManager: SceneManager;\n public modelLoader: ModelLoader;\n public boundingBoxTool: BoundingBoxTool;\n public cameraTool: CameraTool;\n public clippingTool: ClippingTool;\n public measurementTool: MeasurementTool;\n public screenshotTool: ScreenshotTool;\n\n public currentModel: THREE.Group | null = null;\n public currentModelInfo: ModelLoadResult | null = null;\n private unit: string = 'm';\n private defaultShowBoundingBox: boolean = false;\n\n // Animations (GLTF / GLB)\n private animationMixer: THREE.AnimationMixer | null = null;\n private animationClips: THREE.AnimationClip[] = [];\n private activeAction: THREE.AnimationAction | null = null;\n private currentAnimationName: string | null = null;\n private animationClock: THREE.Clock = new THREE.Clock();\n private isAnimationActive: boolean = false;\n\n constructor(options: ViewerOptions) {\n super();\n\n this.unit = options.unit || 'm';\n this.defaultShowBoundingBox = options.showBoundingBox ?? false;\n\n // 1. Core Scene Manager\n this.sceneManager = new SceneManager(options);\n\n // 2. Model Loader with Draco, Meshopt and KTX2 Basis compression support\n this.modelLoader = new ModelLoader({\n dracoDecoderPath: options.dracoDecoderPath,\n ktx2TranscoderPath: options.ktx2TranscoderPath,\n renderer: this.sceneManager.renderer,\n });\n\n // 3. Tools\n this.boundingBoxTool = new BoundingBoxTool(this.sceneManager.scene);\n this.cameraTool = new CameraTool(\n this.sceneManager.camera,\n this.sceneManager.controls,\n this.sceneManager.tweenGroup\n );\n this.clippingTool = new ClippingTool(\n this.sceneManager.scene,\n this.sceneManager.camera,\n this.sceneManager.container,\n this.modelLoader,\n this.sceneManager.controls,\n this\n );\n this.measurementTool = new MeasurementTool(\n this.sceneManager.scene,\n this.sceneManager.camera,\n this.sceneManager.container,\n this.modelLoader,\n this,\n this.sceneManager.controls\n );\n this.measurementTool.setUnit(this.unit);\n\n this.screenshotTool = new ScreenshotTool(\n this.sceneManager.renderer,\n this.sceneManager.scene,\n this.sceneManager.camera,\n () => this.currentModel\n );\n\n // Forward internal measurement events\n this.measurementTool.setUnit(this.unit);\n\n // Dynamic per-frame updates: track bounding box edges, fixed-size markers, auto-rotation, and animations\n this.sceneManager.onBeforeRender = () => {\n if (this.animationMixer && this.isAnimationActive) {\n const delta = this.animationClock.getDelta();\n this.animationMixer.update(delta);\n }\n this.boundingBoxTool.update(this.sceneManager.camera);\n this.cameraTool.updatePerFrame();\n this.measurementTool.updatePerFrame();\n this.clippingTool.updatePerFrame();\n };\n }\n\n // ==========================================\n // Model Loading\n // ==========================================\n\n /**\n * Load model from a ZIP file containing .obj, .mtl and textures\n */\n public async loadZip(source: File | Blob | ArrayBuffer | string): Promise<ModelLoadResult> {\n try {\n this.emit('load:start', typeof source === 'string' ? source : 'ZIP File');\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadZip(source, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Load model directly from OBJ URL and optional MTL URL\n */\n public async loadOBJ(objUrl: string, mtlUrl?: string): Promise<ModelLoadResult> {\n try {\n this.emit('load:start', objUrl);\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadOBJ(objUrl, mtlUrl, undefined, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Load model from a GLTF URL or ArrayBuffer\n */\n public async loadGLTF(source: string | ArrayBuffer): Promise<ModelLoadResult> {\n try {\n this.emit('load:start', typeof source === 'string' ? source : 'GLTF Data');\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadGLTF(source, undefined, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Load GLB binary model from File, Blob, ArrayBuffer, or online URL\n */\n public async loadGLB(source: string | ArrayBuffer | Blob | File): Promise<ModelLoadResult> {\n try {\n const name = typeof source === 'string' ? source : (source instanceof File ? source.name : 'GLB Model');\n this.emit('load:start', name);\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadGLB(source, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Automatically detect and load multiple selected or drag-dropped files\n * (Supports .zip, .glb, .gltf + companion .bin / textures, .obj + .mtl / textures)\n */\n public async loadFiles(files: FileList | File[]): Promise<ModelLoadResult> {\n try {\n const fileArr = Array.from(files);\n const mainName = fileArr[0]?.name || 'Files';\n this.emit('load:start', mainName);\n this.clearModel();\n\n const onProgress = (percent: number, message: string) => {\n this.emit('load:progress', percent, message);\n };\n\n const result = await this.modelLoader.loadFiles(files, onProgress);\n this.setupLoadedModel(result);\n this.emit('load:success', result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('load:error', error);\n throw error;\n }\n }\n\n /**\n * Automatically load 3D model from an online URL (.glb, .gltf, .zip, .obj)\n */\n public async loadFromUrl(url: string): Promise<ModelLoadResult> {\n const cleanUrl = url.split('?')[0].toLowerCase();\n if (cleanUrl.endsWith('.glb')) {\n return this.loadGLB(url);\n }\n if (cleanUrl.endsWith('.gltf')) {\n return this.loadGLTF(url);\n }\n if (cleanUrl.endsWith('.zip')) {\n return this.loadZip(url);\n }\n if (cleanUrl.endsWith('.obj')) {\n return this.loadOBJ(url);\n }\n // Fallback: try glb -> gltf -> zip -> obj\n try {\n return await this.loadGLB(url);\n } catch (_) {\n try {\n return await this.loadZip(url);\n } catch (_2) {\n return await this.loadOBJ(url);\n }\n }\n }\n\n /**\n * Loads a built-in sample geometry (convenient for immediate testing and demo playground)\n */\n public loadSampleGeometry(type: 'mechanical' | 'building' | 'torusKnot' = 'mechanical'): ModelLoadResult {\n this.clearModel();\n const group = new THREE.Group();\n\n if (type === 'mechanical') {\n // Create a mechanical-like assembly\n const baseGeom = new THREE.BoxGeometry(6, 1.2, 4);\n const baseMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });\n const baseMesh = new THREE.Mesh(baseGeom, baseMat);\n baseMesh.position.y = 0.6;\n group.add(baseMesh);\n\n const cylinderGeom = new THREE.CylinderGeometry(1.2, 1.2, 3, 32);\n const cylinderMat = new THREE.MeshStandardMaterial({ color: 0x0284c7, metalness: 0.6, roughness: 0.2 });\n const cylMesh = new THREE.Mesh(cylinderGeom, cylinderMat);\n cylMesh.position.set(0, 2.7, 0);\n group.add(cylMesh);\n\n const ringGeom = new THREE.TorusGeometry(1.6, 0.25, 16, 40);\n const ringMat = new THREE.MeshStandardMaterial({ color: 0xe11d48, metalness: 0.7, roughness: 0.2 });\n const ringMesh = new THREE.Mesh(ringGeom, ringMat);\n ringMesh.rotation.x = Math.PI / 2;\n ringMesh.position.set(0, 3.2, 0);\n group.add(ringMesh);\n } else if (type === 'building') {\n const bldgGeom = new THREE.BoxGeometry(4, 10, 4);\n const bldgMat = new THREE.MeshStandardMaterial({ color: 0x334155, roughness: 0.4 });\n const bldg = new THREE.Mesh(bldgGeom, bldgMat);\n bldg.position.y = 5;\n group.add(bldg);\n\n const roofGeom = new THREE.ConeGeometry(3, 2.5, 4);\n const roofMat = new THREE.MeshStandardMaterial({ color: 0xb91c1c, roughness: 0.5 });\n const roof = new THREE.Mesh(roofGeom, roofMat);\n roof.position.y = 11.25;\n roof.rotation.y = Math.PI / 4;\n group.add(roof);\n } else {\n const torusGeom = new THREE.TorusKnotGeometry(2.5, 0.7, 100, 16);\n const torusMat = new THREE.MeshStandardMaterial({ color: 0x8b5cf6, metalness: 0.5, roughness: 0.2 });\n const mesh = new THREE.Mesh(torusGeom, torusMat);\n mesh.position.y = 3;\n group.add(mesh);\n }\n\n const postProcessed = this.modelLoader.postProcessModel(group);\n this.setupLoadedModel(postProcessed);\n this.emit('load:success', postProcessed);\n return postProcessed;\n }\n\n private setupLoadedModel(result: ModelLoadResult): void {\n this.currentModel = result.model;\n this.currentModel.name = 'gmv-current-model';\n this.currentModelInfo = result;\n\n this.sceneManager.scene.add(this.currentModel);\n\n // Setup animations if present in GLTF/GLB\n if (result.animations && result.animations.length > 0) {\n this.animationClips = result.animations;\n this.animationMixer = new THREE.AnimationMixer(this.currentModel);\n this.playAnimation(0);\n } else {\n this.animationClips = [];\n this.animationMixer = null;\n this.activeAction = null;\n this.currentAnimationName = null;\n this.isAnimationActive = false;\n }\n\n // Attach dynamic dimensional bounding box (default: hidden)\n this.boundingBoxTool.attachToModel(this.currentModel, {\n visible: this.defaultShowBoundingBox,\n unit: this.unit,\n });\n\n // Feed bounds to clipping tool\n this.clippingTool.setModelBounds(result.box);\n\n // Default view when model is loaded: Front View (前视图)\n this.cameraTool.setView('front', this.currentModel, 0);\n }\n\n public clearModel(): void {\n if (this.animationMixer) {\n this.animationMixer.stopAllAction();\n if (this.currentModel) {\n this.animationMixer.uncacheRoot(this.currentModel);\n }\n this.animationMixer = null;\n }\n this.animationClips = [];\n this.activeAction = null;\n this.currentAnimationName = null;\n this.isAnimationActive = false;\n\n this.measurementTool.clearAll();\n this.clippingTool.resetAll();\n this.boundingBoxTool.detach();\n\n if (this.currentModel) {\n this.sceneManager.scene.remove(this.currentModel);\n this.currentModel = null;\n }\n this.currentModelInfo = null;\n this.modelLoader.cleanup();\n }\n\n // ==========================================\n // Appearance & Background\n // ==========================================\n\n public setBackground(color: string | number | null, alpha?: boolean): void {\n this.sceneManager.setBackground(color, alpha);\n }\n\n /**\n * Set model display style: 'solid' (实体), 'wireframe' (网格线框)\n */\n public setModelDisplayStyle(style: ModelDisplayStyle): void {\n this.modelLoader.setDisplayStyle(style);\n this.emit('style:change', style);\n }\n\n public getModelDisplayStyle(): ModelDisplayStyle {\n return this.modelLoader.getDisplayStyle();\n }\n\n public toggleTexture(showTexture: boolean): void {\n this.modelLoader.toggleTexture(showTexture);\n this.emit('texture:toggle', showTexture);\n }\n\n public getTextureState(): boolean {\n return this.modelLoader.getTextureState();\n }\n\n /**\n * Set color for model when texture is turned off (defaults to Digital3DCloud CAD blue 0x4c8cd6)\n */\n public setPlainColor(color: THREE.ColorRepresentation): void {\n this.modelLoader.setPlainColor(color);\n }\n\n public getPlainColor(): number {\n return this.modelLoader.getPlainColor();\n }\n\n /**\n * Retrieves the maximum original texture resolution of the current model\n */\n public getModelTextureSize(): { width: number; height: number } | null {\n return this.modelLoader.getModelTextureSize();\n }\n\n public setGridVisible(visible: boolean): void {\n this.sceneManager.gridHelper.visible = visible;\n }\n\n // ==========================================\n // Bounding Box & Dimensions (L, W, H)\n // ==========================================\n\n public showBoundingBox(visible: boolean, options?: Partial<BoundingBoxOptions>): void {\n this.boundingBoxTool.setVisible(visible);\n if (options?.unit) {\n this.unit = options.unit;\n this.boundingBoxTool.setUnit(options.unit);\n this.measurementTool.setUnit(options.unit);\n }\n }\n\n public getBoundingBoxVisible(): boolean {\n return this.boundingBoxTool.getVisible();\n }\n\n // ==========================================\n // Slicing / Clipping Tools\n // ==========================================\n\n public setClipping(axis: ClippingAxis, progress: number, inverted: boolean = false): void {\n this.clippingTool.setAxisClipping(axis, progress, inverted);\n }\n\n public disableClippingAxis(axis: ClippingAxis): void {\n this.clippingTool.disableAxis(axis);\n }\n\n public toggleClippingAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n return this.clippingTool.toggleAxis(axis, enabled);\n }\n\n public invertClippingAxis(axis: ClippingAxis): boolean {\n return this.clippingTool.invertAxis(axis);\n }\n\n public getClippingAxisState(axis: ClippingAxis) {\n return this.clippingTool.getAxisState(axis);\n }\n\n public setClippingPlanesVisible(visible: boolean): void {\n this.clippingTool.setPlanesVisible(visible);\n }\n\n public getClippingPlanesVisible(): boolean {\n return this.clippingTool.getPlanesVisible();\n }\n\n // Digital3DCloud Standard Multi-Axis Sectioning API\n public setSectioningEnabled(enabled: boolean): void {\n this.clippingTool.setSectioningEnabled(enabled);\n }\n\n public getSectioningEnabled(): boolean {\n return this.clippingTool.getSectioningEnabled();\n }\n\n public setSectioningAxis(axis: ClippingAxis): void {\n this.clippingTool.setSectioningAxis(axis);\n }\n\n public getSectioningAxis(): ClippingAxis {\n return this.clippingTool.getSectioningAxis();\n }\n\n public setSectioningAxisEnabled(axis: ClippingAxis, enabled: boolean): void {\n this.clippingTool.setAxisEnabled(axis, enabled);\n }\n\n public toggleSectioningAxis(axis: ClippingAxis, enabled?: boolean): boolean {\n return this.clippingTool.toggleAxis(axis, enabled);\n }\n\n public isSectioningAxisEnabled(axis: ClippingAxis): boolean {\n return this.clippingTool.isAxisEnabled(axis);\n }\n\n public setSectioningAxisReverse(axis: ClippingAxis, reverse: boolean): void {\n this.clippingTool.setAxisReverse(axis, reverse);\n }\n\n public invertSectioningAxis(axis: ClippingAxis): boolean {\n return this.clippingTool.invertAxis(axis);\n }\n\n public setSectioningAxisProgress(axis: ClippingAxis, progress: number): void {\n this.clippingTool.setAxisProgress(axis, progress);\n }\n\n public getSectioningAxisProgress(axis: ClippingAxis): number {\n return this.clippingTool.getAxisProgress(axis);\n }\n\n public setSectioningHelperVisible(visible: boolean): void {\n this.clippingTool.setSectioningHelperVisible(visible);\n }\n\n public getSectioningHelperVisible(): boolean {\n return this.clippingTool.getSectioningHelperVisible();\n }\n\n public setSectioningReverse(reverse: boolean): void {\n this.clippingTool.setSectioningReverse(reverse);\n }\n\n public getSectioningReverse(): boolean {\n return this.clippingTool.getSectioningReverse();\n }\n\n public resetSectioning(axis?: ClippingAxis): void {\n this.clippingTool.resetSectioning(axis);\n }\n\n public getSectioningState() {\n return this.clippingTool.getSectioningState();\n }\n\n public startCustomClip(): void {\n this.measurementTool.setMode('none'); // avoid mode collision\n this.clippingTool.startCustomClip();\n }\n\n public cancelCustomClip(): void {\n this.clippingTool.cancelCustomClip();\n }\n\n public clearCustomClip(): void {\n this.clippingTool.clearCustomPlane();\n }\n\n public invertCustomClip(): boolean {\n return this.clippingTool.invertCustomClip();\n }\n\n public hasCustomClip(): boolean {\n return this.clippingTool.hasCustomPlane();\n }\n\n public resetClipping(): void {\n this.clippingTool.resetAll();\n }\n\n // ==========================================\n // Measurement Tools\n // ==========================================\n\n public setMeasurementMode(mode: MeasurementType): void {\n if (mode !== 'none') {\n this.clippingTool.cancelCustomClip();\n this.clippingTool.setInteractive(false);\n } else {\n this.clippingTool.setInteractive(true);\n }\n this.measurementTool.setMode(mode);\n }\n\n public getMeasurementMode(): MeasurementType {\n return this.measurementTool.getMode();\n }\n\n public clearMeasurements(): void {\n this.measurementTool.clearAll();\n }\n\n public undoMeasurement(): void {\n this.measurementTool.undoLastPoint();\n }\n\n public getMeasurements(): MeasurementResult[] {\n return this.measurementTool.getResults();\n }\n\n // ==========================================\n // Camera & Views\n // ==========================================\n\n public async setView(view: CameraView, duration: number = 600): Promise<void> {\n if (!this.currentModel) return;\n await this.cameraTool.setView(view, this.currentModel, duration);\n this.emit('view:change', view);\n }\n\n public async fitView(duration: number = 500): Promise<void> {\n if (!this.currentModel) return;\n await this.cameraTool.fitToObject(this.currentModel, duration);\n }\n\n // ==========================================\n // Auto-Rotation & Axis Locking\n // ==========================================\n\n public setAutoRotate(\n enabled: boolean,\n axis?: AutoRotateAxis,\n direction?: AutoRotateDirection,\n speed?: number\n ): void {\n this.cameraTool.setAutoRotate(enabled, axis, direction, speed);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public toggleAutoRotate(): boolean {\n const state = this.cameraTool.toggleAutoRotate();\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n return state;\n }\n\n public setAutoRotateAxis(axis: AutoRotateAxis): void {\n this.cameraTool.setAutoRotateAxis(axis);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public setAutoRotateDirection(direction: AutoRotateDirection): void {\n this.cameraTool.setAutoRotateDirection(direction);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public setAutoRotateSpeed(speed: number): void {\n this.cameraTool.setAutoRotateSpeed(speed);\n this.emit('autorotate:change', this.cameraTool.getAutoRotateState());\n }\n\n public getAutoRotateState(): AutoRotateOptions {\n return this.cameraTool.getAutoRotateState();\n }\n\n public setRotationLockedAxis(axis: RotationLockAxis): void {\n this.cameraTool.setLockedAxis(axis);\n this.emit('axislock:change', axis);\n }\n\n public getRotationLockedAxis(): RotationLockAxis {\n return this.cameraTool.getLockedAxis();\n }\n\n // ==========================================\n // High-Resolution Export\n // ==========================================\n\n public async exportImage(options: ExportImageOptions = {}): Promise<string> {\n return this.screenshotTool.exportImage(options);\n }\n\n // Typed EventEmitter helper\n public override on<K extends keyof ViewerEvents>(event: K, listener: ViewerEvents[K]): this;\n public override on(event: string, listener: (...args: unknown[]) => void): this {\n return super.on(event, listener as (...args: unknown[]) => void);\n }\n\n public override off<K extends keyof ViewerEvents>(event: K, listener: ViewerEvents[K]): this;\n public override off(event: string, listener: (...args: unknown[]) => void): this {\n return super.off(event, listener as (...args: unknown[]) => void);\n }\n\n // ==========================================\n // Animation Controls (GLTF / GLB)\n // ==========================================\n\n /**\n * Returns list of animation clip names available in the current model\n */\n public getAnimations(): string[] {\n return this.animationClips.map((clip, index) => clip.name || `Animation ${index + 1}`);\n }\n\n /**\n * Returns name of the currently active animation\n */\n public getCurrentAnimation(): string | null {\n return this.currentAnimationName;\n }\n\n /**\n * Play an animation by name or clip index\n */\n public playAnimation(nameOrIndex: string | number = 0): void {\n if (!this.animationMixer || this.animationClips.length === 0) return;\n\n let targetClip: THREE.AnimationClip | undefined;\n if (typeof nameOrIndex === 'number') {\n targetClip = this.animationClips[nameOrIndex];\n } else {\n targetClip = this.animationClips.find(\n (c, idx) => c.name === nameOrIndex || `Animation ${idx + 1}` === nameOrIndex\n );\n }\n\n if (!targetClip) {\n targetClip = this.animationClips[0];\n }\n\n if (!targetClip) return;\n\n const newAction = this.animationMixer.clipAction(targetClip);\n\n if (this.activeAction && this.activeAction !== newAction) {\n this.activeAction.fadeOut(0.3);\n newAction.reset().fadeIn(0.3).play();\n } else {\n newAction.play();\n }\n\n this.activeAction = newAction;\n this.currentAnimationName = targetClip.name || 'Animation 1';\n this.isAnimationActive = true;\n this.animationClock.getDelta(); // reset delta to prevent time jump\n\n this.emit('animation:play', this.currentAnimationName);\n }\n\n /**\n * Pause playback of the active animation\n */\n public pauseAnimation(): void {\n this.isAnimationActive = false;\n this.emit('animation:pause');\n }\n\n /**\n * Resume playback of paused animation\n */\n public resumeAnimation(): void {\n if (this.animationMixer && this.activeAction) {\n this.isAnimationActive = true;\n this.animationClock.getDelta();\n this.emit('animation:play', this.currentAnimationName || '');\n }\n }\n\n /**\n * Stop active animation and reset to start frame\n */\n public stopAnimation(): void {\n this.isAnimationActive = false;\n if (this.activeAction) {\n this.activeAction.stop();\n }\n this.emit('animation:stop');\n }\n\n /**\n * Check whether animation is actively playing\n */\n public isAnimationPlaying(): boolean {\n return this.isAnimationActive;\n }\n\n /**\n * Set playback timeScale speed factor (e.g. 0.5, 1.0, 2.0)\n */\n public setAnimationSpeed(speed: number): void {\n if (this.animationMixer) {\n this.animationMixer.timeScale = Math.max(0, speed);\n this.emit('animation:speed', speed);\n }\n }\n\n // ==========================================\n // Cleanup\n // ==========================================\n\n public destroy(): void {\n this.clearModel();\n this.modelLoader.destroy();\n this.measurementTool.destroy();\n this.clippingTool.destroy();\n this.sceneManager.destroy();\n this.removeAllListeners();\n }\n}\n"],"mappings":"A,woCAOA,IAAa8D,EAAb,KAA0B,CACxB,UACA,MACA,OACA,SACA,cACA,SACA,YACA,WACA,WACA,eAEA,iBAA0C,KAC1C,eAAgD,KAChD,YAA+B,GAE/B,YAAY,EAAwB,CAClC,KAAK,UAAYC,EAAQ,UAGzB,KAAK,MAAQ,IAAI/D,EAAM,MACnB+D,EAAQ,aAAe,IAAA,IAAaA,EAAQ,aAAe,MACzD,OAAOA,EAAQ,YAAe,UAAY,OAAOA,EAAQ,YAAe,YAC1E,KAAK,MAAM,WAAa,IAAI/D,EAAM,MAAM+D,EAAQ,UAAU,GAG5D,KAAK,MAAM,WAAa,IAAI/D,EAAM,MAAM,OAAQ,EAIlD,IAAMgE,EAAQ,KAAK,UAAU,aAAe,IACtCC,EAAS,KAAK,UAAU,cAAgB,IAyC9C,GAxCA,KAAK,OAAS,IAAIjE,EAAM,kBACtB+D,EAAQ,WAAa,GACrBC,EAAQC,EACRF,EAAQ,YAAc,GACtBA,EAAQ,WAAa,GACvB,EACA,KAAK,OAAO,SAAS,IAAI,GAAI,GAAI,EAAE,EAGnC,KAAK,SAAW,IAAI/D,EAAM,cAAc,CACtC,UAAW+D,EAAQ,WAAa,GAChC,MAAOA,EAAQ,OAAS,GACxB,sBAAuB,GACvB,gBAAiB,kBACnB,CAAC,EACD,KAAK,SAAS,QAAQC,EAAOC,CAAM,EACnC,KAAK,SAAS,cAAc,KAAK,IAAI,OAAO,iBAAkB,CAAC,CAAC,EAChE,KAAK,SAAS,qBAAuB,GACrC,KAAK,SAAS,YAAcjE,EAAM,sBAClC,KAAK,SAAS,oBAAsB,EACpC,KAAK,SAAS,iBAAmBA,EAAM,eACvC,KAAK,SAAS,WAAW,MAAM,QAAU,QACzC,KAAK,SAAS,WAAW,MAAM,MAAQ,OACvC,KAAK,SAAS,WAAW,MAAM,OAAS,OACxC,KAAK,UAAU,YAAY,KAAK,SAAS,UAAU,EAGnD,KAAK,cAAgB,IAAIC,EAAAA,cACzB,KAAK,cAAc,QAAQ+D,EAAOC,CAAM,EACxC,KAAK,cAAc,WAAW,MAAM,SAAW,WAC/C,KAAK,cAAc,WAAW,MAAM,IAAM,IAC1C,KAAK,cAAc,WAAW,MAAM,KAAO,IAC3C,KAAK,cAAc,WAAW,MAAM,cAAgB,OACpD,KAAK,cAAc,WAAW,MAAM,MAAQ,OAC5C,KAAK,cAAc,WAAW,MAAM,OAAS,OAC7C,KAAK,cAAc,WAAW,UAAY,sBAC1C,KAAK,UAAU,YAAY,KAAK,cAAc,UAAU,GAGnCF,EAAQ,cAAgB,eACxB,QAAS,CAC5B,IAAMG,EAAQ,IAAIhE,EAAAA,cAAc,KAAK,OAAQ,KAAK,SAAS,UAAU,EACrE,EAAM,cAAgB,GACtB,EAAM,cAAgB,IACtB,EAAM,mBAAqB,GAC3B,KAAK,SAAWgE,CAClB,KAAO,CACL,IAAMC,EAAK,IAAIhE,EAAAA,kBAAkB,KAAK,OAAQ,KAAK,SAAS,UAAU,EACtE,EAAG,YAAc,IACjB,EAAG,UAAY,IACf,EAAG,SAAW,GACd,EAAG,aAAe,GAClB,EAAG,qBAAuB,IAC1B,KAAK,SAAWgE,CAClB,CAGA,KAAK,WAAa,IAAI/D,EAAM,MAG5B,KAAK,YAAc,IAAIJ,EAAM,MAC7B,KAAK,YAAY,KAAO,mBACxB,KAAK,WAAW,EAChB,KAAK,MAAM,IAAI,KAAK,WAAW,EAG/B,KAAK,WAAa,IAAIA,EAAM,WAAW,GAAI,GAAI,QAAU,OAAQ,EACjE,KAAK,WAAW,KAAO,kBACvB,KAAK,WAAW,SAAS,EAAI,EAC7B,KAAK,WAAW,QAAU,GAC1B,KAAK,MAAM,IAAI,KAAK,UAAU,EAG9B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EAGxB,0BAA4B,KAAK,aAAa,CAAC,EAC/C,eAAiB,KAAK,aAAa,EAAG,EAAE,CAC1C,CAEA,YAA2B,CAEzB,IAAMoE,EAAY,IAAIpE,EAAM,gBAAgB,SAAU,QAAU,CAAG,EACnEoE,EAAU,SAAS,IAAI,EAAG,GAAI,CAAC,EAC/B,KAAK,YAAY,IAAIA,CAAS,EAE9B,IAAMC,EAAe,IAAIrE,EAAM,aAAa,SAAU,EAAG,EACzD,KAAK,YAAY,IAAIqE,CAAY,EAGjC,IAAMC,EAAY,IAAItE,EAAM,iBAAiB,SAAU,GAAG,EAC1DsE,EAAU,SAAS,IAAI,GAAI,IAAK,EAAE,EAClC,KAAK,YAAY,IAAIA,CAAS,EAE9B,IAAMC,EAAY,IAAIvE,EAAM,iBAAiB,SAAU,EAAG,EAC1DuE,EAAU,SAAS,IAAI,IAAK,GAAI,GAAG,EACnC,KAAK,YAAY,IAAIA,CAAS,EAG9B,IAAMC,EAAY,IAAIxE,EAAM,iBAAiB,SAAU,EAAG,EAC1DwE,EAAU,SAAS,IAAI,EAAG,EAAG,CAAC,EAC9B,KAAK,OAAO,IAAIA,CAAS,EACzB,KAAK,MAAM,IAAI,KAAK,MAAM,CAC5B,CAEA,oBAAmC,CACjC,KAAK,eAAiB,IAAI,mBAAqB,CAC7C,KAAK,aAAa,CACpB,CAAC,EACD,KAAK,eAAe,QAAQ,KAAK,SAAS,CAC5C,CAEA,cAA4B,CAC1B,GAAI,KAAK,aAAe,CAAC,KAAK,UAAW,OACzC,IAAMR,EAAQ,KAAK,UAAU,YACvBC,EAAS,KAAK,UAAU,aAE1BD,IAAU,GAAKC,IAAW,IAE9B,KAAK,OAAO,OAASD,EAAQC,EAC7B,KAAK,OAAO,uBAAuB,EAEnC,KAAK,SAAS,QAAQD,EAAOC,CAAM,EACnC,KAAK,cAAc,QAAQD,EAAOC,CAAM,EAEpC,iBAAkB,KAAK,UAAY,OAAQ,KAAK,SAAqD,cAAiB,YACxH,KAAM,SAAqD,aAAa,EAE5E,CAEA,oBAAmC,CACjC,IAAMQ,MAAgB,CAChB,KAAK,cAET,KAAK,iBAAmB,sBAAsBA,CAAO,EAGrD,KAAK,WAAW,OAAO,EAGvB,KAAK,SAAS,OAAO,EAGjB,KAAK,gBACP,KAAK,eAAe,EAItB,KAAK,SAAS,OAAO,KAAK,MAAO,KAAK,MAAM,EAC5C,KAAK,cAAc,OAAO,KAAK,MAAO,KAAK,MAAM,EACnD,EAEA,KAAK,iBAAmB,sBAAsBA,CAAO,CACvD,CAEA,cAAqB,EAA+B,EAAuB,CACrEC,GAGOC,GAAU,MAFnB,KAAK,MAAM,WAAa,KACxB,KAAK,SAAS,cAAc,CAAC,IAK7B,KAAK,MAAM,WAAa,IAAI3E,EAAM,MAAM2E,CAAK,EAC7C,KAAK,SAAS,cAAc,CAAC,EAEjC,CAEA,SAAuB,CACrB,KAAK,YAAc,GACf,KAAK,mBAAqB,MAC5B,qBAAqB,KAAK,gBAAgB,EAExC,KAAK,gBACP,KAAK,eAAe,WAAW,EAEjC,KAAK,SAAS,QAAQ,EACtB,KAAK,SAAS,QAAQ,EAElB,KAAK,SAAS,WAAW,eAC3B,KAAK,SAAS,WAAW,cAAc,YAAY,KAAK,SAAS,UAAU,EAEzE,KAAK,cAAc,WAAW,eAChC,KAAK,cAAc,WAAW,cAAc,YAAY,KAAK,cAAc,UAAU,CAEzF,CACF,EChOaC,EAAb,KAA0B,CACxB,OAEA,aAAc,CACZ,KAAK,OAAS,IAAI,GACpB,CAEA,GAAU,EAAe,EAA0B,CAKjD,OAJK,KAAK,OAAO,IAAIC,CAAK,GACxB,KAAK,OAAO,IAAIA,EAAO,CAAC,CAAC,EAE3B,KAAK,OAAO,IAAIA,CAAK,CAAC,CAAE,KAAKC,CAAQ,EAC9B,IACT,CAEA,IAAW,EAAe,EAA0B,CAClD,IAAMC,EAAY,KAAK,OAAO,IAAIF,CAAK,EACvC,GAAI,CAACE,EAAW,OAAO,KAEvB,IAAMC,EAAQD,EAAU,QAAQD,CAAQ,EAIxC,OAHIE,IAAU,IACZD,EAAU,OAAOC,EAAO,CAAC,EAEpB,IACT,CAEA,KAAY,EAAe,GAAGC,EAA0B,CACtD,IAAMF,EAAY,KAAK,OAAO,IAAIF,CAAK,EAYvC,MAXI,CAACE,GAAaA,EAAU,SAAW,EAAU,IAGjD,CAAC,GAAGA,CAAS,CAAC,CAAC,QAAS,GAAa,CACnC,GAAI,CACFD,EAAS,GAAGG,CAAI,CAClB,OAASC,EAAK,CACZ,QAAQ,MAAM,gCAAgCL,EAAM,IAAKK,CAAG,CAC9D,CACF,CAAC,EAEM,GACT,CAEA,mBAA0B,EAAsB,CAM9C,OALIL,EACF,KAAK,OAAO,OAAOA,CAAK,EAExB,KAAK,OAAO,MAAM,EAEb,IACT,CACF,EClCaM,EAAb,KAA0B,CACxB,aAAoB,QAClB,EACA,EAC8B,CAC9B,IAAIC,EAIJ,GAFAC,IAAa,EAAG,eAAe,EAE3B,OAAOC,GAAW,SAAU,CAC9B,IAAMC,EAAW,MAAM,MAAMD,CAAM,EACnC,GAAI,CAACC,EAAS,GACZ,MAAU,MAAM,6BAA6BA,EAAS,YAAY,EAEpE,EAAc,MAAMA,EAAS,YAAY,CAC3C,KAAO,CAGLH,EAHSE,aAAkB,MAAQA,aAAkB,KACvC,MAAMA,EAAO,YAAY,EAEzBA,EAGhBD,IAAa,GAAI,iBAAiB,EAClC,IAAMG,EAAM,MAAMnF,EAAAA,QAAM,UAAU+E,CAAW,EACvCK,EAAW,IAAI,IAEjBC,EAAwC,KACxCC,EAAyC,KACzCC,EAAwC,KACxCC,EAAwC,KAEtCC,EAAkB,CAAC,OAAQ,OAAQ,QAAS,QAAS,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAO,EAGpGC,EAAQ,OAAO,KAAKP,EAAI,KAAK,EAC7BQ,EAAaD,EAAM,QAAU,EAEnC,IAAK,IAAIzD,EAAI,EAAGA,EAAIyD,EAAM,OAAQ,IAAK,CACrC,IAAME,EAAWF,EAAMzD,GACjB4D,EAAQV,EAAI,MAAMS,GAIxB,GAHIC,EAAM,KAGND,EAAS,SAAS,UAAU,GAAKA,EAAS,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,WAAW,IAAI,EAC7E,SAGF,IAAME,EAAQF,EAAS,YAAY,EAC7BG,EAAWH,EAAS,MAAM,GAAG,CAAC,CAAC,IAAI,GAAKA,EACxCI,EAAiB,KAAK,MAAM,GAAM/D,EAAI0D,EAAc,EAAE,EAkB5D,GAfIG,EAAM,SAAS,MAAM,GAAK,CAACT,GAC7B,EAAcQ,EACdb,IAAagB,EAAgB,iBAAiBD,GAAU,GAC/CD,EAAM,SAAS,OAAO,GAAK,CAACR,GACrC,EAAeO,EACfb,IAAagB,EAAgB,iBAAiBD,GAAU,GAC/CD,EAAM,SAAS,MAAM,GAAK,CAACP,GACpC,EAAcM,EACdb,IAAagB,EAAgB,gBAAgBD,GAAU,GAC9CD,EAAM,SAAS,MAAM,GAAK,CAACN,IACpC,EAAcK,EACdb,IAAagB,EAAgB,gBAAgBD,GAAU,GAIrDD,EAAM,SAAS,MAAM,EAAG,CAC1Bd,IAAagB,EAAgB,aAAaD,GAAU,EACpD,IAAME,EAAO,MAAMJ,EAAM,MAAM,MAAM,EAC/BK,EAAY,IAAI,gBAAgBD,CAAI,EAC1Cb,EAAS,IAAIQ,EAAUM,CAAS,EAChCd,EAAS,IAAIW,EAAUG,CAAS,EAChCd,EAAS,IAAIW,EAAS,YAAY,EAAGG,CAAS,CAChD,CAIA,GADgBT,EAAgB,KAAM,GAAQK,EAAM,SAASK,CAAG,CAC5D,EAAS,CACXnB,IAAagB,EAAgB,WAAWD,GAAU,EAClD,IAAME,EAAO,MAAMJ,EAAM,MAAM,MAAM,EAC/BK,EAAY,IAAI,gBAAgBD,CAAI,EAC1Cb,EAAS,IAAIQ,EAAUM,CAAS,EAChCd,EAAS,IAAIW,EAAUG,CAAS,EAChCd,EAAS,IAAIW,EAAS,YAAY,EAAGG,CAAS,CAChD,CACF,CAEAlB,IAAa,GAAI,4BAA4B,EAG7C,IAAIoB,EACJ,GAAIf,EACF,EAAS,WACJ,GAAIC,EACT,EAAS,YACJ,GAAIC,EACT,EAAS,WAIT,MADAH,EAAS,QAAS,GAAQ,IAAI,gBAAgBiB,CAAG,CAAC,EACxC,MAAM,qDAAqD,EAGvE,IAAIC,EACAC,EACAC,EAA4B,KAC5BC,EAA6B,KAC7BC,EACAC,EAEAP,IAAW,OACb,EAAc,MAAMf,EAAa,MAAM,aAAa,EACpD,EAAeA,EAAa,MACnBe,IAAW,QACpB,EAAc,MAAMd,EAAc,MAAM,QAAQ,EAChD,EAAeA,EAAc,OAE7B,EAAa,MAAMC,EAAa,MAAM,QAAQ,EAC9C,EAAcA,EAAa,KACvBC,IACF,EAAa,MAAMA,EAAY,MAAM,QAAQ,EAC7C,EAAcA,EAAY,OAK9B,IAAMoB,EAAiB,IAAI3G,EAAM,eA+BjC,OA9BA2G,EAAe,eAAgB,GAAgB,CAC7C,IAAMC,EAAW,mBAAmBR,CAAG,CAAC,CAAC,QAAQ,QAAS,EAAE,EACtDN,EAAWc,EAAS,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,GAAKA,EAEjE,GAAIzB,EAAS,IAAIiB,CAAG,EAAG,OAAOjB,EAAS,IAAIiB,CAAG,EAC9C,GAAIjB,EAAS,IAAIyB,CAAQ,EAAG,OAAOzB,EAAS,IAAIyB,CAAQ,EACxD,GAAIzB,EAAS,IAAIW,CAAQ,EAAG,OAAOX,EAAS,IAAIW,CAAQ,EAGxD,IAAMe,EAAYf,EAAS,YAAY,EACvC,IAAK,GAAM,CAAC5D,EAAK4E,KAAY3B,EAAS,QAAQ,EAC5C,GAAIjD,EAAI,YAAY,IAAM2E,GAAa3E,EAAI,YAAY,CAAC,CAAC,SAAS,IAAM2E,CAAS,EAC/E,OAAOC,EAIX,OAAOV,CACT,CAAC,EAaM,CACL,SACA,aACA,cACA,aACA,cACA,cACA,eACA,WACA,iBACA,YArBoB,CACpBjB,EAAS,QAAS,GAAQ,CACxB,GAAI,CACF,IAAI,gBAAgBiB,CAAG,CACzB,MAAQ,CAER,CACF,CAAC,EACDjB,EAAS,MAAM,CACjB,CAaA,CACF,CACF,EChLA,EAAO,eAAe,UAAyE,kBAAoBlF,EAAAA,kBACnH,EAAO,eAAe,UAAyE,kBAAoBC,EAAAA,kBACnH,EAAM,KAAK,UAAU,QAAUC,EAAAA,mBAc/B,IAAa4G,EAAb,KAAyB,CACvB,kBAAwD,KACxD,eAAmC,CAAC,EACpC,iBAAkD,CAAC,EACnD,iBAAoC,GACpC,aAA0C,QAC1C,WAA6B,QAC7B,sBAA+C,CAAC,EAGhD,iBAAmC,0DACnC,YAA0C,KAG1C,mBAAqC,sEACrC,WAAwC,KACxC,SAA+C,KAE/C,YAAY,EAAkD,EAAgC,CACxF,OAAOC,GAAuB,UAChC,KAAK,iBAAmBA,EACpBC,IAAU,KAAK,SAAWA,IACrB,OAAOD,GAAuB,UAAYA,IAC/CA,EAAmB,mBACrB,KAAK,iBAAmBA,EAAmB,kBAEzCA,EAAmB,qBACrB,KAAK,mBAAqBA,EAAmB,oBAE/C,KAAK,SAAWA,EAAmB,UAAYC,GAAY,KAE/D,CAKA,YAAmB,EAAqC,CACtD,KAAK,SAAWA,EACZ,KAAK,YACP,KAAK,WAAW,cAAcA,CAAQ,CAE1C,CAKA,oBAA2B,EAAoB,CAC7C,KAAK,iBAAmBC,EACpB,KAAK,aACP,KAAK,YAAY,eAAeA,CAAI,CAExC,CAKA,sBAA6B,EAAoB,CAC/C,KAAK,mBAAqBA,EACtB,KAAK,YACP,KAAK,WAAW,kBAAkBA,CAAI,CAE1C,CAKA,gBAAsC,CAKpC,OAJK,KAAK,cACR,KAAK,YAAc,IAAI9G,EAAAA,YACvB,KAAK,YAAY,eAAe,KAAK,gBAAgB,GAEhD,KAAK,WACd,CAKA,eAAoC,CAClC,GAAI,CAAC,KAAK,WAAY,CAGpB,GAFA,KAAK,WAAa,IAAIC,EAAAA,WACtB,KAAK,WAAW,kBAAkB,KAAK,kBAAkB,EACrD,KAAK,SACP,KAAK,WAAW,cAAc,KAAK,QAAQ,OACtC,GAAI,OAAO,SAAa,IAC7B,GAAI,CACF,IAAM8G,EAAS,SAAS,cAAc,QAAQ,EACxCC,EAAe,IAAI9G,EAAM,cAAc,CAAE,QAAO,CAAC,EACvD,KAAK,WAAW,cAAc8G,CAAY,EAC1CA,EAAa,QAAQ,CACvB,OAAS,EAAG,CACV,QAAQ,KAAK,qEAAsE,CAAC,CACtF,CAEJ,CACA,OAAO,KAAK,UACd,CAKA,iBAAyB,EAAmD,CAC1E,IAAMC,EAAa,IAAI9G,EAAAA,WAAWoG,CAAc,EAIhD,OAHAU,EAAW,eAAe,KAAK,eAAe,CAAC,EAC/CA,EAAW,cAAc,KAAK,cAAc,CAAC,EAC7CA,EAAW,kBAAkB7G,EAAAA,cAAc,EACpC6G,CACT,CAKA,MAAa,QACX,EACA,EAC0B,CAC1B,KAAK,QAAQ,EAEb,IAAMC,EAAa,MAAMzC,EAAa,QAAQG,EAAQD,CAAU,EAGhE,GAFA,KAAK,kBAAoBuC,EAErBA,EAAW,SAAW,OAASA,EAAW,SAAW,OAAQ,CAC/DvC,IAAa,GAAI,4BAA4B,EAC7C,IAAMsC,EAAa,KAAK,iBAAiBC,EAAW,cAAc,EAC5DC,EAAWD,EAAW,YAEtBE,EAAa,MAAM,IAAI,SAAS,EAAS,IAAW,CACxDH,EAAW,MACTE,EACA,GACC,GAAWE,EAAQC,CAAM,EACzB,GAAUC,EAAOC,CAAK,CACzB,CACF,CAAC,EAED7C,IAAa,GAAI,yBAAyB,EAC1C,IAAM2C,EAAS,KAAK,iBAAiBF,EAAK,MAAOA,EAAK,WAAYF,EAAW,MAAM,EAGnF,OADAvC,IAAa,IAAK,MAAM,EACjB2C,CACT,CAGA,GAAM,CAAE,aAAY,aAAY,kBAAmBJ,EAEnDvC,IAAa,GAAI,gBAAgB,EACjC,IAAI8C,EAAqD,KACrDtB,IAEF,EAAmB,IADG9F,EAAAA,UAAUkG,CACb,CAAA,CAAU,MAAMJ,EAAY,EAAE,EACjDsB,EAAiB,QAAQ,GAG3B9C,IAAa,GAAI,oBAAoB,EACrC,IAAM+C,EAAY,IAAIpH,EAAAA,UAAUiG,CAAc,EAC1CkB,GACFC,EAAU,aAAaD,CAAgB,EAGzC,IAAME,EAAO,OAAO1B,GAAe,SAAWA,EAAa,IAAI,YAAY,CAAC,CAAC,OAAOA,CAAU,EACxF2B,EAAaF,EAAU,MAAMC,CAAI,EAEvChD,IAAa,GAAI,yBAAyB,EAC1C,IAAM2C,EAAS,KAAK,iBAAiBM,EAAY,CAAC,EAAG,KAAK,EAG1D,OADAjD,IAAa,IAAK,MAAM,EACjB2C,CACT,CAKA,MAAa,SACX,EACA,EACA,EAC0B,CAC1B,KAAK,QAAQ,EACb,IAAML,EAAa,KAAK,iBAAiBV,CAAc,EAEnDa,EACES,EAAQ,OAAOjD,GAAW,UAAW,iBAAiB,KAAKA,CAAM,EAEnE,OAAOA,GAAW,UACpBD,IAAa,GAAI,uBAAuB,EACxC,EAAO,MAAMsC,EAAW,UAAUrC,EAAS,GAAQ,CACjD,GAAIkD,EAAI,kBAAoBA,EAAI,MAAQ,EAAG,CACzC,IAAMC,EAAI,KAAK,MAAM,GAAMD,EAAI,OAASA,EAAI,MAAS,EAAE,EACvDnD,IAAaoD,EAAG,aAAa,KAAK,MAAOD,EAAI,OAASA,EAAI,MAAS,GAAG,EAAE,MAAM,CAChF,CACF,CAAC,IAEDnD,IAAa,GAAI,yBAAyB,EAC1C,EAAO,MAAM,IAAI,SAAS,EAAS,IAAW,CAC5CsC,EAAW,MACTrC,EACA,GACC,GAAWyC,EAAQC,CAAM,EACzB,GAAUC,EAAOC,CAAK,CACzB,CACF,CAAC,GAGH7C,IAAa,GAAI,yBAAyB,EAC1C,IAAM2C,EAAS,KAAK,iBAAiBF,EAAK,MAAOA,EAAK,WAAYS,EAAQ,MAAQ,MAAM,EAExF,OADAlD,IAAa,IAAK,MAAM,EACjB2C,CACT,CAKA,MAAa,QACX,EACA,EAC0B,CAC1B,GAAI1C,aAAkB,MAAQA,aAAkB,KAAM,CACpD,IAAMoD,EAAS,MAAMpD,EAAO,YAAY,EACxC,OAAO,KAAK,SAASoD,EAAQ,IAAA,GAAWrD,CAAU,CACpD,CACA,OAAO,KAAK,SAASC,EAAQ,IAAA,GAAWD,CAAU,CACpD,CAKA,MAAa,QACX,EACA,EACA,EACA,EAC0B,CAC1B,KAAK,QAAQ,EAEbA,IAAa,GAAI,aAAa,EAC9B,IAAI8C,EAAqD,KACrDQ,IAEF,EAAmB,MAAM,IADH5H,EAAAA,UAAUkG,CACP,CAAA,CAAU,UAAU0B,CAAM,EACnDR,EAAiB,QAAQ,GAG3B9C,IAAa,GAAI,oBAAoB,EACrC,IAAM+C,EAAY,IAAIpH,EAAAA,UAAUiG,CAAc,EAC1CkB,GACFC,EAAU,aAAaD,CAAgB,EAGzC,IAAMG,EAAa,MAAMF,EAAU,UAAUQ,EAAS,GAAQ,CAC5D,GAAIJ,EAAI,kBAAoBA,EAAI,MAAQ,EAAG,CACzC,IAAMC,EAAI,KAAK,MAAM,GAAMD,EAAI,OAASA,EAAI,MAAS,EAAE,EACvDnD,IAAaoD,EAAG,aAAa,KAAK,MAAOD,EAAI,OAASA,EAAI,MAAS,GAAG,EAAE,MAAM,CAChF,CACF,CAAC,EAEDnD,IAAa,GAAI,yBAAyB,EAC1C,IAAM2C,EAAS,KAAK,iBAAiBM,EAAY,CAAC,EAAG,KAAK,EAG1D,OADAjD,IAAa,IAAK,MAAM,EACjB2C,CACT,CAMA,MAAa,UACX,EACA,EAC0B,CAC1B,IAAMjC,EAAQ,MAAM,KAAK8C,CAAQ,EACjC,GAAI9C,EAAM,SAAW,EACnB,MAAU,MAAM,SAAS,EAG3B,IAAM+C,EAAU/C,EAAM,KAAM,GAAMgD,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,EACvE,GAAID,EACF,OAAO,KAAK,QAAQA,EAASzD,CAAU,EAGzC,IAAM2D,EAAUjD,EAAM,KAAM,GAAMgD,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,EACvE,GAAIC,EACF,OAAO,KAAK,QAAQA,EAAS3D,CAAU,EAGzC,IAAM4D,EAAWlD,EAAM,KAAM,GAAMgD,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,OAAO,CAAC,EACzE,GAAIE,EAAU,CACZ5D,IAAa,GAAI,sBAAsB,EACvC,IAAMI,EAAW,IAAI,IACrB,IAAK,IAAMyD,KAAQnD,EAAO,CACxB,IAAMW,EAAM,IAAI,gBAAgBwC,CAAI,EACpC,KAAK,eAAe,KAAKxC,CAAG,EAC5BjB,EAAS,IAAIyD,EAAK,KAAMxC,CAAG,EAC3BjB,EAAS,IAAIyD,EAAK,KAAK,YAAY,EAAGxC,CAAG,CAC3C,CAEA,IAAMO,EAAiB,IAAIrG,EAAM,eACjCqG,EAAe,eAAgB,GAAgB,CAC7C,IAAMC,EAAW,mBAAmBR,CAAG,CAAC,CAAC,QAAQ,QAAS,EAAE,EACtDN,EAAWc,EAAS,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,GAAKA,EACjE,GAAIzB,EAAS,IAAIiB,CAAG,EAAG,OAAOjB,EAAS,IAAIiB,CAAG,EAC9C,GAAIjB,EAAS,IAAIyB,CAAQ,EAAG,OAAOzB,EAAS,IAAIyB,CAAQ,EACxD,GAAIzB,EAAS,IAAIW,CAAQ,EAAG,OAAOX,EAAS,IAAIW,CAAQ,EAExD,IAAMe,EAAYf,EAAS,YAAY,EACvC,IAAK,GAAM,CAAC5D,EAAK4E,KAAY3B,EAAS,QAAQ,EAC5C,GAAIjD,EAAI,YAAY,IAAM2E,GAAa3E,EAAI,YAAY,CAAC,CAAC,SAAS,IAAM2E,CAAS,EAC/E,OAAOC,EAGX,OAAOV,CACT,CAAC,EAED,IAAMyC,EAAW,MAAMF,EAAS,KAAK,EACrC,OAAO,KAAK,SAASE,EAAUlC,EAAgB5B,CAAU,CAC3D,CAEA,IAAM+D,EAAUrD,EAAM,KAAM,GAAMgD,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,EACvE,GAAIK,EAAS,CACX,IAAMC,EAAUtD,EAAM,KAAM,GAAMgD,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,EACjEtD,EAAW,IAAI,IACrB,IAAK,IAAMyD,KAAQnD,EAAO,CACxB,IAAMW,EAAM,IAAI,gBAAgBwC,CAAI,EACpC,KAAK,eAAe,KAAKxC,CAAG,EAC5BjB,EAAS,IAAIyD,EAAK,KAAMxC,CAAG,EAC3BjB,EAAS,IAAIyD,EAAK,KAAK,YAAY,EAAGxC,CAAG,CAC3C,CAEA,IAAMO,EAAiB,IAAIrG,EAAM,eACjCqG,EAAe,eAAgB,GAAgB,CAC7C,IAAMC,EAAW,mBAAmBR,CAAG,CAAC,CAAC,QAAQ,QAAS,EAAE,EACtDN,EAAWc,EAAS,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,GAAKA,EACjE,GAAIzB,EAAS,IAAIiB,CAAG,EAAG,OAAOjB,EAAS,IAAIiB,CAAG,EAC9C,GAAIjB,EAAS,IAAIyB,CAAQ,EAAG,OAAOzB,EAAS,IAAIyB,CAAQ,EACxD,GAAIzB,EAAS,IAAIW,CAAQ,EAAG,OAAOX,EAAS,IAAIW,CAAQ,EAExD,IAAMe,EAAYf,EAAS,YAAY,EACvC,IAAK,GAAM,CAAC5D,EAAK4E,KAAY3B,EAAS,QAAQ,EAC5C,GAAIjD,EAAI,YAAY,IAAM2E,GAAa3E,EAAI,YAAY,CAAC,CAAC,SAAS,IAAM2E,CAAS,EAC/E,OAAOC,EAGX,OAAOV,CACT,CAAC,EAED,IAAMkC,EAASnD,EAAS,IAAI2D,EAAQ,IAAI,EAClCT,EAASU,EAAU5D,EAAS,IAAI4D,EAAQ,IAAI,EAAI,IAAA,GACtD,OAAO,KAAK,QAAQT,EAAQD,EAAQ1B,EAAgB5B,CAAU,CAChE,CAEA,MAAU,MAAM,kDAAkD,CACpE,CASA,iBACE,EACA,EACA,EAA4C,MAC3B,CACjB,IAAIiE,EAAc,EACdC,EAAY,EAChB,KAAK,iBAAmB,CAAC,EAEzB,IAAMC,GAAkB,EAA2B,EAAmB,KAAS,CACzEC,IACF,EAAI,WAAa,GACjB,EAAI,gBAAkB,GACtB,EAAI,UAAY7I,EAAM,yBACtB,EAAI,UAAYA,EAAM,aAClB8I,GAAW,CAACD,EAAI,aAClB,EAAI,WAAa7I,EAAM,gBAEzB,EAAI,YAAc,GAEtB,EAGM+I,EAAyB,GAAwB,CAIrD,GAHA,EAAI,KAAO/I,EAAM,WACjB,EAAI,YAAc,GAEd,UAAWgJ,EAAK,CAClB,IAAMC,EAAID,EACVJ,EAAeK,EAAE,IAAK,EAAI,EACtB,gBAAiBA,GAAGL,EAAeK,EAAE,YAAa,EAAI,EACtD,cAAeA,GAAGL,EAAeK,EAAE,UAAW,EAAK,EACnD,YAAaA,GAAGL,EAAeK,EAAE,QAAS,EAAK,EAC/C,iBAAkBA,GAAGL,EAAeK,EAAE,aAAc,EAAK,EACzD,iBAAkBA,GAAGL,EAAeK,EAAE,aAAc,EAAK,EACzD,UAAWA,GAAGL,EAAeK,EAAE,MAAO,EAAK,EAG3CpD,IAAW,OAAS,CAACoD,EAAE,KAAOA,EAAE,OAASA,EAAE,MAAM,OAAO,EAAI,SAC9DA,EAAE,MAAM,OAAO,QAAQ,CAE3B,CACF,EAGAC,EAAM,kBAAkB,EAAI,EAC5B,IAAMC,EAAa,IAAInJ,EAAM,KAAK,CAAC,CAAC,cAAckJ,CAAK,EACjDE,EAAa,IAAIpJ,EAAM,QACvBqJ,EAAW,IAAIrJ,EAAM,QAC3BmJ,EAAW,UAAUC,CAAU,EAC/BD,EAAW,QAAQE,CAAQ,EAG3BH,EAAM,SAAU,GAAU,CACxB,GAAKI,EAAqB,OAAQ,CAChC,IAAMC,EAAOD,EACb,EAAK,WAAa,GAClB,EAAK,cAAgB,GAEjBC,EAAK,SACH,MAAM,QAAQA,EAAK,QAAQ,EAC7BA,EAAK,SAAS,QAAQR,CAAqB,EAE3CA,EAAsBQ,EAAK,QAAQ,EAGrC,EAAK,SAAW,IAAIvJ,EAAM,qBAAqB,CAC7C,MAAO,KAAK,WACZ,UAAW,IACX,UAAW,IACX,KAAMA,EAAM,UACd,CAAC,EAGH,IAAMwJ,EAAOD,EAAK,SAClB,GAAIC,EAAM,CAEHA,EAAK,WAAW,QACnBA,EAAK,qBAAqB,EAIxBA,EAAK,mBACPA,EAAK,kBAAkB,EAIrBA,EAAK,WAAW,WAClB,GAAeA,EAAK,WAAW,SAAS,OAEtCA,EAAK,MACP,GAAaA,EAAK,MAAM,MAAQ,EACvBA,EAAK,WAAW,WACzB,GAAaA,EAAK,WAAW,SAAS,MAAQ,GAIhD,IAAMC,EAAW,IAAIzJ,EAAM,qBAAqB,CAC9C,MAAO,KAAK,WACZ,UAAW,IACX,UAAW,IACX,KAAMA,EAAM,WACZ,eAAgB,KAAK,sBAAsB,OAAS,EAAI,KAAK,sBAAwB,KACrF,YAAa,EACf,CAAC,EAEK0J,EAAmC,CACvC,OACA,iBAAkBH,EAAK,SACvB,cAAeE,CACjB,EAEA,KAAK,iBAAiBC,CAAW,EACjC,KAAK,iBAAiB,KAAKA,CAAW,CACxC,CACF,CACF,CAAC,EAGD,IAAMC,EAAY,IAAI3J,EAAM,MAS5B,MARA,GAAU,KAAO,iBACjBkJ,EAAM,SAAS,IAAI,CAACE,EAAW,EAAG,CAACA,EAAW,EAAG,CAACA,EAAW,CAAC,EAC9DO,EAAU,IAAIT,CAAK,EACnBS,EAAU,kBAAkB,EAAI,EAKzB,CACL,MAAOA,EACP,IAJU,IAAI3J,EAAM,KAAK,CAAC,CAAC,cAAc2J,CAIzC,EACA,KAAMN,EACN,OAAQD,EACR,cACA,UAAW,KAAK,MAAMT,CAAS,EAC/B,WAAYiB,GAAc,CAAC,EAC3B,QACF,CACF,CAKA,iBAAyB,EAAiC,CACxD,IAAMC,EAAS,KAAK,iBAAmBC,EAAK,iBAAmBA,EAAK,cACpE,EAAK,KAAK,SAAWD,EAErB,IAAME,EAAe,GAAwB,CAC3C,EAAI,QAAU,GACV,cAAef,IACjB,EAAoC,UAAa,KAAK,eAAiB,aAEzE,EAAI,YAAc,EACpB,EAEI,MAAM,QAAQa,CAAM,EACtBA,EAAO,QAAQE,CAAW,EAE1BA,EAAYF,CAAM,CAEtB,CAKA,gBAAuB,EAAgC,CACrD,KAAK,aAAeG,EACpB,IAAK,IAAMF,KAAQ,KAAK,iBACtB,KAAK,iBAAiBA,CAAI,CAE9B,CAEA,iBAA4C,CAC1C,OAAO,KAAK,YACd,CAKA,cAAqB,EAA4B,CAC/C,KAAK,iBAAmBG,EACxB,IAAK,IAAMH,KAAQ,KAAK,iBACtB,KAAK,iBAAiBA,CAAI,CAE9B,CAEA,iBAAkC,CAChC,OAAO,KAAK,gBACd,CAKA,cAAqB,EAAwC,CAC3D,KAAK,WAAa,OAAO/F,GAAU,SAAW,IAAI/D,EAAM,MAAM+D,CAAK,CAAC,CAAC,OAAO,EAAI,OAAOA,CAAK,EAC5F,IAAK,IAAM+F,KAAQ,KAAK,iBAClBA,EAAK,eAAiB,UAAWA,EAAK,gBACxCA,EAAM,cAA6C,MAAM,IAAI,KAAK,UAAU,EAC5E,EAAK,cAAc,YAAc,GAGvC,CAEA,eAA+B,CAC7B,OAAO,KAAK,UACd,CAKA,qBAAuE,CACrE,IAAII,EAAW,EACXC,EAAY,EAEVC,EAAc,GAAwB,CAC1C,GAAI,QAASpB,GAAQA,EAAmC,IAAK,CAC3D,IAAMH,EAAOG,EAAmC,IAChD,GAAIH,EAAK,CACP,IAAMwB,EAAOxB,EAAI,OAAUA,EAAI,QAAsE,KAGrG,GAAIwB,EAAK,CACP,IAAMC,EAAID,EAAI,OAAS,EACjBE,EAAIF,EAAI,QAAU,EACpBC,EAAI,GAAKC,EAAI,GAAKD,EAAIC,EAAIL,EAAWC,IACvC,EAAWG,EACX,EAAYC,EAEhB,CACF,CACF,CACF,EAEA,IAAK,IAAMT,KAAQ,KAAK,iBAClB,MAAM,QAAQA,EAAK,gBAAgB,EACrCA,EAAK,iBAAiB,QAAQM,CAAU,EAC/BN,EAAK,kBACdM,EAAWN,EAAK,gBAAgB,EAOpC,OAHII,EAAW,GAAKC,EAAY,EACvB,CAAE,MAAOD,EAAU,OAAQC,CAAU,EAEvC,IACT,CAKA,WAAiC,CAC/B,OAAO,KAAK,iBAAiB,IAAK,GAAMlB,EAAE,IAAI,CAChD,CAKA,oBAA2B,EAA6B,CACtD,KAAK,sBAAwBuB,EAC7B,IAAK,IAAMV,KAAQ,KAAK,iBAAkB,CACxC,IAAMW,EAAc,GAAwB,CAC1C,EAAI,eAAiBD,EAAO,OAAS,EAAIA,EAAS,KAClD,EAAI,YAAc,GAClB,EAAI,YAAc,EACpB,EAEI,MAAM,QAAQV,EAAK,gBAAgB,EACrCA,EAAK,iBAAiB,QAAQW,CAAU,EAExCA,EAAWX,EAAK,gBAAgB,EAElCW,EAAWX,EAAK,aAAa,CAC/B,CACF,CAKA,SAAuB,CACrB,AAEE,KAAK,qBADL,KAAK,kBAAkB,QAAQ,EACN,MAGvB,KAAK,eAAe,OAAS,IAC/B,KAAK,eAAe,QAAS,GAAQ,CACnC,GAAI,CACF,IAAI,gBAAgBhE,CAAG,CACzB,MAAQ,CAER,CACF,CAAC,EACD,KAAK,eAAiB,CAAC,GAGzB,IAAK,IAAMgE,KAAQ,KAAK,iBAClBA,EAAK,eACPA,EAAK,cAAc,QAAQ,EAEzBA,EAAK,KAAK,UAAYA,EAAK,KAAK,SAAS,mBAC3CA,EAAK,KAAK,SAAS,kBAAkB,EAGzC,KAAK,iBAAmB,CAAC,CAC3B,CAKA,SAAuB,CACrB,KAAK,QAAQ,EACb,AAEE,KAAK,eADL,KAAK,YAAY,QAAQ,EACN,MAErB,AAEE,KAAK,cADL,KAAK,WAAW,QAAQ,EACN,KAEtB,CACF,EC7rBA,SAAgBY,EAAkB,EAA0B,CAC1D,IAAMC,EAAWpB,EAAK,SACtB,GAAI,CAACoB,EAAU,MAAO,GAEtB,IAAMC,EAAWD,EAAS,WAAW,SACrC,GAAI,CAACC,EAAU,MAAO,GAEtB,IAAIC,EAAc,EACZC,EAAK,IAAIzK,EAAM,QACf0K,EAAK,IAAI1K,EAAM,QACf2K,EAAK,IAAI3K,EAAM,QAErBkJ,EAAK,kBAAkB,EAAI,EAC3B,IAAM0B,EAAc1B,EAAK,YAEzB,GAAIoB,EAAS,MAAO,CAClB,IAAMO,EAAUP,EAAS,MACzB,IAAK,IAAIjJ,EAAI,EAAGA,EAAIwJ,EAAQ,MAAO,GAAK,EAAG,CACzC,IAAMC,EAAKD,EAAQ,KAAKxJ,CAAC,EACnB0J,EAAKF,EAAQ,KAAKxJ,EAAI,CAAC,EACvB2J,EAAKH,EAAQ,KAAKxJ,EAAI,CAAC,EAE7BoJ,EAAG,oBAAoBF,EAAUO,CAAE,CAAC,CAAC,aAAaF,CAAW,EAC7DF,EAAG,oBAAoBH,EAAUQ,CAAE,CAAC,CAAC,aAAaH,CAAW,EAC7DD,EAAG,oBAAoBJ,EAAUS,CAAE,CAAC,CAAC,aAAaJ,CAAW,EAE7D,GAAeK,EAAuBR,EAAIC,EAAIC,CAAE,CAClD,CACF,MACE,IAAK,IAAItJ,EAAI,EAAGA,EAAIkJ,EAAS,MAAO,GAAK,EACvCE,EAAG,oBAAoBF,EAAUlJ,CAAC,CAAC,CAAC,aAAauJ,CAAW,EAC5DF,EAAG,oBAAoBH,EAAUlJ,EAAI,CAAC,CAAC,CAAC,aAAauJ,CAAW,EAChED,EAAG,oBAAoBJ,EAAUlJ,EAAI,CAAC,CAAC,CAAC,aAAauJ,CAAW,EAEhE,GAAeK,EAAuBR,EAAIC,EAAIC,CAAE,EAIpD,OAAO,KAAK,IAAIH,CAAW,CAC7B,CAKA,SAAgBU,EAAmB,EAAkE,CACnG,IAAIC,EAAa,EACjBC,EAAK,SAAU,GAAU,CAClBnC,EAAqB,SACxB,GAAcoB,EAAkBpB,CAAmB,EAEvD,CAAC,EAED,IAAMoC,EAAO,IAAIrL,EAAM,KAAK,CAAC,CAAC,cAAcoL,CAAI,EAC1CE,EAAO,IAAItL,EAAM,QACvBqL,EAAK,QAAQC,CAAI,EACjB,IAAMC,EAAaD,EAAK,EAAIA,EAAK,EAAIA,EAAK,EAE1C,MAAO,CAAE,aAAY,YAAW,CAClC,CAEA,SAASL,EAAuB,EAAmB,EAAmB,EAA2B,CAC/F,OAAOR,EAAG,IAAIC,EAAG,MAAM,CAAC,CAAC,MAAMC,CAAE,CAAC,EAAI,CACxC,CAKA,SAAgBa,EAAqB,EAAiC,CACpE,IAAMlK,EAAImK,EAAO,OACjB,GAAInK,EAAI,EAAG,MAAO,GAElB,IAAMoK,EAAS,IAAI1L,EAAM,QAAQ,EAAG,EAAG,CAAC,EACxC,IAAK,IAAIqB,EAAI,EAAGA,EAAIC,EAAG,IAAK,CAC1B,IAAMqK,EAAUF,EAAOpK,GACjBuK,EAAOH,GAAQpK,EAAI,GAAKC,GAE9B,EAAO,IAAMqK,EAAQ,EAAIC,EAAK,IAAMD,EAAQ,EAAIC,EAAK,GACrD,EAAO,IAAMD,EAAQ,EAAIC,EAAK,IAAMD,EAAQ,EAAIC,EAAK,GACrD,EAAO,IAAMD,EAAQ,EAAIC,EAAK,IAAMD,EAAQ,EAAIC,EAAK,EACvD,CAEA,OAAOF,EAAO,OAAO,EAAI,EAC3B,CAKA,SAAgBG,EAAW,EAAe,EAAe,IAAK,EAAmB,EAAW,CAI1F,OAHI,KAAK,IAAIC,CAAK,EAAI,KACb,QAAQC,IAEV,GAAGD,EAAM,QAAQE,CAAQ,EAAE,GAAGD,GACvC,CC7FA,IAAaE,EAAb,KAA6B,CAC3B,MACA,SACA,aAAkD,KAGlD,MAAmC,KACnC,MAAmC,KACnC,MAAmC,KAGnC,OAAqC,KACrC,OAAqC,KACrC,OAAqC,KAErC,aAA8C,KAC9C,UAA6B,GAC7B,KAAuB,IAGvB,cAAyC,CAAC,EAC1C,cAAyC,CACvC,IAAIhM,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,QACV,IAAIA,EAAM,OACZ,EAGA,UAAmC,IAAIA,EAAM,QAE7C,YAAY,EAAoB,CAC9B,KAAK,MAAQiM,EACb,KAAK,SAAW,IAAIjM,EAAM,MAC1B,KAAK,SAAS,KAAO,yBACrB,KAAK,MAAM,IAAI,KAAK,QAAQ,CAC9B,CAKA,cAAqB,EAAuB,EAA6C,CACvF,KAAK,OAAO,EACZ,KAAK,aAAekM,EACpB,KAAK,KAAOrJ,GAAS,MAAQ,IAG7BqJ,EAAM,kBAAkB,EAAI,EAC5B,IAAMC,EAAM,IAAInM,EAAM,KAChBoM,EAAiBF,EAAM,YAAY,MAAM,CAAC,CAAC,OAAO,EAGxDA,EAAM,SAAU,GAAU,CACxB,GAAKlD,EAAqB,OAAQ,CAChC,IAAMC,EAAOD,EACb,GAAIC,EAAK,WACFA,EAAK,SAAS,aACjBA,EAAK,SAAS,mBAAmB,EAE/BA,EAAK,SAAS,aAAa,CAC7B,IAAMoD,EAAYpD,EAAK,YAAY,MAAM,CAAC,CAAC,YAAYmD,CAAc,EAC/DE,EAAUrD,EAAK,SAAS,YAAY,MAAM,CAAC,CAAC,aAAaoD,CAAS,EACxEF,EAAI,MAAMG,CAAO,CACnB,CAEJ,CACF,CAAC,EAEGH,EAAI,QAAQ,GACdA,EAAI,cAAcD,CAAK,EAGzB,IAAMK,EAAMJ,EAAI,IACVK,EAAML,EAAI,IAChBA,EAAI,QAAQ,KAAK,SAAS,EAI1B,KAAK,cAAgB,CACnB,IAAInM,EAAM,QAAQuM,EAAI,EAAGA,EAAI,EAAGA,EAAI,CAAC,EACrC,IAAIvM,EAAM,QAAQwM,EAAI,EAAGD,EAAI,EAAGA,EAAI,CAAC,EACrC,IAAIvM,EAAM,QAAQuM,EAAI,EAAGC,EAAI,EAAGD,EAAI,CAAC,EACrC,IAAIvM,EAAM,QAAQwM,EAAI,EAAGA,EAAI,EAAGD,EAAI,CAAC,EACrC,IAAIvM,EAAM,QAAQuM,EAAI,EAAGA,EAAI,EAAGC,EAAI,CAAC,EACrC,IAAIxM,EAAM,QAAQwM,EAAI,EAAGD,EAAI,EAAGC,EAAI,CAAC,EACrC,IAAIxM,EAAM,QAAQuM,EAAI,EAAGC,EAAI,EAAGA,EAAI,CAAC,EACrC,IAAIxM,EAAM,QAAQwM,EAAI,EAAGA,EAAI,EAAGA,EAAI,CAAC,CACvC,EAOA,IAAMC,EAAc,CAClB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,EACxB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,EACxB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,CAC1B,EAEMC,EAAU,IAAI1M,EAAM,eACpB2M,EAAY,IAAI,aAAaF,EAAY,OAAS,CAAC,EACzDC,EAAQ,aAAa,WAAY,IAAI1M,EAAM,gBAAgB2M,EAAW,CAAC,CAAC,EAExE,IAAMC,EAAe,IAAI5M,EAAM,kBAAkB,CAC/C,MAAO6C,GAAS,MAAQ,IAAI7C,EAAM,MAAM6C,EAAQ,KAAK,EAAI,MACzD,UAAW,EACX,YAAa,GACb,QAAS,GACT,UAAW,EACb,CAAC,EACD,KAAK,aAAe,IAAI7C,EAAM,aAAa0M,EAASE,CAAY,EAChE,KAAK,SAAS,IAAI,KAAK,YAAY,EAInC,KAAK,MAAQ,KAAK,kBAAkB,QAAQ,EAC5C,KAAK,MAAQ,KAAK,kBAAkB,OAAQ,EAC5C,KAAK,MAAQ,KAAK,kBAAkB,OAAQ,EAG5C,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzC,EAAK,UAAY,4BACjB,EAAK,UAAY,+BAA+BjB,EAAW,KAAK,UAAU,EAAG,KAAK,IAAI,EAAE,WACxF,KAAK,OAAS,IAAI3L,EAAAA,YAAY4M,CAAI,EAClC,KAAK,SAAS,IAAI,KAAK,MAAM,EAE7B,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzC,EAAK,UAAY,4BACjB,EAAK,UAAY,+BAA+BlB,EAAW,KAAK,UAAU,EAAG,KAAK,IAAI,EAAE,WACxF,KAAK,OAAS,IAAI3L,EAAAA,YAAY6M,CAAI,EAClC,KAAK,SAAS,IAAI,KAAK,MAAM,EAE7B,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzC,EAAK,UAAY,4BACjB,EAAK,UAAY,+BAA+BnB,EAAW,KAAK,UAAU,EAAG,KAAK,IAAI,EAAE,WACxF,KAAK,OAAS,IAAI3L,EAAAA,YAAY8M,CAAI,EAClC,KAAK,SAAS,IAAI,KAAK,MAAM,EAE7B,KAAK,WAAWlK,GAAS,SAAW,EAAK,CAC3C,CAEA,kBAA0B,EAA2B,CACnD,IAAMqG,EAAO,IAAIlJ,EAAM,eAAe,CAAC,CAAC,cAAc,CACpD,IAAIA,EAAM,QACV,IAAIA,EAAM,OACZ,CAAC,EACK0I,EAAM,IAAI1I,EAAM,kBAAkB,CACtC,QACA,UAAW,EACX,UAAW,EACb,CAAC,EACKgN,EAAO,IAAIhN,EAAM,KAAKkJ,EAAMR,CAAG,EAErC,OADA,KAAK,SAAS,IAAIsE,CAAI,EACfA,CACT,CAMA,OAAc,EAA4B,CACxC,GAAI,CAAC,KAAK,WAAa,CAAC,KAAK,cAAgB,KAAK,cAAc,OAAS,EAAG,OAE5E,KAAK,aAAa,kBAAkB,EAAI,EACxC,IAAMC,EAAS,KAAK,aAAa,YAGjC,IAAK,IAAI7L,EAAI,EAAGA,EAAI,EAAG,IACrB,KAAK,cAAcA,EAAE,CAAC,KAAK,KAAK,cAAcA,EAAE,CAAC,CAAC,aAAa6L,CAAM,EAIvE,GAAI,KAAK,aAAc,CACrB,IAAMR,EAAc,CAClB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,EACxB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,EACxB,EAAG,EAAI,EAAG,EAAI,EAAG,EAAI,EAAG,CAC1B,EACMS,EAAU,KAAK,aAAa,SAAS,WAAW,SAClDC,EAAM,EACV,IAAK,IAAMC,KAAOX,EAAa,CAC7B,IAAMY,EAAI,KAAK,cAAcD,GAC7BF,EAAQ,OAAO,IAAOG,EAAE,EAAGA,EAAE,EAAGA,EAAE,CAAC,CACrC,CACA,EAAQ,YAAc,EACxB,CAIA,IAAIC,EAAa,EACbC,EAAc,IACZC,EAASC,EAAO,SAEtB,IAAK,IAAIrM,EAAI,EAAGA,EAAI,EAAG,IAAK,CAC1B,IAAMsM,EAAO,KAAK,cAActM,EAAE,CAAC,WAAWoM,CAAM,EAChDE,EAAOH,IACT,EAAcG,EACd,EAAatM,EAEjB,CAMA,IAAMuM,EAAU,KAAK,cAAcL,GAC7BM,EAAY,KAAK,cAAcN,EAAa,GAC5CO,EAAY,KAAK,cAAcP,EAAa,GAC5CQ,EAAY,KAAK,cAAcR,EAAa,GAGlD,KAAK,WAAW,KAAK,MAAOK,EAASC,CAAS,EAC9C,KAAK,WAAW,KAAK,MAAOD,EAASE,CAAS,EAC9C,KAAK,WAAW,KAAK,MAAOF,EAASG,CAAS,EAG1C,KAAK,QACP,KAAK,OAAO,SAAS,KAAKH,CAAO,CAAC,CAAC,IAAIC,CAAS,CAAC,CAAC,eAAe,EAAG,EAElE,KAAK,QACP,KAAK,OAAO,SAAS,KAAKD,CAAO,CAAC,CAAC,IAAIE,CAAS,CAAC,CAAC,eAAe,EAAG,EAElE,KAAK,QACP,KAAK,OAAO,SAAS,KAAKF,CAAO,CAAC,CAAC,IAAIG,CAAS,CAAC,CAAC,eAAe,EAAG,CAExE,CAEA,WAAmB,EAAyB,EAAmB,EAAyB,CACtF,GAAI,CAACd,EAAM,OACX,IAAMe,EAAMf,EAAK,SAAS,WAAW,SACrCe,EAAI,OAAO,EAAGvD,EAAG,EAAGA,EAAG,EAAGA,EAAG,CAAC,EAC9BuD,EAAI,OAAO,EAAGtD,EAAG,EAAGA,EAAG,EAAGA,EAAG,CAAC,EAC9B,EAAI,YAAc,EACpB,CAEA,WAAkB,EAAwB,CACxC,KAAK,UAAYuD,EACjB,KAAK,SAAS,QAAUA,CAC1B,CAEA,YAA6B,CAC3B,OAAO,KAAK,SACd,CAEA,QAAe,EAAoB,CACjC,KAAK,KAAOlC,EACR,KAAK,cACP,KAAK,cAAc,KAAK,aAAc,CAAE,QAAS,KAAK,UAAW,MAAK,CAAC,CAE3E,CAEA,QAAsB,CACpB,AAIE,KAAK,gBAHL,KAAK,aAAa,SAAS,QAAQ,EACnC,KAAM,aAAa,SAA4B,QAAQ,EACvD,KAAK,SAAS,OAAO,KAAK,YAAY,EAClB,MAGtB,AAIE,KAAK,SAHL,KAAK,MAAM,SAAS,QAAQ,EAC5B,KAAM,MAAM,SAA4B,QAAQ,EAChD,KAAK,SAAS,OAAO,KAAK,KAAK,EAClB,MAEf,AAIE,KAAK,SAHL,KAAK,MAAM,SAAS,QAAQ,EAC5B,KAAM,MAAM,SAA4B,QAAQ,EAChD,KAAK,SAAS,OAAO,KAAK,KAAK,EAClB,MAEf,AAIE,KAAK,SAHL,KAAK,MAAM,SAAS,QAAQ,EAC5B,KAAM,MAAM,SAA4B,QAAQ,EAChD,KAAK,SAAS,OAAO,KAAK,KAAK,EAClB,MAGf,AAEE,KAAK,UADL,KAAK,SAAS,OAAO,KAAK,MAAM,EAClB,MAEhB,AAEE,KAAK,UADL,KAAK,SAAS,OAAO,KAAK,MAAM,EAClB,MAEhB,AAEE,KAAK,UADL,KAAK,SAAS,OAAO,KAAK,MAAM,EAClB,MAGhB,KAAK,cAAgB,CAAC,EACtB,KAAK,aAAe,IACtB,CAEA,SAAuB,CACrB,KAAK,OAAO,EACZ,KAAK,MAAM,OAAO,KAAK,QAAQ,CACjC,CACF,EC/SamC,EAAb,KAAwB,CACtB,OACA,SACA,WACA,aAA0D,KAG1D,eAAkC,GAClC,eAAyC,IACzC,oBAAmD,YACnD,gBAAkC,EAGlC,WAAuC,KACvC,iBAAmC,EACnC,eAAiC,EAEjC,YACE,EACA,EACA,EACA,CACA,KAAK,OAASR,EACd,KAAK,SAAWS,EAChB,KAAK,WAAaC,GAAc,IAAIjO,EAAM,KAC5C,CAMA,cACE,EACA,EACA,EACA,EACM,CACN,KAAK,eAAiBkO,EAClBC,IAAS,IAAA,KAAW,KAAK,eAAiBA,GAC1CC,IAAc,IAAA,KAAW,KAAK,oBAAsBA,GACpDC,IAAU,IAAA,KAAW,KAAK,gBAAkBA,EAClD,CAEA,kBAAmC,CAEjC,MADA,MAAK,eAAiB,CAAC,KAAK,eACrB,KAAK,cACd,CAEA,kBAAyB,EAA4B,CACnD,KAAK,eAAiBF,CACxB,CAEA,uBAA8B,EAAsC,CAClE,KAAK,oBAAsBC,CAC7B,CAEA,mBAA0B,EAAqB,CAC7C,KAAK,gBAAkB,KAAK,IAAI,GAAK,KAAK,IAAI,GAAIC,CAAK,CAAC,CAC1D,CAEA,oBAA+C,CAC7C,MAAO,CACL,QAAS,KAAK,eACd,KAAM,KAAK,eACX,UAAW,KAAK,oBAChB,MAAO,KAAK,eACd,CACF,CAMA,cAAqB,EAA8B,CACjD,KAAK,WAAaF,EAClB,IAAMG,EAAS,KAAK,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI,KAAK,SAAS,MAAM,EAC9DC,EAASD,EAAO,OAAO,EAEzBC,EAAS,OACX,KAAK,iBAAmB,KAAK,KAAK,KAAK,IAAI,GAAI,KAAK,IAAI,EAAGD,EAAO,EAAIC,CAAM,CAAC,CAAC,EAC9E,KAAK,eAAiB,KAAK,MAAMD,EAAO,EAAGA,EAAO,CAAC,EAEvD,CAEA,eAAyC,CACvC,OAAO,KAAK,UACd,CAKA,gBAA8B,CAC5B,IAAM1M,EAAS,KAAK,SAAS,OACvB0M,EAAS,KAAK,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI1M,CAAM,EAChD2M,EAASD,EAAO,OAAO,EAG7B,GAAI,KAAK,gBAAkBC,EAAS,KAAO,CACzC,IAAMC,EAAU,KAAK,sBAAwB,YAAc,EAAI,GACzDC,EAAQ,KAAQ,KAAK,gBAAkBD,EAE7C,GAAI,KAAK,iBAAmB,IAAK,CAE/B,IAAME,EAAU,IAAIzO,EAAM,QAAQ,EAAG,EAAG,CAAC,EACzCqO,EAAO,eAAeI,EAASD,CAAK,EACpC,KAAK,OAAO,GAAG,eAAeC,EAASD,CAAK,CAC9C,KAAO,CAEL,IAAMC,EAAU,IAAIzO,EAAM,QAAQ,EAAG,EAAG,CAAC,EACzCqO,EAAO,eAAeI,EAASD,CAAK,EACpC,KAAK,OAAO,GAAG,eAAeC,EAASD,CAAK,CAC9C,CAEA,KAAK,OAAO,SAAS,KAAK7M,CAAM,CAAC,CAAC,IAAI0M,CAAM,EAC5C,KAAK,OAAO,OAAO1M,CAAM,CAC3B,CAGA,GAAI,KAAK,YAAc2M,EAAS,KAAO,CACrC,IAAMI,EAAgB,KAAK,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI/M,CAAM,EACvDgN,EAAYD,EAAc,OAAO,EAEvC,GAAI,KAAK,aAAe,IAAK,CAE3B,IAAME,EAAUD,EAAY,KAAK,IAAI,KAAK,gBAAgB,EACpDE,EAAUF,EAAY,KAAK,IAAI,KAAK,gBAAgB,EACpDG,EAAU,KAAK,MAAMJ,EAAc,EAAGA,EAAc,CAAC,EAC3D,EAAc,EAAIE,EAAU,KAAK,IAAIE,CAAO,EAC5C,EAAc,EAAID,EAClB,EAAc,EAAID,EAAU,KAAK,IAAIE,CAAO,EAC5C,KAAK,OAAO,SAAS,KAAKnN,CAAM,CAAC,CAAC,IAAI+M,CAAa,CACrD,MAAO,GAAI,KAAK,aAAe,IAAK,CAElC,IAAMK,EAAOL,EAAc,EACrBE,EAAU,KAAK,KAAK,KAAK,IAAI,EAAGD,EAAYA,EAAYI,EAAOA,CAAI,CAAC,EAC1E,EAAc,EAAIH,EAAU,KAAK,IAAI,KAAK,cAAc,EACxD,EAAc,EAAIA,EAAU,KAAK,IAAI,KAAK,cAAc,EACxD,KAAK,OAAO,SAAS,KAAKjN,CAAM,CAAC,CAAC,IAAI+M,CAAa,CACrD,MAAW,KAAK,aAAe,KAE7B,KAAK,OAAO,GAAG,IAAI,EAAG,EAAG,CAAC,EAG5B,KAAK,OAAO,OAAO/M,CAAM,CAC3B,CACF,CAKA,QACE,EACA,EACA,EAAmB,IACJ,CACf,OAAO,IAAI,QAAS,GAAY,CAE9B,IAAMqK,EAAM,IAAIhM,EAAM,KAAK,CAAC,CAAC,cAAcgP,CAAY,EACjDC,EAAS,IAAIjP,EAAM,OACzBgM,EAAI,kBAAkBiD,CAAM,EAE5B,IAAMC,EAASD,EAAO,OAAO,MAAM,EAC7BX,EAAS,KAAK,IAAIW,EAAO,OAAQ,CAAC,EAGxC,KAAK,OAAO,KAAO,KAAK,IAAI,IAAMX,EAAS,GAAG,EAC9C,KAAK,OAAO,IAAM,KAAK,IAAI,IAAOA,EAAS,EAAE,EAC7C,KAAK,OAAO,uBAAuB,EAGnC,IAAMa,EAAU,KAAK,OAAO,IAAM,KAAK,GAAM,IACvCC,EAAed,EAAS,KAAK,IAAIa,EAAS,CAAC,EAAK,KAGhDE,EAAM,IAAIrP,EAAM,QAChBsP,EAAK,IAAItP,EAAM,QAAQ,EAAG,EAAG,CAAC,EAEpC,OAAQuP,EAAR,CACE,IAAK,QACHF,EAAI,IAAI,EAAG,EAAG,CAAC,EACf,MACF,IAAK,OACHA,EAAI,IAAI,EAAG,EAAG,EAAE,EAChB,MACF,IAAK,OACHA,EAAI,IAAI,GAAI,EAAG,CAAC,EAChB,MACF,IAAK,QACHA,EAAI,IAAI,EAAG,EAAG,CAAC,EACf,MACF,IAAK,MACHA,EAAI,IAAI,EAAG,EAAG,IAAM,EACpBC,EAAG,IAAI,EAAG,EAAG,EAAE,EACf,MACF,IAAK,SACHD,EAAI,IAAI,EAAG,GAAI,IAAM,EACrBC,EAAG,IAAI,EAAG,EAAG,CAAC,EACd,MACF,IAAK,YACHD,EAAI,IAAI,EAAG,EAAG,CAAC,CAAC,CAAC,UAAU,EAC3B,MACF,IAAK,iBACHA,EAAI,IAAI,GAAI,EAAG,EAAE,CAAC,CAAC,UAAU,CAEjC,CAEA,IAAMG,EAAeN,EAAO,MAAM,CAAC,CAAC,IAAIG,EAAI,eAAeD,CAAW,CAAC,EAWvE,GARA,AAEE,KAAK,gBADL,KAAK,aAAa,KAAK,EACH,MAGtB,KAAK,OAAO,GAAG,KAAKE,CAAE,EAGlBG,GAAY,EAAG,CACjB,KAAK,OAAO,SAAS,KAAKD,CAAY,EACtC,KAAK,SAAS,OAAO,KAAKN,CAAM,EAChC,KAAK,SAAS,OAAO,EACrBxI,EAAQ,EACR,MACF,CAEA,IAAMgJ,EAAW,KAAK,OAAO,SAAS,MAAM,EACtCC,EAAc,KAAK,SAAS,OAAO,MAAM,EAGzCC,EAAW,CAAE,EAAG,CAAE,EACxB,KAAK,aAAe,IAAI7P,EAAM,MAAM6P,EAAU,KAAK,UAAU,CAAC,CAC3D,GAAG,CAAE,EAAG,CAAE,EAAGH,CAAQ,CAAC,CACtB,OAAO1P,EAAM,OAAO,MAAM,GAAG,CAAC,CAC9B,aAAe,CACd,KAAK,OAAO,SAAS,YAAY2P,EAAUF,EAAcI,EAAS,CAAC,EACnE,KAAK,SAAS,OAAO,YAAYD,EAAaT,EAAQU,EAAS,CAAC,EAChE,KAAK,SAAS,OAAO,CACvB,CAAC,CAAC,CACD,eAAiB,CAChB,KAAK,OAAO,SAAS,KAAKJ,CAAY,EACtC,KAAK,SAAS,OAAO,KAAKN,CAAM,EAChC,KAAK,SAAS,OAAO,EACrB,KAAK,aAAe,KACpBxI,EAAQ,CACV,CAAC,CAAC,CACD,MAAM,CACX,CAAC,CACH,CAKA,YAAmB,EAAwB,EAAmB,IAAoB,CAChF,OAAO,IAAI,QAAS,GAAY,CAC9B,IAAMsF,EAAM,IAAIhM,EAAM,KAAK,CAAC,CAAC,cAAc6P,CAAM,EAC3CZ,EAAS,IAAIjP,EAAM,OACzBgM,EAAI,kBAAkBiD,CAAM,EAE5B,IAAMC,EAASD,EAAO,OAChBX,EAAS,KAAK,IAAIW,EAAO,OAAQ,CAAC,EAGxC,KAAK,OAAO,KAAO,KAAK,IAAI,IAAMX,EAAS,GAAG,EAC9C,KAAK,OAAO,IAAM,KAAK,IAAI,IAAOA,EAAS,EAAE,EAC7C,KAAK,OAAO,uBAAuB,EAEnC,IAAMa,EAAU,KAAK,OAAO,IAAM,KAAK,GAAM,IACvCC,EAAed,EAAS,KAAK,IAAIa,EAAS,CAAC,EAAK,KAEhDW,EAAa,KAAK,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI,KAAK,SAAS,MAAM,CAAC,CAAC,UAAU,EAC9EN,EAAeN,EAAO,MAAM,CAAC,CAAC,IAAIY,EAAW,eAAeV,CAAW,CAAC,EAExEM,EAAW,KAAK,OAAO,SAAS,MAAM,EACtCC,EAAc,KAAK,SAAS,OAAO,MAAM,EAEzCC,EAAW,CAAE,EAAG,CAAE,EACxB,IAAI7P,EAAM,MAAM6P,EAAU,KAAK,UAAU,CAAC,CACvC,GAAG,CAAE,EAAG,CAAE,EAAGH,CAAQ,CAAC,CACtB,OAAO1P,EAAM,OAAO,MAAM,GAAG,CAAC,CAC9B,aAAe,CACd,KAAK,OAAO,SAAS,YAAY2P,EAAUF,EAAcI,EAAS,CAAC,EACnE,KAAK,SAAS,OAAO,YAAYD,EAAaT,EAAQU,EAAS,CAAC,EAChE,KAAK,SAAS,OAAO,CACvB,CAAC,CAAC,CACD,eAAiB,CAChB,KAAK,OAAO,SAAS,KAAKJ,CAAY,EACtC,KAAK,SAAS,OAAO,KAAKN,CAAM,EAChC,KAAK,SAAS,OAAO,EACrBxI,EAAQ,CACV,CAAC,CAAC,CACD,MAAM,CACX,CAAC,CACH,CACF,EC7RA,SAAgBqJ,EACd,EACA,EACA,EACA,EAA6B,GAC7B,EAAwB,IACxB,EAAyB,IACb,CACZ,GAAI,CAACC,GAAeA,EAAY,SAAW,EACzC,MAAO,CAAE,IAAK,GAAO,MAAO,KAAM,OAAQ,KAAM,gBAAiB,GAAO,iBAAkB,GAAS,EAGrG,IAAMC,EAAY,IAAIhQ,EAAM,UAC5BgQ,EAAU,cAAcC,EAAO5C,CAAM,EAGrC,EAAoD,aAAe,GAEnE,IAAM6C,EAAaF,EAAU,iBAAiBD,EAAa,EAAI,EAE/D,GAAIG,EAAW,SAAW,EACxB,MAAO,CAAE,IAAK,GAAO,MAAO,KAAM,OAAQ,KAAM,gBAAiB,GAAO,iBAAkB,GAAS,EAGrG,IAAMC,EAAMD,EAAW,GACjBE,EAAWD,EAAI,MAAM,MAAM,EAC3BE,EAAaF,EAAI,KAAOA,EAAI,KAAK,OAAO,MAAM,EAAI,IAAInQ,EAAM,QAAQ,EAAG,EAAG,CAAC,EAC7EmQ,EAAI,QACNE,EAAW,mBAAmBF,EAAI,OAAO,WAAW,EAItD,IAAIG,EAAsC,KACtCC,EAAgBC,EAEpB,GAAIL,EAAI,MAAQA,EAAI,QAAWA,EAAI,OAAsB,SAAU,CACjE,IAAMtH,EAAOsH,EAAI,OAEXrD,EADWjE,EAAK,SACG,WAAW,SAEpC,GAAIiE,EAAS,CACX,IAAM2D,EAAW,CAACN,EAAI,KAAK,EAAGA,EAAI,KAAK,EAAGA,EAAI,KAAK,CAAC,EAC9CO,EAAS,IAAI1Q,EAAM,QACnB2Q,EAAU,IAAI3Q,EAAM,QAE1B,IAAK,IAAMgN,KAAOyD,EAAU,CAC1BC,EAAO,oBAAoB5D,EAASE,CAAG,CAAC,CAAC,aAAanE,EAAK,WAAW,EACtE8H,EAAQ,KAAKD,CAAM,CAAC,CAAC,QAAQrD,CAAM,EAGnC,IAAMuD,GAAYD,EAAQ,EAAI,GAAK,GAAOE,EACpCC,GAAY,CAACH,EAAQ,EAAI,GAAK,GAAOI,EAErCC,GAAiBf,EAAM,EAAI,GAAK,GAAOY,EACvCI,GAAiB,CAAChB,EAAM,EAAI,GAAK,GAAOc,EAExCG,EAAa,KAAK,MAAMN,EAAUI,EAAcF,EAAUG,CAAY,EAExEC,EAAaX,IACf,EAAgBW,EAChB,EAAgBR,EAAO,MAAM,EAEjC,CACF,CACF,CAYA,OAVIJ,EACK,CACL,IAAK,GACL,MAAOA,EACP,OAAQD,EACR,gBAAiB,GACjB,iBAAkBE,CACpB,EAGK,CACL,IAAK,GACL,MAAOH,EACP,OAAQC,EACR,gBAAiB,GACjB,iBAAkB,GACpB,CACF,CCpEA,IAAac,EAAb,KAA0B,CACxB,MACA,OACA,UACA,YACA,SAA6D,KAC7D,QAAuC,KAGvC,OACA,OACA,OACA,YAA0C,KAI1C,UAA6B,GAE7B,WAAmC,IAEnC,cAAiC,GAGjC,YACA,YAA+B,GAC/B,QAA8D,CAAC,EAG/D,gBAAmC,GACnC,cAA4C,KAC5C,iBAA0C,IAAIlR,EAAM,QACpD,kBAAoC,EACpC,eAAwC,IAAIA,EAAM,QAClD,oBAAsC,EACtC,UAAqC,IAAIA,EAAM,UAC/C,YAA2C,KAG3C,sBAAyC,GACzC,aAAwC,CAAC,EACzC,mBAGA,SAA+B,IAAIA,EAAM,KAGzC,mBACA,mBACA,uBACA,qBACA,mBAEA,YACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,MAAQ4L,EACb,KAAK,OAASwB,EACd,KAAK,UAAY+D,EACjB,KAAK,YAAcC,EACnB,KAAK,SAAWvD,GAAY,KAC5B,KAAK,QAAUwD,GAAW,KAG1B,KAAK,OAAS,IAAIrR,EAAM,MAAM,IAAIA,EAAM,QAAQ,GAAI,EAAG,CAAC,EAAG,GAAM,EACjE,KAAK,OAAS,IAAIA,EAAM,MAAM,IAAIA,EAAM,QAAQ,EAAG,GAAI,CAAC,EAAG,GAAM,EACjE,KAAK,OAAS,IAAIA,EAAM,MAAM,IAAIA,EAAM,QAAQ,EAAG,EAAG,EAAE,EAAG,GAAM,EAGjE,KAAK,YAAc,IAAIA,EAAM,MAC7B,KAAK,YAAY,KAAO,kCACxB,KAAK,YAAY,QAAU,GAC3B,KAAK,MAAM,IAAI,KAAK,WAAW,EAG/B,KAAK,mBAAqB,IAAIA,EAAM,MACpC,KAAK,mBAAmB,KAAO,0BAC/B,KAAK,MAAM,IAAI,KAAK,kBAAkB,EAGtC,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,EAC1D,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,EAC1D,KAAK,uBAAyB,KAAK,sBAAsB,KAAK,IAAI,EAClE,KAAK,qBAAuB,KAAK,oBAAoB,KAAK,IAAI,EAC9D,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,EAI1D,KAAK,UAAU,iBAAiB,cAAe,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,EACzF,KAAK,UAAU,iBAAiB,cAAe,KAAK,kBAAkB,EACtE,KAAK,UAAU,iBAAiB,WAAY,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,CACxF,CAEA,YAAmB,EAAmD,CACpE,KAAK,SAAW6N,CAClB,CAEA,WAAkB,EAA6B,CAC7C,KAAK,QAAUwD,CACjB,CAEA,eAAsB,EAA4B,CAChD,KAAK,YAAcC,GAAe,KAAK,UACnC,CAAC,KAAK,aAAe,KAAK,UAAU,MAAM,SAAW,SACvD,KAAK,UAAU,MAAM,OAAS,UAElC,CAKA,eAAsB,EAAuB,CAC3C,KAAK,SAAS,KAAKxF,CAAG,EACtB,KAAK,oBAAoB,EACzB,KAAK,SAAS,CAChB,CAKA,qBAAoC,CAClC,KAAK,kBAAkB,EAEvB,IAAMd,EAAO,IAAIhL,EAAM,QAAQ,EAAG,EAAG,CAAC,EAChCgP,EAAS,IAAIhP,EAAM,QAAQ,EAAG,EAAG,CAAC,EACnC,KAAK,SAAS,QAAQ,IACzB,KAAK,SAAS,QAAQgL,CAAI,EAC1B,KAAK,SAAS,UAAUgE,CAAM,GAKhC,IAAMuC,EAAK,KAAK,IAAI,KAAOvG,EAAK,CAAC,EAC3BwG,EAAK,KAAK,IAAI,KAAOxG,EAAK,CAAC,EAC3ByG,EAAK,KAAK,IAAI,KAAOzG,EAAK,CAAC,EAGjC,KAAK,QAAQ,EAAI,KAAK,kBAAkB,IAAK,SAAU,SAAUyG,EAAID,EAAIxC,CAAM,EAG/E,KAAK,QAAQ,EAAI,KAAK,kBAAkB,IAAK,QAAU,QAAUuC,EAAIE,EAAIzC,CAAM,EAG/E,KAAK,QAAQ,EAAI,KAAK,kBAAkB,IAAK,QAAU,QAAUuC,EAAIC,EAAIxC,CAAM,EAG3E,KAAK,QAAQ,IAAG,KAAK,QAAQ,EAAE,QAAU,IACzC,KAAK,QAAQ,IAAG,KAAK,QAAQ,EAAE,QAAU,IACzC,KAAK,QAAQ,IAAG,KAAK,QAAQ,EAAE,QAAU,IAG7C,KAAK,wBAAwB,CAC/B,CAEA,kBACE,EACA,EACA,EACA,EACA,EACA,EACa,CACb,IAAMzG,EAAQ,IAAIvI,EAAM,MACxB,EAAM,KAAO,kBAAkBgO,IAI/B,IAAMnF,EAAO,IAAI7I,EAAM,cAAc2J,EAAGC,CAAC,EACnC8H,EAAU,IAAI1R,EAAM,kBAAkB,CAC1C,MAAO2R,EACP,YAAa,GACb,QAAS,IACT,KAAM3R,EAAM,WACZ,WAAY,EACd,CAAC,EACK4I,EAAO,IAAI5I,EAAM,KAAK6I,EAAM6I,CAAO,EACzC,EAAK,SAAW,CAAE,MAAK,EACvB,EAAK,YAAc,IACnBnJ,EAAM,IAAIK,CAAI,EAGd,IAAMgJ,EAAQ,IAAI5R,EAAM,cAAc6I,CAAI,EACpCgJ,EAAY,IAAI7R,EAAM,kBAAkB,CAC5C,MAAO8R,EACP,YAAa,GACb,QAAS,GACX,CAAC,EACKC,EAAS,IAAI/R,EAAM,aAAa4R,EAAOC,CAAS,EACtDtJ,EAAM,IAAIwJ,CAAM,EAGhB,IAAMC,EAAa,IAAIhS,EAAM,MACvBiS,EAAc,KAAK,IAAItI,EAAGC,CAAC,EAAI,KAC/BsI,EAAW,IAAIlS,EAAM,kBAAkB,CAC3C,MAAO8R,EACP,YAAa,GACb,QAAS,GACT,UAAW,EACb,CAAC,EAGKK,EAAa,IAAInS,EAAM,eAAeiS,EAAc,GAAK,GAAI,EAAE,EAC/DG,EAAe,IAAIpS,EAAM,KAAKmS,EAAYD,CAAQ,EACxD,EAAa,SAAW,CAAE,MAAK,EAC/BF,EAAW,IAAII,CAAY,EAG3B,IAAMC,EAAY,IAAIrS,EAAM,iBAAiBiS,EAAc,IAAMA,EAAc,IAAMA,EAAc,IAAK,EAAE,EAC1GI,EAAU,QAAQ,KAAK,GAAK,CAAC,EAC7B,IAAMC,EAAY,IAAItS,EAAM,KAAKqS,EAAWH,CAAQ,EACpD,EAAU,SAAW,CAAE,MAAK,EAC5BF,EAAW,IAAIM,CAAS,EAGxB,IAAMC,EAAY,IAAIvS,EAAM,aAAaiS,EAAc,IAAMA,EAAc,IAAM,EAAE,EACnFM,EAAU,QAAQ,KAAK,GAAK,CAAC,EAC7BA,EAAU,UAAU,EAAG,EAAGN,EAAc,GAAG,EAC3C,IAAMO,EAAY,IAAIxS,EAAM,KAAKuS,EAAWL,CAAQ,EACpD,EAAU,SAAW,CAAE,MAAK,EAC5BF,EAAW,IAAIQ,CAAS,EAGxB,IAAMC,EAAY,IAAIzS,EAAM,aAAaiS,EAAc,IAAMA,EAAc,IAAM,EAAE,EACnFQ,EAAU,QAAQ,CAAC,KAAK,GAAK,CAAC,EAC9BA,EAAU,UAAU,EAAG,EAAG,CAACR,EAAc,GAAG,EAC5C,IAAMS,EAAY,IAAI1S,EAAM,KAAKyS,EAAWP,CAAQ,EACpD,EAAU,SAAW,CAAE,MAAK,EAC5BF,EAAW,IAAIU,CAAS,EAExB,EAAW,YAAc,IACzBnK,EAAM,IAAIyJ,CAAU,EAGpB,IAAMW,EAAY,CAAC/J,EAAMwJ,EAAcE,EAAWE,EAAWE,CAAS,EAGhEE,EAAM,SAAS,cAAc,KAAK,EACxC,EAAI,UAAY,mCAAmC5E,IACnD,EAAI,YAAc,GAAGA,EAAK,YAAY,EAAE,MACxC,IAAM6E,EAAQ,IAAI5S,EAAAA,YAAY2S,CAAG,EACjCC,EAAM,SAAS,IAAKlJ,EAAI,EAAK,IAAOC,EAAI,EAAK,IAAM,CAAC,EACpDrB,EAAM,IAAIsK,CAAK,EAEf,IAAIzH,EAAS,IAAIpL,EAAM,QAAQ,EAAG,EAAG,CAAC,EAkBtC,OAhBIgO,IAAS,KACX,EAAM,SAAS,EAAI,KAAK,GAAK,EAC7BzF,EAAM,SAAS,IAAIyG,EAAO,EAAGA,EAAO,EAAGA,EAAO,CAAC,EAC/C,EAAS,IAAIhP,EAAM,QAAQ,EAAG,EAAG,CAAC,GACzBgO,IAAS,KAClB,EAAM,SAAS,EAAI,CAAC,KAAK,GAAK,EAC9BzF,EAAM,SAAS,IAAIyG,EAAO,EAAGA,EAAO,EAAGA,EAAO,CAAC,EAC/C,EAAS,IAAIhP,EAAM,QAAQ,EAAG,EAAG,CAAC,IAElCuI,EAAM,SAAS,IAAI,EAAG,EAAG,CAAC,EAC1BA,EAAM,SAAS,IAAIyG,EAAO,EAAGA,EAAO,EAAGA,EAAO,CAAC,EAC/C,EAAS,IAAIhP,EAAM,QAAQ,EAAG,EAAG,CAAC,GAGpC,KAAK,YAAY,IAAIuI,CAAK,EAEnB,CACL,OACA,QACA,OACA,SACA,aACA,YACA,QACA,UAAWqK,EACX,UACA,YACA,WACA,SACA,YACA,cACA,SAAU5D,EAAOhB,GACjB,SAAU,GACV,QAAS,EACX,CACF,CAEA,mBAAkC,CAChC,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAS,CACpD,IAAM8E,EAAS,KAAK,QAAQ9E,GACxB8E,IACFA,EAAO,KAAK,SAAS,QAAQ,EAC7BA,EAAO,QAAQ,QAAQ,EACvBA,EAAO,OAAO,SAAS,QAAQ,EAC/BA,EAAO,UAAU,QAAQ,EACzBA,EAAO,SAAS,QAAQ,EACxB,KAAK,YAAY,OAAOA,EAAO,KAAK,EAExC,CAAC,EACD,KAAK,QAAU,CAAC,CAClB,CAMA,yBAAwC,CACtC,KAAK,YAAY,QAAU,KAAK,WAAa,KAAK,cAClD,KAAK,YAAc,KAAK,UAExB,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAS,CACpD,IAAMnJ,EAAI,KAAK,QAAQqE,GACnBrE,IAEF,EAAE,MAAM,QAAU,KAAK,WAAa,KAAK,eAAiBA,EAAE,QAEhE,CAAC,CACH,CAKA,qBAA4B,EAAwB,CAIlD,GAHA,KAAK,UAAYoE,EACjB,KAAK,wBAAwB,EAEzBA,EAAS,CAGX,GAAI,CADgB,CAAC,IAAK,IAAK,GAAG,CAAC,CAAoB,KAAM,GAAM,KAAK,QAAQgF,EAAE,EAAE,OAC/E,EAAY,CACf,IAAMC,EAAW,KAAK,QAAQ,KAAK,aAAe,KAAK,QAAQ,EAC3DA,IAAU,EAAS,QAAU,GACnC,CAGA,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAS,CACpD,IAAMrJ,EAAI,KAAK,QAAQqE,GACnBrE,GAAKA,EAAE,QACT,KAAK,yBAAyBqE,EAAMrE,EAAE,QAAQ,EAE9C,KAAK,oBAAoBqE,CAAI,CAEjC,CAAC,CACH,MAEE,KAAK,qBAAqB,EAG5B,KAAK,YAAY,EACjB,KAAK,oBAAoB,CAC3B,CAEA,sBAAuC,CACrC,OAAO,KAAK,SACd,CAMA,eAAsB,EAAoB,EAAwB,CAChE,IAAMrE,EAAI,KAAK,QAAQqE,GAClBrE,IAEL,EAAE,QAAUoE,EACRA,GACF,KAAK,WAAaC,EACd,KAAK,WACP,KAAK,yBAAyBA,EAAMrE,EAAE,QAAQ,GAGhD,KAAK,oBAAoBqE,CAAI,EAG/B,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAC3B,CAEA,WAAkB,EAAoB,EAA4B,CAChE,IAAMrE,EAAI,KAAK,QAAQqE,GACjBiF,EAAWlF,IAAY,IAAA,GAAsB,CAAEpE,GAAG,QAAfoE,EAEzC,OADA,KAAK,eAAeC,EAAMiF,CAAQ,EAC3BA,CACT,CAEA,cAAqB,EAA6B,CAChD,MAAO,CAAC,CAAE,KAAK,QAAQjF,EAAK,EAAE,OAChC,CAKA,kBAAyB,EAA0B,CACjD,KAAK,WAAaA,EAClB,IAAMrE,EAAI,KAAK,QAAQqE,GACnBrE,GAAK,CAACA,EAAE,UACV,EAAE,QAAU,GACR,KAAK,WACP,KAAK,yBAAyBqE,EAAMrE,EAAE,QAAQ,GAGlD,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,CAC3B,CAEA,mBAAyC,CACvC,OAAO,KAAK,UACd,CAKA,2BAAkC,EAAwB,CACxD,KAAK,cAAgBgE,EACrB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,CAC3B,CAEA,4BAA6C,CAC3C,OAAO,KAAK,aACd,CAKA,eAAsB,EAAoB,EAAwB,CAChE,IAAMmF,EAAS,KAAK,QAAQ9E,GAC5B,GAAI8E,EAAQ,CAEV,GADA,EAAO,SAAWI,EACd,KAAK,WAAaJ,EAAO,QAC3B,KAAK,yBAAyB9E,EAAM8E,EAAO,QAAQ,MAC9C,CACL,IAAM5G,EAAM,KAAK,SAAS,IAAI8B,GAExBmF,EADM,KAAK,SAAS,IAAInF,GACV9B,EACdkH,EAAWD,EAAQ,GAAKL,EAAO,SAAW5G,GAAOiH,EAAQ,GAC/D,EAAO,UAAU,YAAc,GAAGnF,EAAK,YAAY,EAAE,QAAQoF,EAAW,IAAA,CAAK,QAAQ,CAAC,EAAE,GAAGF,EAAU,QAAU,IACjH,CACF,CACA,KAAK,oBAAoB,CAC3B,CAEA,qBAA4B,EAAwB,CAClD,KAAK,eAAe,KAAK,WAAYA,CAAO,CAC9C,CAEA,sBAAuC,CACrC,MAAO,CAAC,CAAE,KAAK,QAAQ,KAAK,WAAW,EAAE,QAC3C,CAEA,WAAkB,EAA6B,CAC7C,IAAMvJ,EAAI,KAAK,QAAQqE,GAGvB,OAFKrE,GACL,KAAK,eAAeqE,EAAM,CAACrE,EAAE,QAAQ,EAC9BA,EAAE,UAFM,EAGjB,CAKA,gBAAuB,EAA2B,CAChD,IAAMqF,EAAS,IAAIhP,EAAM,QACnBqT,EAAU,KAAK,SAAS,QAAQ,EACjCA,EAGHrE,EAAO,IAAI,EAAG,EAAG,CAAC,EAFlB,KAAK,SAAS,UAAUA,CAAM,EAMhC,KAAK,iBAAiB,GAGahB,EAAO,CAACA,CAAI,EAAK,CAAC,IAAK,IAAK,GAAG,EAAA,CAEvD,QAAS,GAAM,CACxB,IAAM8E,EAAS,KAAK,QAAQC,GAC5B,GAAID,EAAQ,CACV,EAAO,SAAW9D,EAAO+D,GACzB,EAAO,SAAW,GAClBD,EAAO,MAAM,SAAS,IAAI9D,EAAO,EAAGA,EAAO,EAAGA,EAAO,CAAC,EACjDhB,IAEH,EAAO,QAAU+E,IAAM,KAEzB,IAAM7G,EAAMmH,EAAU,GAAK,KAAK,SAAS,IAAIN,GAEvCI,GADME,EAAU,EAAI,KAAK,SAAS,IAAIN,IACxB7G,EACdkH,EAAWD,EAAQ,GAAK,CAAC,MAAML,EAAO,QAAQ,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,GAAIA,EAAO,SAAW5G,GAAOiH,CAAK,CAAC,EAAI,GACpH,EAAO,UAAU,YAAc,GAAGJ,EAAE,YAAY,EAAE,QAAQK,EAAW,IAAA,CAAK,QAAQ,CAAC,EAAE,EACvF,CACF,CAAC,EAED,KAAK,WAAapF,GAAQ,IAC1B,KAAK,cAAgB,GACrB,KAAK,UAAY,GAGjB,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAM,CACjD,IAAM8E,EAAS,KAAK,QAAQC,GACxBD,GAAUA,EAAO,QACnB,KAAK,yBAAyBC,EAAGD,EAAO,QAAQ,EAEhD,KAAK,oBAAoBC,CAAC,CAE9B,CAAC,EAED,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,KAAK,oBAAoB,CAC3B,CAKA,oBAA6C,CAC3C,IAAMM,EAAU,KAAK,SAAS,QAAQ,EAChCC,EAAOD,EAAU,GAAK,KAAK,SAAS,IAAI,EACxCE,EAAOF,EAAU,EAAI,KAAK,SAAS,IAAI,EACvCG,EAAOH,EAAU,GAAK,KAAK,SAAS,IAAI,EACxCI,EAAOJ,EAAU,EAAI,KAAK,SAAS,IAAI,EACvCK,EAAOL,EAAU,GAAK,KAAK,SAAS,IAAI,EACxCM,EAAON,EAAU,EAAI,KAAK,SAAS,IAAI,EAEvCO,GAAY,EAAa,EAAa,IAAgB,CAC1D,IAAMT,EAAQhH,EAAMD,EAEpB,OADI,MAAMwB,CAAG,GAAKyF,GAAS,EAAU,GAC9B,KAAK,IAAI,EAAG,KAAK,IAAI,GAAIzF,EAAMxB,GAAOiH,CAAK,CAAC,CACrD,EAEMU,EAAK,KAAK,QAAQ,EAClBC,EAAK,KAAK,QAAQ,EAClBC,EAAK,KAAK,QAAQ,EAElBC,EAA+C,CACnD,EAAG,CACD,QAASH,GAAI,SAAW,GACxB,SAAUA,GAAI,UAAY,GAC1B,SAAUA,GAAI,UAAY,EAC1B,SAAUD,EAASC,GAAI,UAAY,EAAGP,EAAMC,CAAI,CAClD,EACA,EAAG,CACD,QAASO,GAAI,SAAW,GACxB,SAAUA,GAAI,UAAY,GAC1B,SAAUA,GAAI,UAAY,EAC1B,SAAUF,EAASE,GAAI,UAAY,EAAGN,EAAMC,CAAI,CAClD,EACA,EAAG,CACD,QAASM,GAAI,SAAW,GACxB,SAAUA,GAAI,UAAY,GAC1B,SAAUA,GAAI,UAAY,EAC1B,SAAUH,EAASG,GAAI,UAAY,EAAGL,EAAMC,CAAI,CAClD,CACF,EAEMM,EAAU,KAAK,QAAQ,KAAK,YAC5BC,EAAcF,EAAK,KAAK,WAAW,EAAE,UAAY,GACjDG,EAAaF,GAAS,UAAY,EAClCG,EAAaH,GAAS,UAAY,GAExC,MAAO,CACL,QAAS,KAAK,UACd,OACA,cAAe,KAAK,cACpB,KAAM,KAAK,WACX,UAAWG,EACX,SAAUF,EACV,SAAUC,CACZ,CACF,CAEA,qBAAoC,CAC9B,KAAK,SACP,KAAK,QAAQ,KAAK,oBAAqB,KAAK,mBAAmB,CAAC,CAEpE,CAOA,kBAA0B,EAAuB,CAC/C,GAAI,EAAE,SAAW,EAAG,OAEpB,GAAI,KAAK,sBAAuB,CAC9B,KAAK,sBAAsB,CAAC,EAC5B,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EACjB,MACF,CAEA,GAAI,CAAC,KAAK,aAAe,CAAC,KAAK,WAAa,CAAC,KAAK,cAAe,OAEjE,IAAME,EAAO,KAAK,UAAU,sBAAsB,EAC5CrE,EAAQ,IAAIhQ,EAAM,SACpB,EAAE,QAAUqU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAChD,EACA,KAAK,UAAU,cAAcrE,EAAO,KAAK,MAAM,EAG/C,IAAMsE,EAAoB,CAAC,IAAK,IAAK,GAAG,CAAC,CACtC,IAAK,GAAS,KAAK,QAAQtG,EAAK,CAAC,CACjC,OAAQ,GAAwB,CAAC,EAAErE,GAAKA,EAAE,SAAWA,EAAE,MAAM,QAAQ,EAExE,GAAI2K,EAAiB,SAAW,EAAG,OAEnC,IAAIC,EAA+D,KAEnE,IAAK,IAAMzB,KAAUwB,EAAkB,CACrC,IAAMrE,EAAa,KAAK,UAAU,iBAAiB6C,EAAO,UAAW,EAAI,EACzE,GAAI7C,EAAW,OAAS,EAAG,CACzB,IAAM5C,EAAO4C,EAAW,EAAE,CAAC,UACvB,CAACsE,GAAclH,EAAOkH,EAAW,YACnC,EAAa,CAAE,SAAQ,SAAUlH,CAAK,EAE1C,CACF,CAEA,GAAIkH,EAAY,CAEd,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EAEjB,IAAMC,EAAeD,EAAW,OAChC,KAAK,WAAaC,EAAa,KAC/B,KAAK,gBAAkB,GACvB,KAAK,cAAgBA,EAGjB,KAAK,WACP,KAAK,SAAS,QAAU,GACpB,UAAW,KAAK,WAClB,KAAM,SAAiB,MAAQ,GAC1B,KAAK,SAAiB,WAAc,KAAK,SAAiB,WAC7D,KAAM,SAAiB,UAAU,KAAM,KAAK,SAAiB,SAAS,IAK5E,KAAK,UAAU,MAAM,OAAS,WAC9B,EAAa,QAAQ,QAAU,IAC/B,EAAa,SAAS,QAAU,EAGhC,KAAK,iBAAiB,IAAI,EAAE,QAAS,EAAE,OAAO,EAC9C,KAAK,kBAAoBA,EAAa,SAGtC,IAAMC,EAAa,IAAIzU,EAAM,QACzBwU,EAAa,OAAS,IAAKC,EAAW,IAAI,EAAG,EAAG,CAAC,EAC5CD,EAAa,OAAS,IAAKC,EAAW,IAAI,EAAG,EAAG,CAAC,EACrDA,EAAW,IAAI,EAAG,EAAG,CAAC,EAE3B,IAAMC,EAAKF,EAAa,MAAM,SAAS,MAAM,EACvCrK,EAAKqK,EAAa,MAAM,SAAS,MAAM,CAAC,CAAC,IAAIC,CAAU,EAEvDE,EAAOD,EAAG,QAAQ,KAAK,MAAM,EAC7BE,EAAU,IAAI5U,EAAM,SACtB2U,EAAK,EAAI,GAAKN,EAAK,MAAS,GAC5B,CAACM,EAAK,EAAI,GAAKN,EAAK,OAAU,CAClC,EAEMQ,EAAO1K,EAAG,QAAQ,KAAK,MAAM,EAC7B2K,EAAU,IAAI9U,EAAM,SACtB6U,EAAK,EAAI,GAAKR,EAAK,MAAS,GAC5B,CAACQ,EAAK,EAAI,GAAKR,EAAK,OAAU,CAClC,EAEA,KAAK,eAAe,WAAWS,EAASF,CAAO,EAC/C,KAAK,oBAAsB,KAAK,eAAe,SAAS,EAGxD,OAAO,iBAAiB,cAAe,KAAK,uBAAwB,CAAE,QAAS,EAAK,CAAC,EACrF,OAAO,iBAAiB,YAAa,KAAK,qBAAsB,CAAE,QAAS,EAAK,CAAC,CACnF,CACF,CAEA,sBAA8B,EAAuB,CACnD,GAAI,CAAC,KAAK,iBAAmB,CAAC,KAAK,cAAe,OAElD,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EAEjB,IAAMG,EAAa,IAAI/U,EAAM,QAC3B,EAAE,QAAU,KAAK,iBAAiB,EAClC,EAAE,QAAU,KAAK,iBAAiB,CACpC,EAEIgV,EAAa,EACjB,GAAI,KAAK,oBAAsB,GAE7B,EAAa,KAAK,eAAe,IAAID,CAAU,EAAI,KAAK,wBACnD,CAEL,IAAM/J,EAAO,IAAIhL,EAAM,QACvB,KAAK,SAAS,QAAQgL,CAAI,EAC1B,IAAMiK,EAAW,KAAK,IAAI,GAAKjK,EAAK,KAAK,cAAc,KAAK,EAC5D,EAAc,CAAC+J,EAAW,EAAI,IAAOE,CACvC,CAEA,IAAM/I,EAAM,KAAK,SAAS,IAAI,KAAK,cAAc,MAC3CC,EAAM,KAAK,SAAS,IAAI,KAAK,cAAc,MAE7C+I,EAAS,KAAK,kBAAoBF,EACtC,EAAS,KAAK,IAAI9I,EAAK,KAAK,IAAIC,EAAK+I,CAAM,CAAC,EAE5C,KAAK,yBAAyB,KAAK,cAAc,KAAMA,CAAM,CAC/D,CAEA,oBAA4B,EAAuB,CAC5C,KAAK,kBAEV,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EAEjB,KAAK,gBAAkB,GAEvB,AAGE,KAAK,iBAFL,KAAK,cAAc,QAAQ,QAAU,IACrC,KAAK,cAAc,SAAS,QAAU,GACjB,MAGvB,KAAK,UAAU,MAAM,OAAS,UAE9B,OAAO,oBAAoB,cAAe,KAAK,uBAAwB,CAAE,QAAS,EAAK,CAAC,EACxF,OAAO,oBAAoB,YAAa,KAAK,qBAAsB,CAAE,QAAS,EAAK,CAAC,EAIpF,0BAA4B,CACtB,KAAK,WACH,UAAW,KAAK,WAClB,KAAM,SAAiB,MAAQ,GAC1B,KAAK,SAAiB,WAAc,KAAK,SAAiB,WAC7D,KAAM,SAAiB,UAAU,KAAM,KAAK,SAAiB,SAAS,GAG1E,KAAK,SAAS,QAAU,GAE5B,CAAC,EACH,CAKA,kBAA0B,EAAuB,CAG/C,GAFI,KAAK,iBACL,KAAK,uBACL,CAAC,KAAK,aAAe,CAAC,KAAK,WAAa,CAAC,KAAK,cAAe,OAEjE,IAAMb,EAAO,KAAK,UAAU,sBAAsB,EAC5CrE,EAAQ,IAAIhQ,EAAM,SACpB,EAAE,QAAUqU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAChD,EACA,KAAK,UAAU,cAAcrE,EAAO,KAAK,MAAM,EAE/C,IAAMsE,EAAoB,CAAC,IAAK,IAAK,GAAG,CAAC,CACtC,IAAK,GAAS,KAAK,QAAQtG,EAAK,CAAC,CACjC,OAAQ,GAAwB,CAAC,EAAErE,GAAKA,EAAE,SAAWA,EAAE,MAAM,QAAQ,EAEpEwL,EAAsC,KAE1C,IAAK,IAAMrC,KAAUwB,EAEnB,GADmB,KAAK,UAAU,iBAAiBxB,EAAO,UAAW,EACjE,CAAA,CAAW,OAAS,EAAG,CACzB,EAAiBA,EAAO,KACxB,KACF,CAGF,KAAK,eAAeqC,CAAc,EAC9BA,EACF,KAAK,UAAU,MAAM,OAAS,OACrB,KAAK,UAAU,MAAM,SAAW,SACzC,KAAK,UAAU,MAAM,OAAS,UAElC,CAEA,eAAuB,EAAiC,CAClD,KAAK,cAAgBnH,IACzB,KAAK,YAAcA,EAEnB,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAM,CACjD,IAAMrE,EAAI,KAAK,QAAQoJ,GACnBpJ,IACEoJ,IAAM/E,GACR,EAAE,QAAQ,QAAU,IACpB,EAAE,UAAU,QAAU,EACtB,EAAE,SAAS,QAAU,IAErB,EAAE,QAAQ,QAAU,IACpB,EAAE,UAAU,QAAU,IACtB,EAAE,SAAS,QAAU,IAG3B,CAAC,EACH,CAKA,kBAA0B,EAAqB,CAC7C,GAAI,CAAC,KAAK,aAAe,CAAC,KAAK,WAAa,CAAC,KAAK,cAAe,OAEjE,IAAMqG,EAAO,KAAK,UAAU,sBAAsB,EAC5CrE,EAAQ,IAAIhQ,EAAM,SACpB,EAAE,QAAUqU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAChD,EACA,KAAK,UAAU,cAAcrE,EAAO,KAAK,MAAM,EAE/C,IAAMsE,EAAoB,CAAC,IAAK,IAAK,GAAG,CAAC,CACtC,IAAK,GAAS,KAAK,QAAQtG,EAAK,CAAC,CACjC,OAAQ,GAAwB,CAAC,EAAErE,GAAKA,EAAE,SAAWA,EAAE,MAAM,QAAQ,EAExE,IAAK,IAAMmJ,KAAUwB,EAEnB,GADmB,KAAK,UAAU,iBAAiBxB,EAAO,UAAW,EACjE,CAAA,CAAW,OAAS,EAAG,CACzB,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EACjB,KAAK,WAAWA,EAAO,IAAI,EAC3B,KACF,CAEJ,CAMA,yBAAgC,EAAoB,EAAmB,CACrE,IAAMA,EAAS,KAAK,QAAQ9E,GAC5B,GAAI,CAAC8E,EAAQ,OAEb,EAAO,SAAWpF,EAEdM,IAAS,KACX,EAAO,MAAM,SAAS,EAAIN,EACtBoF,EAAO,SACT,KAAK,OAAO,IAAI,IAAI9S,EAAM,QAAQ,EAAG,EAAG,CAAC,EAAG,CAAC0N,CAAG,EAEhD,KAAK,OAAO,IAAI,IAAI1N,EAAM,QAAQ,GAAI,EAAG,CAAC,EAAG0N,CAAG,GAEzCM,IAAS,KAClB,EAAO,MAAM,SAAS,EAAIN,EACtBoF,EAAO,SACT,KAAK,OAAO,IAAI,IAAI9S,EAAM,QAAQ,EAAG,EAAG,CAAC,EAAG,CAAC0N,CAAG,EAEhD,KAAK,OAAO,IAAI,IAAI1N,EAAM,QAAQ,EAAG,GAAI,CAAC,EAAG0N,CAAG,GAEzCM,IAAS,MAClB,EAAO,MAAM,SAAS,EAAIN,EACtBoF,EAAO,SACT,KAAK,OAAO,IAAI,IAAI9S,EAAM,QAAQ,EAAG,EAAG,CAAC,EAAG,CAAC0N,CAAG,EAEhD,KAAK,OAAO,IAAI,IAAI1N,EAAM,QAAQ,EAAG,EAAG,EAAE,EAAG0N,CAAG,GAIpD,EAAO,QAAU,GACjB,KAAK,YAAY,EAEjB,IAAMxB,EAAM,KAAK,SAAS,IAAI8B,GAExBmF,EADM,KAAK,SAAS,IAAInF,GACV9B,EACdkH,EAAWD,EAAQ,GAAKzF,EAAMxB,GAAOiH,EAAQ,GACnD,EAAO,UAAU,YAAc,GAAGnF,EAAK,YAAY,EAAE,QAAQoF,EAAW,IAAA,CAAK,QAAQ,CAAC,EAAE,GAAGN,EAAO,SAAW,QAAU,KAEvH,KAAK,oBAAoB,EAErB,KAAK,SACP,KAAK,QAAQ,KAAK,kBAAmB,CACnC,OACA,SAAUpF,EACV,WACA,SAAUoF,EAAO,SACjB,QAAS,EACX,CAAC,CAEL,CAEA,gBAAuB,EAAoB,EAAwB,CACjE,IAAM5G,EAAM,KAAK,SAAS,IAAI8B,GACxB7B,EAAM,KAAK,SAAS,IAAI6B,GAExBN,EAAMxB,EADY,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGkH,CAAQ,CACtC,GAAmBjH,EAAMD,GAC3C,KAAK,yBAAyB8B,EAAMN,CAAG,CACzC,CAEA,gBAAuB,EAA4B,CACjD,IAAMoF,EAAS,KAAK,QAAQ9E,GAC5B,GAAI,CAAC8E,EAAQ,MAAO,IACpB,IAAM5G,EAAM,KAAK,SAAS,IAAI8B,GAExBmF,EADM,KAAK,SAAS,IAAInF,GACV9B,EACpB,OAAOiH,EAAQ,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,GAAIL,EAAO,SAAW5G,GAAOiH,CAAK,CAAC,EAAI,EACjF,CAEA,oBAA4B,EAA0B,CAChDnF,IAAS,IACX,KAAK,OAAO,IAAI,IAAIhO,EAAM,QAAQ,GAAI,EAAG,CAAC,EAAG,GAAM,EAC1CgO,IAAS,IAClB,KAAK,OAAO,IAAI,IAAIhO,EAAM,QAAQ,EAAG,GAAI,CAAC,EAAG,GAAM,EAC1CgO,IAAS,KAClB,KAAK,OAAO,IAAI,IAAIhO,EAAM,QAAQ,EAAG,EAAG,EAAE,EAAG,GAAM,CAEvD,CAEA,sBAAqC,CACnC,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAM,CACjD,KAAK,oBAAoB+S,CAAC,CAC5B,CAAC,CACH,CAGA,gBAAuB,EAAoB,EAAkB,EAAoB,GAAa,CAC5F,KAAK,WAAa/E,EAClB,IAAM8E,EAAS,KAAK,QAAQ9E,GACxB8E,IACF,EAAO,SAAWsC,EAClB,EAAO,QAAU,IAEnB,KAAK,qBAAqB,EAAI,EAC9B,KAAK,gBAAgBpH,EAAMoF,CAAQ,CACrC,CAEA,YAAmB,EAA0B,CAC3C,KAAK,eAAepF,EAAM,EAAK,CACjC,CAEA,aAAoB,EAKlB,CACA,IAAM8E,EAAS,KAAK,QAAQ9E,GAC5B,GAAI,CAAC8E,EACH,MAAO,CAAE,QAAS,GAAO,SAAU,GAAK,SAAU,GAAO,SAAU,CAAE,EAEvE,IAAM5G,EAAM,KAAK,SAAS,IAAI8B,GAExBmF,EADM,KAAK,SAAS,IAAInF,GACV9B,EACdkH,EAAWD,EAAQ,GAAKL,EAAO,SAAW5G,GAAOiH,EAAQ,GAC/D,MAAO,CACL,QAAS,KAAK,WAAaL,EAAO,QAClC,WACA,SAAUA,EAAO,SACjB,SAAUA,EAAO,QACnB,CACF,CAEA,iBAAwB,EAAwB,CAC9C,KAAK,2BAA2BnF,CAAO,CACzC,CAEA,kBAAmC,CACjC,OAAO,KAAK,aACd,CAMA,iBAA+B,CAC7B,KAAK,iBAAiB,EACtB,KAAK,sBAAwB,GAC7B,KAAK,aAAe,CAAC,EACrB,KAAK,UAAU,MAAM,OAAS,WAChC,CAEA,kBAAgC,CAC9B,KAAK,sBAAwB,GAC7B,KAAK,UAAU,MAAM,OAAS,UAC9B,KAAK,mBAAmB,CAC1B,CAEA,kBAAgC,CAC9B,KAAK,YAAc,KACnB,KAAK,mBAAmB,EACxB,KAAK,YAAY,CACnB,CAEA,kBAAmC,CAMjC,OALI,KAAK,aACP,KAAK,YAAY,OAAO,EACxB,KAAK,YAAY,EACV,IAEF,EACT,CAEA,gBAAiC,CAC/B,OAAO,KAAK,cAAgB,IAC9B,CAEA,sBAA8B,EAAuB,CACnD,IAAM0G,EAAO,KAAK,UAAU,sBAAsB,EAO5CgB,EAAOxF,EACX,IAPgB7P,EAAM,SACpB,EAAE,QAAUqU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAK9C,EACA,KAAK,OACL,KAAK,YAAY,UAAU,EAC3B,GACAA,EAAK,MACLA,EAAK,MACP,EAEKgB,EAAK,KAAQA,EAAK,OAIvB,KAAK,eAAeA,EAAK,KAAK,CAChC,CAEA,eAAuB,EAA4B,CACjD,KAAK,aAAa,KAAKC,EAAM,MAAM,CAAC,EAEpC,IAAMC,EAAa,IAAIvV,EAAM,eAAe,EAAG,GAAI,EAAE,EAC/CwV,EAAY,IAAIxV,EAAM,kBAAkB,CAAE,MAAO,QAAU,UAAW,EAAM,CAAC,EAC7EyV,EAAS,IAAIzV,EAAM,KAAKuV,EAAYC,CAAS,EAMnD,GALAC,EAAO,SAAS,KAAKH,CAAK,EAC1B,EAAO,SAAW,CAAE,kBAAmB,GAAI,EAC3C,KAAK,mBAAmB,IAAIG,CAAM,EAClC,KAAK,wBAAwBA,CAAM,EAE/B,KAAK,aAAa,SAAW,EAAG,CAClC,IAAMtL,EAAK,KAAK,aAAa,GACvBC,EAAK,KAAK,aAAa,GAEvBsL,EAAU,IAAI1V,EAAM,QAAQ,CAAC,CAAC,WAAWoK,EAAID,CAAE,EACrD,GAAIuL,EAAQ,SAAS,EAAI,KAAS,CAChC,KAAK,aAAa,IAAI,EACtB,KAAK,mBAAmB,OAAOD,CAAM,EACrC,MACF,CACAC,EAAQ,UAAU,EAElB,IAAMC,EAAU,IAAI3V,EAAM,QAC1B,KAAK,OAAO,kBAAkB2V,CAAO,EAErC,IAAIvK,EAAS,IAAIpL,EAAM,QAAQ,CAAC,CAAC,aAAa0V,EAASC,CAAO,CAAC,CAAC,UAAU,EACtEvK,EAAO,SAAS,EAAI,OACtB,EAAS,IAAIpL,EAAM,QAAQ,CAAC,CAAC,aAAa0V,EAAS,KAAK,OAAO,EAAE,CAAC,CAAC,UAAU,GAG/E,KAAK,YAAc,IAAI1V,EAAM,MAAM,CAAC,CAAC,8BAA8BoL,EAAQjB,CAAE,EAE7E,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,YAAY,CACnB,CACF,CAKA,eAAsB,EAA+B,CACnD,GAAI,KAAK,mBAAmB,SAAS,SAAW,EAAG,OACnD,IAAMP,EAAIkH,GAAkB,KAAK,UAAU,cAAgB,OAAO,aAAe,IAC3E8E,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EAEvC,KAAK,mBAAmB,SAAU,GAAQ,CACxC,GAAIE,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBE,EAAW,IAAIhW,EAAM,QAC3B8V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAInM,EAAG,CAAC,EACtEkM,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,CAAC,CACH,CAEA,wBAAgC,EAAqB,EAA+B,CAClF,GAAIH,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBlM,EAAIkH,GAAkB,KAAK,UAAU,cAAgB,OAAO,aAAe,IAC3E8E,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EAEjCI,EAAW,IAAIhW,EAAM,QAC3B8V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAInM,EAAG,CAAC,EACtEkM,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,CAEA,oBAAmC,CACjC,KAAO,KAAK,mBAAmB,SAAS,OAAS,GAAG,CAClD,IAAMH,EAAM,KAAK,mBAAmB,SAAS,GACzCA,EAAI,UAAUA,EAAI,SAAS,QAAQ,EACnCA,EAAI,UAAUA,EAAK,SAA4B,QAAQ,EAC3D,KAAK,mBAAmB,OAAOA,CAAG,CACpC,CACF,CAKA,aAA4B,CAC1B,IAAMI,EAA8B,CAAC,EAEjC,KAAK,YACH,KAAK,QAAQ,GAAG,SAASA,EAAa,KAAK,KAAK,MAAM,EACtD,KAAK,QAAQ,GAAG,SAASA,EAAa,KAAK,KAAK,MAAM,EACtD,KAAK,QAAQ,GAAG,SAASA,EAAa,KAAK,KAAK,MAAM,GAGxD,KAAK,aAAaA,EAAa,KAAK,KAAK,WAAW,EAExD,KAAK,YAAY,oBAAoBA,CAAY,CACnD,CAEA,UAAwB,CACtB,KAAK,UAAY,GACjB,KAAK,WAAa,IAClB,KAAK,cAAgB,GAErB,KAAK,qBAAqB,EAE1B,IAAMlH,EAAS,IAAIhP,EAAM,QACzB,KAAK,SAAS,UAAUgP,CAAM,EAE9B,CAAE,IAAK,IAAK,GAAG,CAAC,CAAoB,QAAS,GAAS,CACpD,IAAMrF,EAAI,KAAK,QAAQqE,GACnBrE,IAEF,EAAE,QAAUqE,IAAS,IACrB,EAAE,SAAW,GACb,EAAE,SAAWgB,EAAOhB,GACpBrE,EAAE,MAAM,SAAS,IAAIqF,EAAO,EAAGA,EAAO,EAAGA,EAAO,CAAC,EACjD,EAAE,UAAU,YAAc,GAAGhB,EAAK,YAAY,EAAE,MAEpD,CAAC,EAED,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,oBAAoB,CAC3B,CAEA,SAAuB,CACrB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,MAAM,OAAO,KAAK,WAAW,EAClC,KAAK,MAAM,OAAO,KAAK,kBAAkB,EAEzC,KAAK,UAAU,oBAAoB,cAAe,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,EAC5F,KAAK,UAAU,oBAAoB,cAAe,KAAK,kBAAkB,EACzE,KAAK,UAAU,oBAAoB,WAAY,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,EACzF,OAAO,oBAAoB,cAAe,KAAK,uBAAwB,CAAE,QAAS,EAAK,CAAC,EACxF,OAAO,oBAAoB,YAAa,KAAK,qBAAsB,CAAE,QAAS,EAAK,CAAC,CACtF,CACF,ECnqCamI,EAAb,KAA6B,CAC3B,MACA,OACA,UACA,YACA,QACA,SAA6D,KAE7D,YAAuC,OACvC,KAAuB,IAGvB,iBACA,QAAuC,CAAC,EAGxC,aAAwC,CAAC,EACzC,mBACA,mBACA,gBACA,eAGA,mBACA,mBACA,mBAEA,YACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,MAAQvK,EACb,KAAK,OAASwB,EACd,KAAK,UAAY+D,EACjB,KAAK,YAAcC,EACnB,KAAK,QAAUC,EACf,KAAK,SAAWxD,GAAY,KAE5B,KAAK,iBAAmB,IAAI3N,EAAM,MAClC,KAAK,iBAAiB,KAAO,yBAC7B,KAAK,MAAM,IAAI,KAAK,gBAAgB,EAEpC,KAAK,mBAAqB,IAAIA,EAAM,MACpC,KAAK,mBAAmB,KAAO,4BAC/B,KAAK,MAAM,IAAI,KAAK,kBAAkB,EAEtC,KAAK,mBAAqB,IAAIA,EAAM,MACpC,KAAK,mBAAmB,KAAO,2BAC/B,KAAK,mBAAmB,IAAI,KAAK,kBAAkB,EAEnD,KAAK,gBAAkB,IAAIA,EAAM,MACjC,KAAK,gBAAgB,KAAO,wBAC5B,KAAK,mBAAmB,IAAI,KAAK,eAAe,EAGhD,IAAMkW,EAAW,IAAIlW,EAAM,eAAe,EAAG,GAAI,EAAE,EAC7CmW,EAAU,IAAInW,EAAM,kBAAkB,CAC1C,MAAO,QACP,YAAa,GACb,QAAS,GACT,UAAW,EACb,CAAC,EACD,KAAK,eAAiB,IAAIA,EAAM,KAAKkW,EAAUC,CAAO,EACtD,KAAK,eAAe,SAAW,CAAE,kBAAmB,GAAI,EACxD,KAAK,eAAe,QAAU,GAC9B,KAAK,MAAM,IAAI,KAAK,cAAc,EAElC,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,EAC1D,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,EAC1D,KAAK,mBAAqB,KAAK,kBAAkB,KAAK,IAAI,CAC5D,CAEA,YAAmB,EAAmD,CACpE,KAAK,SAAWxI,CAClB,CAEA,QAAe,EAAoB,CACjC,KAAK,KAAOpC,CACd,CAEA,QAAe,EAA6B,CACtC,KAAK,cAAgB6K,IAEzB,KAAK,aAAa,EAClB,KAAK,YAAcA,EAEfA,IAAS,QACX,KAAK,UAAU,MAAM,OAAS,UAC9B,KAAK,eAAe,QAAU,GAC9B,KAAK,gBAAgB,IAErB,KAAK,UAAU,MAAM,OAAS,YAC9B,KAAK,aAAa,EAClB,KAAK,QAAQ,KAAK,gBAAiBA,CAAI,GAE3C,CAEA,SAAkC,CAChC,OAAO,KAAK,WACd,CAEA,aAA+D,CAC7D,OAAO,KAAK,QACd,CAEA,cAA6B,CAC3B,KAAK,gBAAgB,EACrB,KAAK,UAAU,iBAAiB,cAAe,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,EACzF,KAAK,UAAU,iBAAiB,cAAe,KAAK,kBAAkB,EACtE,KAAK,UAAU,iBAAiB,WAAY,KAAK,kBAAkB,CACrE,CAEA,iBAAgC,CAC9B,KAAK,UAAU,oBAAoB,cAAe,KAAK,mBAAoB,CAAE,QAAS,EAAK,CAAC,EAC5F,KAAK,UAAU,oBAAoB,cAAe,KAAK,kBAAkB,EACzE,KAAK,UAAU,oBAAoB,WAAY,KAAK,kBAAkB,CACxE,CAEA,kBAA0B,EAAuB,CAC/C,GAAI,KAAK,cAAgB,OAAQ,OAEjC,IAAMjC,EAAO,KAAK,UAAU,sBAAsB,EAM5CgB,EAAOxF,EACX,IANgB3P,EAAM,SACpB,EAAE,QAAUmU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAI9C,EACA,KAAK,OACL,KAAK,YAAY,UAAU,EAC3B,GACAA,EAAK,MACLA,EAAK,MACP,EAGI,CAACgB,EAAK,KAAO,CAACA,EAAK,OACrB,KAAK,UAAU,MAAM,OAAS,cAC9B,KAAK,eAAe,QAAU,KAIhC,KAAK,UAAU,MAAM,OAAS,YAC9B,KAAK,eAAe,QAAU,GAC9B,KAAK,eAAe,SAAS,KAAKA,EAAK,KAAK,EAExCA,EAAK,iBACP,KAAM,eAAe,SAAqC,MAAM,OAAO,QAAQ,EAC/E,KAAK,eAAe,SAAS,kBAAoB,MAEjD,KAAM,eAAe,SAAqC,MAAM,OAAO,OAAQ,EAC/E,KAAK,eAAe,SAAS,kBAAoB,KAEnD,KAAK,sBAAsB,KAAK,cAAc,EAG1C,KAAK,aAAa,OAAS,GAC7B,KAAK,qBAAqBA,EAAK,KAAK,EAExC,CAEA,kBAA0B,EAAuB,CAE/C,GADI,KAAK,cAAgB,QACrB,EAAE,SAAW,EAAG,OAEpB,IAAMhB,EAAO,KAAK,UAAU,sBAAsB,EAM5CgB,EAAOxF,EACX,IANgB3P,EAAM,SACpB,EAAE,QAAUmU,EAAK,MAAQA,EAAK,MAAS,EAAI,EAC7C,GAAG,EAAE,QAAUA,EAAK,KAAOA,EAAK,QAAU,EAAI,CAI9C,EACA,KAAK,OACL,KAAK,YAAY,UAAU,EAC3B,GACAA,EAAK,MACLA,EAAK,MACP,EAGA,GAAI,CAACgB,EAAK,KAAO,CAACA,EAAK,MACrB,OAGF,IAAMkB,EAAelB,EAAK,MAAM,MAAM,EACtC,KAAK,aAAa,KAAKkB,CAAY,EACnC,KAAK,gBAAgBA,CAAY,EACjC,KAAK,QAAQ,KAAK,iBAAkB,CAAC,GAAG,KAAK,YAAY,CAAC,EAGtD,KAAK,cAAgB,YAAc,KAAK,aAAa,SAAW,EAClE,KAAK,0BAA0B,EACtB,KAAK,cAAgB,UAAY,KAAK,aAAa,SAAW,EACvE,KAAK,wBAAwB,EACpB,KAAK,cAAgB,SAAW,KAAK,aAAa,SAAW,GACtE,KAAK,uBAAuB,CAEhC,CAEA,gBAAwB,EAA0B,CAChD,IAAM1N,EAAO,IAAI3I,EAAM,eAAe,EAAG,GAAI,EAAE,EACzCmI,EAAM,IAAInI,EAAM,kBAAkB,CAAE,MAAO,QAAU,UAAW,EAAM,CAAC,EACvE6O,EAAS,IAAI7O,EAAM,KAAK2I,EAAMR,CAAG,EACvC0G,EAAO,SAAS,KAAKrB,CAAG,EACxB,EAAO,SAAW,CAAE,kBAAmB,GAAI,EAC3C,KAAK,mBAAmB,IAAIqB,CAAM,EAClC,KAAK,sBAAsBA,CAAM,CACnC,CAEA,kBAA0B,EAAsB,CAC1C,KAAK,cAAgB,QAAU,KAAK,aAAa,QAAU,GAC7D,KAAK,sBAAsB,CAE/B,CAMA,qBAA6B,EAAmC,CAC9D,KAAK,gBAAgB,EAErB,IAAM5D,EAAS,CAAC,GAAG,KAAK,aAAcqL,CAAY,EAClD,GAAIrL,EAAO,OAAS,EAAG,OAEvB,IAAMtC,EAAO,IAAI3I,EAAM,eAAe,CAAC,CAAC,cAAciL,CAAM,EACtD9C,EAAM,IAAInI,EAAM,mBAAmB,CACvC,MAAO,QACP,SAAU,GACV,QAAS,GACT,UAAW,EACb,CAAC,EACKyM,EAAO,IAAIzM,EAAM,KAAK2I,EAAMR,CAAG,EACrCsE,EAAK,qBAAqB,EAC1B,KAAK,gBAAgB,IAAIA,CAAI,CAC/B,CAMA,2BAA0C,CACxC,IAAMxC,EAAK,KAAK,aAAa,GACvBC,EAAK,KAAK,aAAa,GACvBqM,EAAWtM,EAAG,WAAWC,CAAE,EAC3BsM,EAAW,IAAIxW,EAAM,QAAQ,CAAC,CAAC,WAAWiK,EAAIC,CAAE,CAAC,CAAC,eAAe,EAAG,EAEpE7B,EAAQ,IAAIrI,EAAM,MAGlB2I,EAAO,IAAI3I,EAAM,eAAe,CAAC,CAAC,cAAc,CAACiK,EAAIC,CAAE,CAAC,EACxD/B,EAAM,IAAInI,EAAM,kBAAkB,CAAE,MAAO,QAAU,UAAW,EAAG,UAAW,EAAM,CAAC,EACrFyM,EAAO,IAAIzM,EAAM,KAAK2I,EAAMR,CAAG,EACrCE,EAAM,IAAIoE,CAAI,EAGd,KAAK,aAAapE,EAAO4B,EAAI,OAAQ,EACrC,KAAK,aAAa5B,EAAO6B,EAAI,OAAQ,EAGrC,IAAMwI,EAAM,SAAS,cAAc,KAAK,EACxC,EAAI,UAAY,6BAChB,EAAI,UAAY,4BAA4BrH,EAAWkL,EAAU,KAAK,IAAI,EAAE,WAC5E,IAAM5D,EAAQ,IAAI1S,EAAAA,YAAYyS,CAAG,EACjCC,EAAM,SAAS,KAAK6D,CAAQ,EAC5BnO,EAAM,IAAIsK,CAAK,EAEf,KAAK,iBAAiB,IAAItK,CAAK,EAE/B,IAAM9B,EAA4B,CAChC,GAAI,QAAQ,KAAK,IAAI,IACrB,KAAM,WACN,MAAOgQ,EACP,eAAgBlL,EAAWkL,EAAU,KAAK,IAAI,EAC9C,KAAM,KAAK,KACX,OAAQ,CAACtM,EAAIC,CAAE,CACjB,EAEA,KAAK,QAAQ,KAAK3D,CAAM,EACxB,KAAK,QAAQ,KAAK,mBAAoBA,CAAM,EAC5C,KAAK,aAAa,EAClB,KAAK,QAAQ,MAAM,CACrB,CAEA,yBAAwC,CACtC,IAAM0D,EAAK,KAAK,aAAa,GACvBC,EAAK,KAAK,aAAa,GAEvBuM,EAAiB,KAAK,IAAIvM,EAAG,EAAID,EAAG,CAAC,EACrCyM,EAAqB,IAAI1W,EAAM,QAAQiK,EAAG,EAAGA,EAAG,CAAC,CAAC,CAAC,WAAW,IAAIjK,EAAM,QAAQkK,EAAG,EAAGA,EAAG,CAAC,CAAC,EAC3FyM,EAAgB1M,EAAG,WAAWC,CAAE,EAEhC7B,EAAQ,IAAIrI,EAAM,MAGlB4W,EAAa,IAAI5W,EAAM,eAAe,CAAC,CAAC,cAAc,CAACiK,EAAIC,CAAE,CAAC,EAC9D2M,EAAY,IAAI7W,EAAM,mBAAmB,CAAE,MAAO,QAAU,SAAU,IAAM,QAAS,IAAM,UAAW,EAAM,CAAC,EAC7G8W,EAAa,IAAI9W,EAAM,KAAK4W,EAAYC,CAAS,EACvDC,EAAW,qBAAqB,EAChCzO,EAAM,IAAIyO,CAAU,EAGpB,IAAMC,EAAS,IAAI/W,EAAM,QAAQkK,EAAG,EAAGD,EAAG,EAAGC,EAAG,CAAC,EAE3C8M,EAAQ,IAAIhX,EAAM,eAAe,CAAC,CAAC,cAAc,CAACiK,EAAI8M,CAAM,CAAC,EAC7DE,EAAO,IAAIjX,EAAM,kBAAkB,CAAE,MAAO,QAAU,UAAW,EAAM,CAAC,EACxEkX,EAAQ,IAAIlX,EAAM,KAAKgX,EAAOC,CAAI,EACxC5O,EAAM,IAAI6O,CAAK,EAEf,IAAMC,EAAQ,IAAInX,EAAM,eAAe,CAAC,CAAC,cAAc,CAAC+W,EAAQ7M,CAAE,CAAC,EAC7DkN,EAAO,IAAIpX,EAAM,kBAAkB,CAAE,MAAO,SAAU,UAAW,EAAG,UAAW,EAAM,CAAC,EACtFqX,EAAQ,IAAIrX,EAAM,KAAKmX,EAAOC,CAAI,EACxC/O,EAAM,IAAIgP,CAAK,EAEf,KAAK,aAAahP,EAAO4B,EAAI,OAAQ,EACrC,KAAK,aAAa5B,EAAO6B,EAAI,QAAQ,EACrC,KAAK,aAAa7B,EAAO0O,EAAQ,SAAU,CAAG,EAG9C,IAAMO,EAAO,IAAItX,EAAM,QAAQ,CAAC,CAAC,WAAW+W,EAAQ7M,CAAE,CAAC,CAAC,eAAe,EAAG,EACpEwI,EAAM,SAAS,cAAc,KAAK,EACxC,EAAI,UAAY,2BAChB,EAAI,UAAY;2CACuBrH,EAAWoL,EAAgB,KAAK,IAAI,EAAE;sDAC3BpL,EAAWqL,EAAoB,KAAK,IAAI,EAAE,SAASrL,EAAWsL,EAAe,KAAK,IAAI,EAAE;MAE1I,IAAMhE,EAAQ,IAAI1S,EAAAA,YAAYyS,CAAG,EACjCC,EAAM,SAAS,KAAK2E,CAAI,EACxBjP,EAAM,IAAIsK,CAAK,EAEf,KAAK,iBAAiB,IAAItK,CAAK,EAE/B,IAAM9B,EAA4B,CAChC,GAAI,UAAU,KAAK,IAAI,IACvB,KAAM,SACN,MAAOkQ,EACP,eAAgBpL,EAAWoL,EAAgB,KAAK,IAAI,EACpD,KAAM,KAAK,KACX,OAAQ,CAACxM,EAAIC,CAAE,EACf,MAAO,CACL,iBACA,oBACF,CACF,EAEA,KAAK,QAAQ,KAAK3D,CAAM,EACxB,KAAK,QAAQ,KAAK,mBAAoBA,CAAM,EAC5C,KAAK,aAAa,EAClB,KAAK,QAAQ,MAAM,CACrB,CAEA,uBAAsC,CACpC,GAAI,KAAK,aAAa,OAAS,EAAG,OAElC,IAAM0E,EAAS,CAAC,GAAG,KAAK,YAAY,EAC9BsM,EAAOvM,EAAqBC,CAAM,EAElC5C,EAAQ,IAAIrI,EAAM,MAGlBwX,EAAa,CAAC,GAAGvM,EAAQA,EAAO,EAAE,EAClCwM,EAAW,IAAIzX,EAAM,eAAe,CAAC,CAAC,cAAcwX,CAAU,EAC9DE,EAAU,IAAI1X,EAAM,kBAAkB,CAAE,MAAO,QAAU,UAAW,EAAG,UAAW,EAAM,CAAC,EACzFyM,EAAO,IAAIzM,EAAM,KAAKyX,EAAUC,CAAO,EAC7CrP,EAAM,IAAIoE,CAAI,EAEdxB,EAAO,QAAS,GAAM,KAAK,aAAa5C,EAAOrB,EAAG,OAAQ,CAAC,EAG3D,IAAM2Q,EAAW,IAAI3X,EAAM,QAC3BiL,EAAO,QAAS,GAAM0M,EAAS,IAAI3Q,CAAC,CAAC,EACrC2Q,EAAS,aAAa1M,EAAO,MAAM,EAGnC,IAAMyH,EAAM,SAAS,cAAc,KAAK,EACxC,EAAI,UAAY,yBAChB,EAAI,UAAY,4BAA4BrH,EAAWkM,EAAM,GAAG,KAAK,KAAK,EAAE,EAAE,WAC9E,IAAM5E,EAAQ,IAAI1S,EAAAA,YAAYyS,CAAG,EACjCC,EAAM,SAAS,KAAKgF,CAAQ,EAC5BtP,EAAM,IAAIsK,CAAK,EAEf,KAAK,iBAAiB,IAAItK,CAAK,EAE/B,IAAM9B,EAA4B,CAChC,GAAI,QAAQ,KAAK,IAAI,IACrB,KAAM,OACN,MAAOgR,EACP,eAAgBlM,EAAWkM,EAAM,GAAG,KAAK,KAAK,EAAE,EAChD,KAAM,GAAG,KAAK,KAAK,GACnB,SACA,MAAO,CACL,MACF,CACF,EAEA,KAAK,QAAQ,KAAKhR,CAAM,EACxB,KAAK,QAAQ,KAAK,mBAAoBA,CAAM,EAC5C,KAAK,aAAa,EAClB,KAAK,QAAQ,MAAM,CACrB,CAEA,wBAAuC,CACrC,GAAI,KAAK,aAAa,SAAW,EAAG,OAEpC,IAAM0D,EAAK,KAAK,aAAa,GACvB2N,EAAS,KAAK,aAAa,GAC3B1N,EAAK,KAAK,aAAa,GAEvB2N,EAAK,IAAI7X,EAAM,QAAQ,CAAC,CAAC,WAAWiK,EAAI2N,CAAM,CAAC,CAAC,UAAU,EAC1DE,EAAK,IAAI9X,EAAM,QAAQ,CAAC,CAAC,WAAWkK,EAAI0N,CAAM,CAAC,CAAC,UAAU,EAE5DG,EAAMF,EAAG,IAAIC,CAAE,EACnB,EAAM,KAAK,IAAI,GAAI,KAAK,IAAI,EAAGC,CAAG,CAAC,EACnC,IAAMC,EAAW,KAAK,KAAKD,CAAG,EACxBE,EAAWjY,EAAM,UAAU,SAASgY,CAAQ,EAE5C3P,EAAQ,IAAIrI,EAAM,MAGlBkY,EAAQ,IAAIlY,EAAM,eAAe,CAAC,CAAC,cAAc,CAAC4X,EAAQ3N,CAAE,CAAC,EAC7DkO,EAAQ,IAAInY,EAAM,eAAe,CAAC,CAAC,cAAc,CAAC4X,EAAQ1N,CAAE,CAAC,EAC7D/B,EAAM,IAAInI,EAAM,kBAAkB,CAAE,MAAO,SAAU,UAAW,EAAG,UAAW,EAAM,CAAC,EAC3FqI,EAAM,IAAI,IAAIrI,EAAM,KAAKkY,EAAO/P,CAAG,CAAC,EACpCE,EAAM,IAAI,IAAIrI,EAAM,KAAKmY,EAAOhQ,CAAG,CAAC,EAEpC,KAAK,aAAaE,EAAO4B,EAAI,QAAQ,EACrC,KAAK,aAAa5B,EAAO6B,EAAI,QAAQ,EACrC,KAAK,aAAa7B,EAAOuP,EAAQ,SAAU,GAAG,EAG9C,IAAMQ,EAAY,KAAK,IAAInO,EAAG,WAAW2N,CAAM,EAAG1N,EAAG,WAAW0N,CAAM,CAAC,EAAI,KAAQ,GAC7ES,EAA6B,CAAC,EAEpC,IAAK,IAAIxX,EAAI,EAAGA,GAAK,GAAU,IAAK,CAClC,IAAMyX,EAAIzX,EAAI,GACR0X,EAAS,IAAIvY,EAAM,QAAQ,CAAC,CAAC,KAAK6X,CAAE,CAAC,CAAC,KAAKC,EAAIQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,eAAeF,CAAS,EAC5FC,EAAU,KAAK,IAAIrY,EAAM,QAAQ,CAAC,CAAC,WAAW4X,EAAQW,CAAM,CAAC,CAC/D,CACA,IAAMC,EAAU,IAAIxY,EAAM,eAAe,CAAC,CAAC,cAAcqY,CAAS,EAC5DI,EAAS,IAAIzY,EAAM,kBAAkB,CAAE,MAAO,SAAU,UAAW,EAAM,CAAC,EAChFqI,EAAM,IAAI,IAAIrI,EAAM,KAAKwY,EAASC,CAAM,CAAC,EAGzC,IAAMC,EAAS,IAAI1Y,EAAM,QAAQ,CAAC,CAAC,WAAW6X,EAAIC,CAAE,CAAC,CAAC,UAAU,EAC1Da,EAAWf,EAAO,MAAM,CAAC,CAAC,IAAIc,EAAO,eAAeN,EAAY,GAAG,CAAC,EAEpE1F,EAAM,SAAS,cAAc,KAAK,EACxC,EAAI,UAAY,0BAChB,EAAI,UAAY,4BAA4BuF,EAAS,QAAQ,CAAC,EAAE,YAChE,IAAMtF,EAAQ,IAAI1S,EAAAA,YAAYyS,CAAG,EACjCC,EAAM,SAAS,KAAKgG,CAAQ,EAC5BtQ,EAAM,IAAIsK,CAAK,EAEf,KAAK,iBAAiB,IAAItK,CAAK,EAE/B,IAAM9B,EAA4B,CAChC,GAAI,SAAS,KAAK,IAAI,IACtB,KAAM,QACN,MAAO0R,EACP,eAAgB,GAAGA,EAAS,QAAQ,CAAC,EAAE,GACvC,KAAM,IACN,OAAQ,CAAChO,EAAI2N,EAAQ1N,CAAE,EACvB,MAAO,CACL,YAAa+N,CACf,CACF,EAEA,KAAK,QAAQ,KAAK1R,CAAM,EACxB,KAAK,QAAQ,KAAK,mBAAoBA,CAAM,EAC5C,KAAK,aAAa,EAClB,KAAK,QAAQ,MAAM,CACrB,CAEA,aAAqB,EAAoB,EAAoB,EAAe,EAAsB,IAAW,CAC3G,IAAMoC,EAAO,IAAI3I,EAAM,eAAe,EAAG,GAAI,EAAE,EACzCmI,EAAM,IAAInI,EAAM,kBAAkB,CAAE,QAAO,UAAW,EAAM,CAAC,EAC7D6O,EAAS,IAAI7O,EAAM,KAAK2I,EAAMR,CAAG,EACvC0G,EAAO,SAAS,KAAKrB,CAAG,EACxB,EAAO,SAAW,CAAE,kBAAmBoL,CAAY,EACnDvQ,EAAM,IAAIwG,CAAM,EAChB,KAAK,sBAAsBA,CAAM,CACnC,CAEA,eAA6B,CAC3B,GAAI,KAAK,aAAa,OAAS,EAAG,CAEhC,GADA,KAAK,aAAa,IAAI,EAClB,KAAK,mBAAmB,SAAS,OAAS,EAAG,CAC/C,IAAMgK,EAAa,KAAK,mBAAmB,SAAS,KAAK,mBAAmB,SAAS,OAAS,GAC9F,KAAK,uBAAuBA,CAAU,EACtC,KAAK,mBAAmB,OAAOA,CAAU,CAC3C,CACA,KAAK,gBAAgB,EACrB,KAAK,QAAQ,KAAK,iBAAkB,CAAC,GAAG,KAAK,YAAY,CAAC,CAC5D,CACF,CAEA,cAA4B,CAC1B,KAAK,aAAe,CAAC,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,CAC1B,CAEA,iBAAgC,CAC9B,KAAO,KAAK,gBAAgB,SAAS,OAAS,GAAG,CAC/C,IAAMjD,EAAM,KAAK,gBAAgB,SAAS,GAC1C,KAAK,uBAAuBA,CAAG,EAC/B,KAAK,gBAAgB,OAAOA,CAAG,CACjC,CACF,CAEA,oBAAmC,CACjC,KAAO,KAAK,mBAAmB,SAAS,OAAS,GAAG,CAClD,IAAMA,EAAM,KAAK,mBAAmB,SAAS,GAC7C,KAAK,uBAAuBA,CAAG,EAC/B,KAAK,mBAAmB,OAAOA,CAAG,CACpC,CACF,CAMA,eAAsB,EAA+B,CACnD,IAAMlM,EAAIkH,GAAkB,KAAK,UAAU,cAAgB,OAAO,aAAe,IAC3E8E,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EAEjCoD,EAAc,GAAwB,CAC1C,GAAIlD,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBE,EAAW,IAAI9V,EAAM,QAC3B4V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAInM,EAAG,CAAC,EACtEkM,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,EAEI,KAAK,gBAAkB,KAAK,eAAe,SAC7C+C,EAAW,KAAK,cAAc,EAGhC,KAAK,mBAAmB,SAASA,CAAU,EAC3C,KAAK,iBAAiB,SAASA,CAAU,CAC3C,CAEA,sBAA8B,EAAqB,EAA+B,CAChF,GAAIlD,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBlM,EAAIkH,GAAkB,KAAK,UAAU,cAAgB,OAAO,aAAe,IAC3E8E,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EAEjCI,EAAW,IAAI9V,EAAM,QAC3B4V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAInM,EAAG,CAAC,EACtEkM,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,CAKA,UAAwB,CAGtB,IAFA,KAAK,aAAa,EAEX,KAAK,iBAAiB,SAAS,OAAS,GAAG,CAChD,IAAM1N,EAAQ,KAAK,iBAAiB,SAAS,GAC7C,KAAK,uBAAuBA,CAAK,EACjC,KAAK,iBAAiB,OAAOA,CAAK,CACpC,CAEA,KAAK,eAAe,QAAU,GAC9B,KAAK,QAAU,CAAC,EAChB,KAAK,QAAQ,KAAK,eAAe,CACnC,CAEA,uBAA+B,EAA2B,CACxDuN,EAAI,SAAU,GAAU,CAEtB,GAAKnN,EAAc,eAAkBA,EAAsB,QAAS,CAClE,IAAMsQ,EAAMtQ,EAAsB,QAC9BsQ,GAAMA,EAAG,cACXA,EAAG,cAAc,YAAYA,CAAE,EACtBA,GACTA,EAAG,OAAO,CAEd,CAEA,GAAKtQ,EAAqB,QAAWA,EAAqB,OAAQ,CAChE,IAAMQ,EAAOR,EACTQ,EAAK,UAAUA,EAAK,SAAS,QAAQ,EACrCA,EAAK,WACH,MAAM,QAAQA,EAAK,QAAQ,EAAGA,EAAK,SAAS,QAAS,GAAMb,EAAE,QAAQ,CAAC,EACrEa,EAAK,SAAS,QAAQ,EAE/B,CACF,CAAC,CACH,CAEA,YAAyC,CACvC,MAAO,CAAC,GAAG,KAAK,OAAO,CACzB,CAEA,SAAuB,CACrB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,MAAM,OAAO,KAAK,gBAAgB,EACvC,KAAK,MAAM,OAAO,KAAK,kBAAkB,EACzC,KAAK,MAAM,OAAO,KAAK,cAAc,CACvC,CACF,ECnnBa+P,EAAb,KAA4B,CAC1B,SACA,MACA,OACA,SAEA,YACE,EACA,EACA,EACA,EACA,CACA,KAAK,SAAWlT,EAChB,KAAK,MAAQ4F,EACb,KAAK,OAASwB,EACd,KAAK,SAAW+L,QAAmB,KACrC,CAKA,wBAA0E,CACxE,IAAI5P,EAAW,EACXC,EAAY,EAmChB,OAjCA,KAAK,MAAM,SAAU,GAAU,CAC7B,GAAKb,EAAqB,OAAQ,CAChC,IAAMC,EAAOD,EACPyQ,EAAY,GAAwB,CACxC,GAAI,QAAS/Q,EAAK,CAChB,IAAMC,EAAID,EACV,GAAIC,EAAE,IAAK,CACT,IAAMoB,EAAOpB,EAAE,IAAI,OAAUA,EAAE,IAAI,QAAsE,KAGzG,GAAIoB,EAAK,CACP,IAAMC,EAAID,EAAI,OAAS,EACjBE,EAAIF,EAAI,QAAU,EACpBC,EAAI,GAAKC,EAAI,GAAKD,EAAIC,EAAIL,EAAWC,IACvC,EAAWG,EACX,EAAYC,EAEhB,CACF,CACF,CACF,EAEI,MAAM,QAAQhB,EAAK,QAAQ,EAC7BA,EAAK,SAAS,QAAQwQ,CAAQ,EACrBxQ,EAAK,UACdwQ,EAASxQ,EAAK,QAAQ,CAE1B,CACF,CAAC,EAEGW,EAAW,GAAKC,EAAY,EACvB,CAAE,MAAOD,EAAU,OAAQC,CAAU,EAEvC,IACT,CAMA,MAAa,YAAY,EAA8B,CAAC,EAAoB,CAC1E,IAAM6P,EAAa7W,EAAQ,kBAAoB,IAAQA,EAAQ,QAAU,WACnE8W,EAAQD,EAAa,EAAK,OAAO7W,EAAQ,OAAU,SAAWA,EAAQ,MAAQ,EAC9E+W,EAAO/W,EAAQ,MAAQ,YACvBgX,EAAUhX,EAAQ,UAAY+W,IAAS,YAAc,EAAI,KACzDE,EAAWjX,EAAQ,UAAY,GAG/BkX,EAAe,IAAItZ,EAAM,QAC/B,KAAK,SAAS,QAAQsZ,CAAY,EAClC,IAAMC,EAAqB,KAAK,SAAS,cAAc,EACjDC,EAAqB,KAAK,MAAM,WAEhCC,EAASH,EAAa,EAAIA,EAAa,EACzCI,EACAC,EAEJ,GAAIvX,EAAQ,WACV,EAAcA,EAAQ,WAAW,MACjC,EAAeA,EAAQ,WAAW,YAC7B,GAAI6W,EAAY,CAErB,IAAMW,EAAU,KAAK,uBAAuB,EAC5C,GAAIA,GAAWA,EAAQ,MAAQ,GAAKA,EAAQ,OAAS,EAAG,CACtD,IAAMC,EAAS,KAAK,IAAID,EAAQ,MAAOA,EAAQ,MAAM,EACjDH,GAAU,GACZ,EAAcI,EACd,EAAe,KAAK,MAAMA,EAASJ,CAAM,IAEzC,EAAeI,EACf,EAAc,KAAK,MAAMA,EAASJ,CAAM,EAE5C,KAAO,CAEL,IAAMK,EAAU,KAAK,IAAI,KAAK,MAAMR,EAAa,EAAIC,CAAkB,EAAG,IAAI,EAC1EE,GAAU,GACZ,EAAcK,EACd,EAAe,KAAK,MAAMA,EAAUL,CAAM,IAE1C,EAAeK,EACf,EAAc,KAAK,MAAMA,EAAUL,CAAM,EAE7C,CACF,KACE,GAAc,KAAK,MAAMH,EAAa,EAAIJ,CAAK,EAC/C,EAAe,KAAK,MAAMI,EAAa,EAAIJ,CAAK,EAGlD,IAAM5U,EAAWlC,EAAQ,UAAY,SAAS6W,EAAa,oBAAoBS,EAAY,GAAGC,IAAiB,GAAGT,EAAM,GAAG,GAAG,KAAK,IAAI,EAAE,GAAGC,IAAS,aAAe,MAAQ,QACtKY,EAAY3X,EAAQ,WAAa,GAGjC4X,EAAgE,CAAC,EAGnE5X,EAAQ,aAAe,IAAA,KACrBA,EAAQ,aAAe,MACzB,KAAK,MAAM,WAAa,KACxB,KAAK,SAAS,cAAc,CAAC,IAE7B,KAAK,MAAM,WAAa,IAAIpC,EAAM,MAAMoC,EAAQ,UAAU,EAC1D,KAAK,SAAS,cAAc,CAAC,IAIjC,GAAI,CAEF,GAAI2X,EAAW,CACb,IAAME,EAAe,KAAK,SAAS,EAC7BC,EAAU,GAQd,GANKxE,EAAY,SACbA,EAAI,OAAS,oBACbA,EAAI,UAAYA,EAAI,SAAS,KAAM,GAAWyE,EAAE,OAAO,GAGvDF,IAAiBvE,IAAQuE,GAAgBvE,EAAI,OAASuE,EAAa,OACnEvE,EAAI,OAAS,qBAMnB,IAAK,IAAMnN,KAAS,KAAK,MAAM,SACzB,CAAC2R,EAAO3R,CAAK,GAAKA,EAAM,UAC1ByR,EAAc,KAAK,CAAE,IAAKzR,EAAO,WAAY,EAAK,CAAC,EACnD,EAAM,QAAU,GAGtB,CAEA,KAAK,SAAS,cAAc,CAAC,EAC7B,KAAK,SAAS,QAAQmR,EAAaC,EAAc,EAAK,EACtD,KAAK,OAAO,OAASD,EAAcC,EACnC,KAAK,OAAO,uBAAuB,EAGnC,IAAMnE,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EACvC,KAAK,MAAM,SAAU,GAAQ,CAC3B,GAAIE,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBE,EAAW,IAAI5V,EAAM,QAC3B0V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAIgE,EAAc,CAAC,EACjFjE,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,CAAC,EAGD,KAAK,SAAS,OAAO,KAAK,MAAO,KAAK,MAAM,EAG5C,IAAMuE,EAAU,KAAK,SAAS,WAAW,UAAUjB,EAAMC,CAAO,EAGhE,GAAIC,EAAU,CACZ,IAAMgB,EAAO,SAAS,cAAc,GAAG,EACvC,EAAK,SAAW/V,EAChB,EAAK,KAAO8V,EACZ,SAAS,KAAK,YAAYC,CAAI,EAC9BA,EAAK,MAAM,EACX,SAAS,KAAK,YAAYA,CAAI,CAChC,CAEA,OAAOD,CACT,QAAU,CAER,GAAIJ,EAAc,OAAS,EACzB,IAAK,GAAM,CAAE,MAAK,gBAAgBA,EAChC,EAAI,QAAUM,EAKlB,KAAK,MAAM,WAAad,EACxB,KAAK,SAAS,cAAcD,CAAkB,EAC9C,KAAK,SAAS,QAAQD,EAAa,EAAGA,EAAa,EAAG,EAAI,EAC1D,KAAK,OAAO,OAASA,EAAa,EAAIA,EAAa,EACnD,KAAK,OAAO,uBAAuB,EAGnC,IAAMiB,EAAQjB,EAAa,EACrB9D,EAAW,KAAK,OAAO,IAAM,KAAK,GAAM,IACxCzI,EAAS,KAAK,OAAO,SACrB0I,EAAa,KAAK,IAAID,EAAU,CAAC,EACvC,KAAK,MAAM,SAAU,GAAQ,CAC3B,GAAIE,EAAI,UAAY,OAAOA,EAAI,SAAS,mBAAsB,SAAU,CACtE,IAAMC,EAAWD,EAAI,SAAS,kBACxBE,EAAW,IAAI5V,EAAM,QAC3B0V,EAAI,iBAAiBE,CAAQ,EAE7B,IAAMC,EAAe,EADRD,EAAS,WAAW7I,CACR,EAAO0I,EAAaE,EAAY,KAAK,IAAI4E,EAAO,CAAC,EAC1E7E,EAAI,MAAM,UAAUG,CAAW,CACjC,CACF,CAAC,EAED,KAAK,SAAS,OAAO,KAAK,MAAO,KAAK,MAAM,CAC9C,CACF,CACF,EC5Ma2E,EAAb,cAA4BvX,CAAa,CACvC,aACA,YACA,gBACA,WACA,aACA,gBACA,eAEA,aAA0C,KAC1C,iBAAkD,KAClD,KAAuB,IACvB,uBAA0C,GAG1C,eAAsD,KACtD,eAAgD,CAAC,EACjD,aAAqD,KACrD,qBAA8C,KAC9C,eAAsC,IAAIhD,EAAM,MAChD,kBAAqC,GAErC,YAAY,EAAwB,CAClC,MAAM,EAEN,KAAK,KAAOmC,EAAQ,MAAQ,IAC5B,KAAK,uBAAyBA,EAAQ,iBAAmB,GAGzD,KAAK,aAAe,IAAID,EAAaC,CAAO,EAG5C,KAAK,YAAc,IAAIsD,EAAY,CACjC,iBAAkBtD,EAAQ,iBAC1B,mBAAoBA,EAAQ,mBAC5B,SAAU,KAAK,aAAa,QAC9B,CAAC,EAGD,KAAK,gBAAkB,IAAImJ,EAAgB,KAAK,aAAa,KAAK,EAClE,KAAK,WAAa,IAAIiC,EACpB,KAAK,aAAa,OAClB,KAAK,aAAa,SAClB,KAAK,aAAa,UACpB,EACA,KAAK,aAAe,IAAIsD,EACtB,KAAK,aAAa,MAClB,KAAK,aAAa,OAClB,KAAK,aAAa,UAClB,KAAK,YACL,KAAK,aAAa,SAClB,IACF,EACA,KAAK,gBAAkB,IAAIiF,EACzB,KAAK,aAAa,MAClB,KAAK,aAAa,OAClB,KAAK,aAAa,UAClB,KAAK,YACL,KACA,KAAK,aAAa,QACpB,EACA,KAAK,gBAAgB,QAAQ,KAAK,IAAI,EAEtC,KAAK,eAAiB,IAAI+C,EACxB,KAAK,aAAa,SAClB,KAAK,aAAa,MAClB,KAAK,aAAa,WACZ,KAAK,YACb,EAGA,KAAK,gBAAgB,QAAQ,KAAK,IAAI,EAGtC,KAAK,aAAa,mBAAuB,CACvC,GAAI,KAAK,gBAAkB,KAAK,kBAAmB,CACjD,IAAM2B,EAAQ,KAAK,eAAe,SAAS,EAC3C,KAAK,eAAe,OAAOA,CAAK,CAClC,CACA,KAAK,gBAAgB,OAAO,KAAK,aAAa,MAAM,EACpD,KAAK,WAAW,eAAe,EAC/B,KAAK,gBAAgB,eAAe,EACpC,KAAK,aAAa,eAAe,CACnC,CACF,CASA,MAAa,QAAQ,EAAsE,CACzF,GAAI,CACF,KAAK,KAAK,aAAc,OAAO9W,GAAW,SAAWA,EAAS,UAAU,EACxE,KAAK,WAAW,EAMhB,IAAM0C,EAAS,MAAM,KAAK,YAAY,QAAQ1C,GAJ1B,EAAiB,IAAoB,CACvD,KAAK,KAAK,gBAAiB+W,EAASC,CAAO,CAC7C,CAEgE,EAGhE,OAFA,KAAK,iBAAiBtU,CAAM,EAC5B,KAAK,KAAK,eAAgBA,CAAM,EACzBA,CACT,OAAS9C,EAAK,CACZ,IAAMgD,EAAQhD,aAAe,MAAQA,EAAU,MAAM,OAAOA,CAAG,CAAC,EAEhE,MADA,KAAK,KAAK,aAAcgD,CAAK,EACvBA,CACR,CACF,CAKA,MAAa,QAAQ,EAAgB,EAA2C,CAC9E,GAAI,CACF,KAAK,KAAK,aAAcU,CAAM,EAC9B,KAAK,WAAW,EAMhB,IAAMZ,EAAS,MAAM,KAAK,YAAY,QAAQY,EAAQD,EAAQ,IAAA,IAJ1C,EAAiB,IAAoB,CACvD,KAAK,KAAK,gBAAiB0T,EAASC,CAAO,CAC7C,CAEmF,EAGnF,OAFA,KAAK,iBAAiBtU,CAAM,EAC5B,KAAK,KAAK,eAAgBA,CAAM,EACzBA,CACT,OAAS9C,EAAK,CACZ,IAAMgD,EAAQhD,aAAe,MAAQA,EAAU,MAAM,OAAOA,CAAG,CAAC,EAEhE,MADA,KAAK,KAAK,aAAcgD,CAAK,EACvBA,CACR,CACF,CAKA,MAAa,SAAS,EAAwD,CAC5E,GAAI,CACF,KAAK,KAAK,aAAc,OAAO5C,GAAW,SAAWA,EAAS,WAAW,EACzE,KAAK,WAAW,EAMhB,IAAM0C,EAAS,MAAM,KAAK,YAAY,SAAS1C,EAAQ,IAAA,IAJnC,EAAiB,IAAoB,CACvD,KAAK,KAAK,gBAAiB+W,EAASC,CAAO,CAC7C,CAE4E,EAG5E,OAFA,KAAK,iBAAiBtU,CAAM,EAC5B,KAAK,KAAK,eAAgBA,CAAM,EACzBA,CACT,OAAS9C,EAAK,CACZ,IAAMgD,EAAQhD,aAAe,MAAQA,EAAU,MAAM,OAAOA,CAAG,CAAC,EAEhE,MADA,KAAK,KAAK,aAAcgD,CAAK,EACvBA,CACR,CACF,CAKA,MAAa,QAAQ,EAAsE,CACzF,GAAI,CACF,IAAMqU,EAAO,OAAOjX,GAAW,SAAWA,EAAUA,aAAkB,KAAOA,EAAO,KAAO,YAC3F,KAAK,KAAK,aAAciX,CAAI,EAC5B,KAAK,WAAW,EAMhB,IAAMvU,EAAS,MAAM,KAAK,YAAY,QAAQ1C,GAJ1B,EAAiB,IAAoB,CACvD,KAAK,KAAK,gBAAiB+W,EAASC,CAAO,CAC7C,CAEgE,EAGhE,OAFA,KAAK,iBAAiBtU,CAAM,EAC5B,KAAK,KAAK,eAAgBA,CAAM,EACzBA,CACT,OAAS9C,EAAK,CACZ,IAAMgD,EAAQhD,aAAe,MAAQA,EAAU,MAAM,OAAOA,CAAG,CAAC,EAEhE,MADA,KAAK,KAAK,aAAcgD,CAAK,EACvBA,CACR,CACF,CAMA,MAAa,UAAU,EAAoD,CACzE,GAAI,CAEF,IAAMsU,EADU,MAAM,KAAKzW,CACV,CAAA,CAAQ,EAAE,EAAE,MAAQ,QACrC,KAAK,KAAK,aAAcyW,CAAQ,EAChC,KAAK,WAAW,EAMhB,IAAMxU,EAAS,MAAM,KAAK,YAAY,UAAUjC,GAJ5B,EAAiB,IAAoB,CACvD,KAAK,KAAK,gBAAiBsW,EAASC,CAAO,CAC7C,CAEiE,EAGjE,OAFA,KAAK,iBAAiBtU,CAAM,EAC5B,KAAK,KAAK,eAAgBA,CAAM,EACzBA,CACT,OAAS9C,EAAK,CACZ,IAAMgD,EAAQhD,aAAe,MAAQA,EAAU,MAAM,OAAOA,CAAG,CAAC,EAEhE,MADA,KAAK,KAAK,aAAcgD,CAAK,EACvBA,CACR,CACF,CAKA,MAAa,YAAY,EAAuC,CAC9D,IAAMhB,EAAWR,EAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,YAAY,EAC/C,GAAIQ,EAAS,SAAS,MAAM,EAC1B,OAAO,KAAK,QAAQR,CAAG,EAEzB,GAAIQ,EAAS,SAAS,OAAO,EAC3B,OAAO,KAAK,SAASR,CAAG,EAE1B,GAAIQ,EAAS,SAAS,MAAM,EAC1B,OAAO,KAAK,QAAQR,CAAG,EAEzB,GAAIQ,EAAS,SAAS,MAAM,EAC1B,OAAO,KAAK,QAAQR,CAAG,EAGzB,GAAI,CACF,OAAO,MAAM,KAAK,QAAQA,CAAG,CAC/B,MAAY,CACV,GAAI,CACF,OAAO,MAAM,KAAK,QAAQA,CAAG,CAC/B,MAAa,CACX,OAAO,MAAM,KAAK,QAAQA,CAAG,CAC/B,CACF,CACF,CAKA,mBAA0B,EAAgD,aAA+B,CACvG,KAAK,WAAW,EAChB,IAAMoD,EAAQ,IAAIlI,EAAM,MAExB,GAAIkZ,IAAS,aAAc,CAEzB,IAAM2B,EAAW,IAAI7a,EAAM,YAAY,EAAG,IAAK,CAAC,EAC1C8a,EAAU,IAAI9a,EAAM,qBAAqB,CAAE,MAAO,QAAU,UAAW,GAAK,UAAW,EAAI,CAAC,EAC5F+a,EAAW,IAAI/a,EAAM,KAAK6a,EAAUC,CAAO,EACjD,EAAS,SAAS,EAAI,GACtB5S,EAAM,IAAI6S,CAAQ,EAElB,IAAMC,EAAe,IAAIhb,EAAM,iBAAiB,IAAK,IAAK,EAAG,EAAE,EACzDib,EAAc,IAAIjb,EAAM,qBAAqB,CAAE,MAAO,OAAU,UAAW,GAAK,UAAW,EAAI,CAAC,EAChGkb,EAAU,IAAIlb,EAAM,KAAKgb,EAAcC,CAAW,EACxDC,EAAQ,SAAS,IAAI,EAAG,IAAK,CAAC,EAC9BhT,EAAM,IAAIgT,CAAO,EAEjB,IAAMC,EAAW,IAAInb,EAAM,cAAc,IAAK,IAAM,GAAI,EAAE,EACpDob,EAAU,IAAIpb,EAAM,qBAAqB,CAAE,MAAO,SAAU,UAAW,GAAK,UAAW,EAAI,CAAC,EAC5Fqb,EAAW,IAAIrb,EAAM,KAAKmb,EAAUC,CAAO,EACjD,EAAS,SAAS,EAAI,KAAK,GAAK,EAChCC,EAAS,SAAS,IAAI,EAAG,IAAK,CAAC,EAC/BnT,EAAM,IAAImT,CAAQ,CACpB,MAAO,GAAInC,IAAS,WAAY,CAC9B,IAAMoC,EAAW,IAAItb,EAAM,YAAY,EAAG,GAAI,CAAC,EACzCub,EAAU,IAAIvb,EAAM,qBAAqB,CAAE,MAAO,QAAU,UAAW,EAAI,CAAC,EAC5Ewb,EAAO,IAAIxb,EAAM,KAAKsb,EAAUC,CAAO,EAC7C,EAAK,SAAS,EAAI,EAClBrT,EAAM,IAAIsT,CAAI,EAEd,IAAMC,EAAW,IAAIzb,EAAM,aAAa,EAAG,IAAK,CAAC,EAC3C0b,EAAU,IAAI1b,EAAM,qBAAqB,CAAE,MAAO,SAAU,UAAW,EAAI,CAAC,EAC5E2b,EAAO,IAAI3b,EAAM,KAAKyb,EAAUC,CAAO,EAC7C,EAAK,SAAS,EAAI,MAClB,EAAK,SAAS,EAAI,KAAK,GAAK,EAC5BxT,EAAM,IAAIyT,CAAI,CAChB,KAAO,CACL,IAAMC,EAAY,IAAI5b,EAAM,kBAAkB,IAAK,GAAK,IAAK,EAAE,EACzD6b,EAAW,IAAI7b,EAAM,qBAAqB,CAAE,MAAO,QAAU,UAAW,GAAK,UAAW,EAAI,CAAC,EAC7FuI,EAAO,IAAIvI,EAAM,KAAK4b,EAAWC,CAAQ,EAC/C,EAAK,SAAS,EAAI,EAClB3T,EAAM,IAAIK,CAAI,CAChB,CAEA,IAAMuT,EAAgB,KAAK,YAAY,iBAAiB5T,CAAK,EAG7D,OAFA,KAAK,iBAAiB4T,CAAa,EACnC,KAAK,KAAK,eAAgBA,CAAa,EAChCA,CACT,CAEA,iBAAyB,EAA+B,CACtD,KAAK,aAAe1V,EAAO,MAC3B,KAAK,aAAa,KAAO,oBACzB,KAAK,iBAAmBA,EAExB,KAAK,aAAa,MAAM,IAAI,KAAK,YAAY,EAGzCA,EAAO,YAAcA,EAAO,WAAW,OAAS,GAClD,KAAK,eAAiBA,EAAO,WAC7B,KAAK,eAAiB,IAAIpG,EAAM,eAAe,KAAK,YAAY,EAChE,KAAK,cAAc,CAAC,IAEpB,KAAK,eAAiB,CAAC,EACvB,KAAK,eAAiB,KACtB,KAAK,aAAe,KACpB,KAAK,qBAAuB,KAC5B,KAAK,kBAAoB,IAI3B,KAAK,gBAAgB,cAAc,KAAK,aAAc,CACpD,QAAS,KAAK,uBACd,KAAM,KAAK,IACb,CAAC,EAGD,KAAK,aAAa,eAAeoG,EAAO,GAAG,EAG3C,KAAK,WAAW,QAAQ,QAAS,KAAK,aAAc,CAAC,CACvD,CAEA,YAA0B,CACxB,AAKE,KAAK,kBAJL,KAAK,eAAe,cAAc,EAC9B,KAAK,cACP,KAAK,eAAe,YAAY,KAAK,YAAY,EAE7B,MAExB,KAAK,eAAiB,CAAC,EACvB,KAAK,aAAe,KACpB,KAAK,qBAAuB,KAC5B,KAAK,kBAAoB,GAEzB,KAAK,gBAAgB,SAAS,EAC9B,KAAK,aAAa,SAAS,EAC3B,KAAK,gBAAgB,OAAO,EAE5B,AAEE,KAAK,gBADL,KAAK,aAAa,MAAM,OAAO,KAAK,YAAY,EAC5B,MAEtB,KAAK,iBAAmB,KACxB,KAAK,YAAY,QAAQ,CAC3B,CAMA,cAAqB,EAA+B,EAAuB,CACzE,KAAK,aAAa,cAAcrD,EAAOD,CAAK,CAC9C,CAKA,qBAA4B,EAAgC,CAC1D,KAAK,YAAY,gBAAgBkG,CAAK,EACtC,KAAK,KAAK,eAAgBA,CAAK,CACjC,CAEA,sBAAiD,CAC/C,OAAO,KAAK,YAAY,gBAAgB,CAC1C,CAEA,cAAqB,EAA4B,CAC/C,KAAK,YAAY,cAAcC,CAAW,EAC1C,KAAK,KAAK,iBAAkBA,CAAW,CACzC,CAEA,iBAAkC,CAChC,OAAO,KAAK,YAAY,gBAAgB,CAC1C,CAKA,cAAqB,EAAwC,CAC3D,KAAK,YAAY,cAAclG,CAAK,CACtC,CAEA,eAA+B,CAC7B,OAAO,KAAK,YAAY,cAAc,CACxC,CAKA,qBAAuE,CACrE,OAAO,KAAK,YAAY,oBAAoB,CAC9C,CAEA,eAAsB,EAAwB,CAC5C,KAAK,aAAa,WAAW,QAAUuK,CACzC,CAMA,gBAAuB,EAAkB,EAA6C,CACpF,KAAK,gBAAgB,WAAWA,CAAO,EACnCnL,GAAS,OACX,KAAK,KAAOA,EAAQ,KACpB,KAAK,gBAAgB,QAAQA,EAAQ,IAAI,EACzC,KAAK,gBAAgB,QAAQA,EAAQ,IAAI,EAE7C,CAEA,uBAAwC,CACtC,OAAO,KAAK,gBAAgB,WAAW,CACzC,CAMA,YAAmB,EAAoB,EAAkB,EAAoB,GAAa,CACxF,KAAK,aAAa,gBAAgBwL,EAAMoF,EAAUgC,CAAQ,CAC5D,CAEA,oBAA2B,EAA0B,CACnD,KAAK,aAAa,YAAYpH,CAAI,CACpC,CAEA,mBAA0B,EAAoB,EAA4B,CACxE,OAAO,KAAK,aAAa,WAAWA,EAAMD,CAAO,CACnD,CAEA,mBAA0B,EAA6B,CACrD,OAAO,KAAK,aAAa,WAAWC,CAAI,CAC1C,CAEA,qBAA4B,EAAoB,CAC9C,OAAO,KAAK,aAAa,aAAaA,CAAI,CAC5C,CAEA,yBAAgC,EAAwB,CACtD,KAAK,aAAa,iBAAiBL,CAAO,CAC5C,CAEA,0BAA2C,CACzC,OAAO,KAAK,aAAa,iBAAiB,CAC5C,CAGA,qBAA4B,EAAwB,CAClD,KAAK,aAAa,qBAAqBI,CAAO,CAChD,CAEA,sBAAuC,CACrC,OAAO,KAAK,aAAa,qBAAqB,CAChD,CAEA,kBAAyB,EAA0B,CACjD,KAAK,aAAa,kBAAkBC,CAAI,CAC1C,CAEA,mBAAyC,CACvC,OAAO,KAAK,aAAa,kBAAkB,CAC7C,CAEA,yBAAgC,EAAoB,EAAwB,CAC1E,KAAK,aAAa,eAAeA,EAAMD,CAAO,CAChD,CAEA,qBAA4B,EAAoB,EAA4B,CAC1E,OAAO,KAAK,aAAa,WAAWC,EAAMD,CAAO,CACnD,CAEA,wBAA+B,EAA6B,CAC1D,OAAO,KAAK,aAAa,cAAcC,CAAI,CAC7C,CAEA,yBAAgC,EAAoB,EAAwB,CAC1E,KAAK,aAAa,eAAeA,EAAMkF,CAAO,CAChD,CAEA,qBAA4B,EAA6B,CACvD,OAAO,KAAK,aAAa,WAAWlF,CAAI,CAC1C,CAEA,0BAAiC,EAAoB,EAAwB,CAC3E,KAAK,aAAa,gBAAgBA,EAAMoF,CAAQ,CAClD,CAEA,0BAAiC,EAA4B,CAC3D,OAAO,KAAK,aAAa,gBAAgBpF,CAAI,CAC/C,CAEA,2BAAkC,EAAwB,CACxD,KAAK,aAAa,2BAA2BL,CAAO,CACtD,CAEA,4BAA6C,CAC3C,OAAO,KAAK,aAAa,2BAA2B,CACtD,CAEA,qBAA4B,EAAwB,CAClD,KAAK,aAAa,qBAAqBuF,CAAO,CAChD,CAEA,sBAAuC,CACrC,OAAO,KAAK,aAAa,qBAAqB,CAChD,CAEA,gBAAuB,EAA2B,CAChD,KAAK,aAAa,gBAAgBlF,CAAI,CACxC,CAEA,oBAA4B,CAC1B,OAAO,KAAK,aAAa,mBAAmB,CAC9C,CAEA,iBAA+B,CAC7B,KAAK,gBAAgB,QAAQ,MAAM,EACnC,KAAK,aAAa,gBAAgB,CACpC,CAEA,kBAAgC,CAC9B,KAAK,aAAa,iBAAiB,CACrC,CAEA,iBAA+B,CAC7B,KAAK,aAAa,iBAAiB,CACrC,CAEA,kBAAmC,CACjC,OAAO,KAAK,aAAa,iBAAiB,CAC5C,CAEA,eAAgC,CAC9B,OAAO,KAAK,aAAa,eAAe,CAC1C,CAEA,eAA6B,CAC3B,KAAK,aAAa,SAAS,CAC7B,CAMA,mBAA0B,EAA6B,CACjDsI,IAAS,OAIX,KAAK,aAAa,eAAe,EAAI,GAHrC,KAAK,aAAa,iBAAiB,EACnC,KAAK,aAAa,eAAe,EAAK,GAIxC,KAAK,gBAAgB,QAAQA,CAAI,CACnC,CAEA,oBAA6C,CAC3C,OAAO,KAAK,gBAAgB,QAAQ,CACtC,CAEA,mBAAiC,CAC/B,KAAK,gBAAgB,SAAS,CAChC,CAEA,iBAA+B,CAC7B,KAAK,gBAAgB,cAAc,CACrC,CAEA,iBAA8C,CAC5C,OAAO,KAAK,gBAAgB,WAAW,CACzC,CAMA,MAAa,QAAQ,EAAkB,EAAmB,IAAoB,CACvE,KAAK,eACV,MAAM,KAAK,WAAW,QAAQjH,EAAM,KAAK,aAAcE,CAAQ,EAC/D,KAAK,KAAK,cAAeF,CAAI,EAC/B,CAEA,MAAa,QAAQ,EAAmB,IAAoB,CACrD,KAAK,cACV,MAAM,KAAK,WAAW,YAAY,KAAK,aAAcE,CAAQ,CAC/D,CAMA,cACE,EACA,EACA,EACA,EACM,CACN,KAAK,WAAW,cAAcxB,EAASC,EAAMC,EAAWC,CAAK,EAC7D,KAAK,KAAK,oBAAqB,KAAK,WAAW,mBAAmB,CAAC,CACrE,CAEA,kBAAmC,CACjC,IAAMkO,EAAQ,KAAK,WAAW,iBAAiB,EAE/C,OADA,KAAK,KAAK,oBAAqB,KAAK,WAAW,mBAAmB,CAAC,EAC5DA,CACT,CAEA,kBAAyB,EAA4B,CACnD,KAAK,WAAW,kBAAkBpO,CAAI,EACtC,KAAK,KAAK,oBAAqB,KAAK,WAAW,mBAAmB,CAAC,CACrE,CAEA,uBAA8B,EAAsC,CAClE,KAAK,WAAW,uBAAuBC,CAAS,EAChD,KAAK,KAAK,oBAAqB,KAAK,WAAW,mBAAmB,CAAC,CACrE,CAEA,mBAA0B,EAAqB,CAC7C,KAAK,WAAW,mBAAmBC,CAAK,EACxC,KAAK,KAAK,oBAAqB,KAAK,WAAW,mBAAmB,CAAC,CACrE,CAEA,oBAA+C,CAC7C,OAAO,KAAK,WAAW,mBAAmB,CAC5C,CAEA,sBAA6B,EAA8B,CACzD,KAAK,WAAW,cAAcF,CAAI,EAClC,KAAK,KAAK,kBAAmBA,CAAI,CACnC,CAEA,uBAAiD,CAC/C,OAAO,KAAK,WAAW,cAAc,CACvC,CAMA,MAAa,YAAY,EAA8B,CAAC,EAAoB,CAC1E,OAAO,KAAK,eAAe,YAAYxL,CAAO,CAChD,CAIA,GAAmB,EAAe,EAA8C,CAC9E,OAAO,MAAM,GAAGc,EAAOC,CAAwC,CACjE,CAGA,IAAoB,EAAe,EAA8C,CAC/E,OAAO,MAAM,IAAID,EAAOC,CAAwC,CAClE,CASA,eAAiC,CAC/B,OAAO,KAAK,eAAe,KAAK,EAAM,IAAU8Y,EAAK,MAAQ,aAAa5Y,EAAQ,GAAG,CACvF,CAKA,qBAA4C,CAC1C,OAAO,KAAK,oBACd,CAKA,cAAqB,EAA+B,EAAS,CAC3D,GAAI,CAAC,KAAK,gBAAkB,KAAK,eAAe,SAAW,EAAG,OAE9D,IAAI6Y,EAaJ,GAZA,AAGEA,EAHE,OAAOC,GAAgB,SACZ,KAAK,eAAeA,GAEpB,KAAK,eAAe,MAC9B,EAAG,IAAQhC,EAAE,OAASgC,GAAe,aAAaxP,EAAM,MAAQwP,CACnE,EAGF,AACED,IAAa,KAAK,eAAe,GAG/B,CAACA,EAAY,OAEjB,IAAME,EAAY,KAAK,eAAe,WAAWF,CAAU,EAEvD,KAAK,cAAgB,KAAK,eAAiBE,GAC7C,KAAK,aAAa,QAAQ,EAAG,EAC7BA,EAAU,MAAM,CAAC,CAAC,OAAO,EAAG,CAAC,CAAC,KAAK,GAEnCA,EAAU,KAAK,EAGjB,KAAK,aAAeA,EACpB,KAAK,qBAAuBF,EAAW,MAAQ,cAC/C,KAAK,kBAAoB,GACzB,KAAK,eAAe,SAAS,EAE7B,KAAK,KAAK,iBAAkB,KAAK,oBAAoB,CACvD,CAKA,gBAA8B,CAC5B,KAAK,kBAAoB,GACzB,KAAK,KAAK,iBAAiB,CAC7B,CAKA,iBAA+B,CACzB,KAAK,gBAAkB,KAAK,eAC9B,KAAK,kBAAoB,GACzB,KAAK,eAAe,SAAS,EAC7B,KAAK,KAAK,iBAAkB,KAAK,sBAAwB,EAAE,EAE/D,CAKA,eAA6B,CAC3B,KAAK,kBAAoB,GACrB,KAAK,cACP,KAAK,aAAa,KAAK,EAEzB,KAAK,KAAK,gBAAgB,CAC5B,CAKA,oBAAqC,CACnC,OAAO,KAAK,iBACd,CAKA,kBAAyB,EAAqB,CACxC,KAAK,iBACP,KAAK,eAAe,UAAY,KAAK,IAAI,EAAGpO,CAAK,EACjD,KAAK,KAAK,kBAAmBA,CAAK,EAEtC,CAMA,SAAuB,CACrB,KAAK,WAAW,EAChB,KAAK,YAAY,QAAQ,EACzB,KAAK,gBAAgB,QAAQ,EAC7B,KAAK,aAAa,QAAQ,EAC1B,KAAK,aAAa,QAAQ,EAC1B,KAAK,mBAAmB,CAC1B,CACF,E"}