@tonybfox/threejs-tools 1.0.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.
Files changed (80) hide show
  1. package/README.md +321 -0
  2. package/dist/asset-loader/index.cjs +376 -0
  3. package/dist/asset-loader/index.cjs.map +1 -0
  4. package/dist/asset-loader/index.d.mts +101 -0
  5. package/dist/asset-loader/index.d.ts +101 -0
  6. package/dist/asset-loader/index.mjs +7 -0
  7. package/dist/asset-loader/index.mjs.map +1 -0
  8. package/dist/camera/index.cjs +313 -0
  9. package/dist/camera/index.cjs.map +1 -0
  10. package/dist/camera/index.d.mts +82 -0
  11. package/dist/camera/index.d.ts +82 -0
  12. package/dist/camera/index.mjs +7 -0
  13. package/dist/camera/index.mjs.map +1 -0
  14. package/dist/chunk-5DP6WDB3.mjs +1161 -0
  15. package/dist/chunk-5DP6WDB3.mjs.map +1 -0
  16. package/dist/chunk-BJKSICFA.mjs +1579 -0
  17. package/dist/chunk-BJKSICFA.mjs.map +1 -0
  18. package/dist/chunk-BYRZCHE7.mjs +277 -0
  19. package/dist/chunk-BYRZCHE7.mjs.map +1 -0
  20. package/dist/chunk-EIROAPF7.mjs +387 -0
  21. package/dist/chunk-EIROAPF7.mjs.map +1 -0
  22. package/dist/chunk-EQDOX34V.mjs +164 -0
  23. package/dist/chunk-EQDOX34V.mjs.map +1 -0
  24. package/dist/chunk-IIAZ2WJJ.mjs +405 -0
  25. package/dist/chunk-IIAZ2WJJ.mjs.map +1 -0
  26. package/dist/chunk-L4VIIJZD.mjs +340 -0
  27. package/dist/chunk-L4VIIJZD.mjs.map +1 -0
  28. package/dist/chunk-P35QJCOG.mjs +339 -0
  29. package/dist/chunk-P35QJCOG.mjs.map +1 -0
  30. package/dist/chunk-R64RVBRM.mjs +394 -0
  31. package/dist/chunk-R64RVBRM.mjs.map +1 -0
  32. package/dist/compass/index.cjs +375 -0
  33. package/dist/compass/index.cjs.map +1 -0
  34. package/dist/compass/index.d.mts +58 -0
  35. package/dist/compass/index.d.ts +58 -0
  36. package/dist/compass/index.mjs +7 -0
  37. package/dist/compass/index.mjs.map +1 -0
  38. package/dist/grid/index.cjs +200 -0
  39. package/dist/grid/index.cjs.map +1 -0
  40. package/dist/grid/index.d.mts +43 -0
  41. package/dist/grid/index.d.ts +43 -0
  42. package/dist/grid/index.mjs +7 -0
  43. package/dist/grid/index.mjs.map +1 -0
  44. package/dist/index.cjs +5049 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.mts +13 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.mjs +47 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/dist/measurements/index.cjs +1198 -0
  51. package/dist/measurements/index.cjs.map +1 -0
  52. package/dist/measurements/index.d.mts +449 -0
  53. package/dist/measurements/index.d.ts +449 -0
  54. package/dist/measurements/index.mjs +9 -0
  55. package/dist/measurements/index.mjs.map +1 -0
  56. package/dist/sunlight/index.cjs +441 -0
  57. package/dist/sunlight/index.cjs.map +1 -0
  58. package/dist/sunlight/index.d.mts +92 -0
  59. package/dist/sunlight/index.d.ts +92 -0
  60. package/dist/sunlight/index.mjs +7 -0
  61. package/dist/sunlight/index.mjs.map +1 -0
  62. package/dist/terrain/index.cjs +423 -0
  63. package/dist/terrain/index.cjs.map +1 -0
  64. package/dist/terrain/index.d.mts +219 -0
  65. package/dist/terrain/index.d.ts +219 -0
  66. package/dist/terrain/index.mjs +7 -0
  67. package/dist/terrain/index.mjs.map +1 -0
  68. package/dist/transform-controls/index.cjs +1587 -0
  69. package/dist/transform-controls/index.cjs.map +1 -0
  70. package/dist/transform-controls/index.d.mts +162 -0
  71. package/dist/transform-controls/index.d.ts +162 -0
  72. package/dist/transform-controls/index.mjs +13 -0
  73. package/dist/transform-controls/index.mjs.map +1 -0
  74. package/dist/view-helper/index.cjs +430 -0
  75. package/dist/view-helper/index.cjs.map +1 -0
  76. package/dist/view-helper/index.d.mts +75 -0
  77. package/dist/view-helper/index.d.ts +75 -0
  78. package/dist/view-helper/index.mjs +7 -0
  79. package/dist/view-helper/index.mjs.map +1 -0
  80. package/package.json +124 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../packages/measurements/src/MeasurementTool.ts","../packages/measurements/src/MeasurementTypes.ts"],"sourcesContent":["import * as THREE from 'three'\nimport { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'\nimport {\n Measurement,\n MeasurementPoint,\n MeasurementData,\n MeasurementPointData,\n MeasurementToolOptions,\n MeasurementToolEvents,\n MeasurementCreationOptions,\n MeasurementOptions,\n SnapMode,\n SnapResult,\n} from './MeasurementTypes'\n\n/**\n * Main measurement tool class for Three.js scenes\n * Provides both programmatic and interactive measurement creation\n *\n * **Important**: This tool uses CSS2DObject for labels, which requires\n * CSS2DRenderer to be set up and rendered alongside your main WebGL renderer.\n *\n * @example\n * ```javascript\n * import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js'\n *\n * // Set up CSS2DRenderer\n * const css2dRenderer = new CSS2DRenderer()\n * css2dRenderer.setSize(window.innerWidth, window.innerHeight)\n * css2dRenderer.domElement.style.position = 'absolute'\n * css2dRenderer.domElement.style.top = '0'\n * css2dRenderer.domElement.style.pointerEvents = 'none'\n * document.body.appendChild(css2dRenderer.domElement)\n *\n * // In your animation loop:\n * function animate() {\n * renderer.render(scene, camera) // WebGL rendering\n * css2dRenderer.render(scene, camera) // CSS2D label rendering\n * }\n * ```\n */\nexport class MeasurementTool extends THREE.EventDispatcher<MeasurementToolEvents> {\n private scene: THREE.Scene\n private camera: THREE.Camera\n private measurements: Measurement[] = []\n private raycaster: THREE.Raycaster = new THREE.Raycaster()\n\n // Interactive mode properties\n private isInteractive: boolean = false\n private domElement: HTMLElement | null = null\n private controls: { enabled: boolean } | null = null\n private defaultTargets: THREE.Object3D[] = []\n private activeTargets: THREE.Object3D[] = []\n private currentMeasurement: Partial<Measurement> | null = null\n private activeInteractionOptions: MeasurementOptions | null = null\n private pendingMeasurementOptions: MeasurementOptions | null = null\n private previewLine: THREE.Line | null = null\n private previewLabel: CSS2DObject | null = null\n private snapMarker: THREE.Sprite | null = null\n private originalCursor: string = ''\n private cursorHidden: boolean = false\n\n // Configuration defaults\n private defaultOptions: MeasurementOptions = {\n lineColor: 0xff0000,\n labelColor: '#ffffff',\n lineWidth: 2,\n fontSize: 16,\n fontFamily: 'Arial, sans-serif',\n snapMode: SnapMode.VERTEX,\n snapEnabled: true,\n snapDistance: 0.05,\n targets: [],\n isDynamic: false,\n }\n private previewColor: number = 0x00ffff\n private markerColor: number = 0x00ff00\n private markerSize: number = 0.08\n private markerVisible: boolean = true\n\n // Edit mode properties\n private isEditMode: boolean = false\n private editingMeasurement: Measurement | null = null\n private editingPoint: 'start' | 'end' | null = null\n private startEditSprite: THREE.Sprite | null = null\n private endEditSprite: THREE.Sprite | null = null\n private editSpriteMaterial: THREE.SpriteMaterial | null = null\n private isDragging: boolean = false\n\n // Materials\n private previewMaterial: THREE.LineDashedMaterial\n private markerMaterial: THREE.SpriteMaterial\n\n /**\n * Create a crosshair texture for the snap marker sprite\n */\n private createCrosshairTexture(): THREE.Texture {\n const size = 64\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n\n const context = canvas.getContext('2d')!\n const centerX = size / 2\n const centerY = size / 2\n const lineLength = 20\n const gap = 6\n\n // Clear canvas\n context.clearRect(0, 0, size, size)\n\n // Set line style\n context.strokeStyle = '#ffffff'\n context.lineWidth = 3\n context.lineCap = 'round'\n\n // Draw crosshair lines\n context.beginPath()\n\n // Horizontal line (left)\n context.moveTo(centerX - lineLength, centerY)\n context.lineTo(centerX - gap, centerY)\n\n // Horizontal line (right)\n context.moveTo(centerX + gap, centerY)\n context.lineTo(centerX + lineLength, centerY)\n\n // Vertical line (top)\n context.moveTo(centerX, centerY - lineLength)\n context.lineTo(centerX, centerY - gap)\n\n // Vertical line (bottom)\n context.moveTo(centerX, centerY + gap)\n context.lineTo(centerX, centerY + lineLength)\n\n context.stroke()\n\n // Add black outline for better visibility\n context.strokeStyle = '#000000'\n context.lineWidth = 5\n context.globalCompositeOperation = 'destination-over'\n\n context.beginPath()\n\n // Horizontal line (left)\n context.moveTo(centerX - lineLength, centerY)\n context.lineTo(centerX - gap, centerY)\n\n // Horizontal line (right)\n context.moveTo(centerX + gap, centerY)\n context.lineTo(centerX + lineLength, centerY)\n\n // Vertical line (top)\n context.moveTo(centerX, centerY - lineLength)\n context.lineTo(centerX, centerY - gap)\n\n // Vertical line (bottom)\n context.moveTo(centerX, centerY + gap)\n context.lineTo(centerX, centerY + lineLength)\n\n context.stroke()\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.needsUpdate = true\n return texture\n }\n\n /**\n * Create a dot texture for edit point sprites\n */\n private createDotTexture(): THREE.Texture {\n const size = 64\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n\n const context = canvas.getContext('2d')!\n const centerX = size / 2\n const centerY = size / 2\n const radius = 12\n\n // Clear canvas\n context.clearRect(0, 0, size, size)\n\n // Draw white circle\n context.fillStyle = '#ffffff'\n context.beginPath()\n context.arc(centerX, centerY, radius, 0, Math.PI * 2)\n context.fill()\n\n // Add black outline\n context.strokeStyle = '#000000'\n context.lineWidth = 3\n context.beginPath()\n context.arc(centerX, centerY, radius, 0, Math.PI * 2)\n context.stroke()\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.needsUpdate = true\n return texture\n }\n\n constructor(\n scene: THREE.Scene,\n camera: THREE.Camera,\n options: MeasurementToolOptions = {}\n ) {\n super()\n\n this.scene = scene\n this.camera = camera\n\n // Apply options (excluding domElement and controls which need special handling)\n const { domElement, controls } = options\n\n // Set DOM element if provided\n if (domElement) {\n this.domElement = domElement\n }\n\n // Set controls if provided\n if (controls) {\n this.controls = controls\n }\n\n this.previewMaterial = new THREE.LineDashedMaterial({\n color: this.previewColor,\n linewidth: this.defaultOptions.lineWidth,\n dashSize: 0.1,\n gapSize: 0.05,\n })\n\n this.markerMaterial = new THREE.SpriteMaterial({\n map: this.createCrosshairTexture(),\n color: this.markerColor,\n transparent: true,\n opacity: 0.8,\n sizeAttenuation: false, // Keep constant size regardless of distance\n depthTest: false, // Always render in front of other objects\n })\n\n this.editSpriteMaterial = new THREE.SpriteMaterial({\n map: this.createDotTexture(),\n color: 0xffaa00, // Orange color for edit points\n transparent: true,\n opacity: 0.9,\n sizeAttenuation: false,\n depthTest: false,\n })\n\n // Set up raycaster\n this.raycaster.params.Line.threshold = 0.01\n this.raycaster.params.Points.threshold = 0.01\n }\n\n /**\n * Create a measurement point that optionally tracks a scene object.\n */\n private createMeasurementPoint(\n worldPosition: THREE.Vector3,\n object?: THREE.Object3D,\n localPosition?: THREE.Vector3\n ): MeasurementPoint {\n if (!object) {\n return { position: worldPosition.clone() }\n }\n\n const anchorLocal =\n localPosition?.clone() ?? object.worldToLocal(worldPosition.clone())\n const resolvedWorld = object.localToWorld(anchorLocal.clone())\n\n return {\n position: resolvedWorld,\n anchor: {\n object,\n localPosition: anchorLocal,\n },\n }\n }\n\n /**\n * Update a measurement point's world position from its anchor (if dynamic)\n */\n private updateMeasurementPoint(point: MeasurementPoint): boolean {\n if (!point.anchor) {\n return false\n }\n\n const newWorldPosition = point.anchor.localPosition.clone()\n point.anchor.object.localToWorld(newWorldPosition)\n\n if (!point.position.equals(newWorldPosition)) {\n point.position.copy(newWorldPosition)\n return true\n }\n\n return false\n }\n\n /**\n * Add a measurement between two world positions with optional attachments.\n *\n * @param start - Starting world position\n * @param end - Ending world position\n * @param options - Optional configuration for the measurement\n */\n addMeasurement(\n start: THREE.Vector3,\n end: THREE.Vector3,\n options: MeasurementCreationOptions = {}\n ): Measurement {\n const hasAnchors = Boolean(options.startObject || options.endObject)\n const resolvedOptions = this.resolveMeasurementOptions(\n options,\n options.isDynamic ?? hasAnchors\n )\n\n const startPoint = this.createMeasurementPoint(\n start,\n options.startObject,\n options.startLocalPosition\n )\n const endPoint = this.createMeasurementPoint(\n end,\n options.endObject,\n options.endLocalPosition\n )\n\n return this.addMeasurementFromPoints(startPoint, endPoint, resolvedOptions, {\n id: options.id,\n })\n }\n\n private resolveMeasurementOptions(\n overrides: MeasurementCreationOptions = {},\n inferredDynamic?: boolean\n ): MeasurementOptions {\n let targetCandidates: THREE.Object3D[] | undefined\n\n if (overrides.targets && overrides.targets.length > 0) {\n targetCandidates = overrides.targets\n } else if (this.defaultOptions.targets.length > 0) {\n targetCandidates = this.defaultOptions.targets\n } else if (this.defaultTargets.length > 0) {\n targetCandidates = this.defaultTargets\n }\n\n const targets =\n targetCandidates && targetCandidates.length > 0\n ? targetCandidates\n : this.getAllMeshes()\n\n const isDynamic =\n overrides.isDynamic ??\n inferredDynamic ??\n this.defaultOptions.isDynamic\n\n return {\n lineColor: overrides.lineColor ?? this.defaultOptions.lineColor,\n labelColor: overrides.labelColor ?? this.defaultOptions.labelColor,\n lineWidth: overrides.lineWidth ?? this.defaultOptions.lineWidth,\n fontSize: overrides.fontSize ?? this.defaultOptions.fontSize,\n fontFamily: overrides.fontFamily ?? this.defaultOptions.fontFamily,\n snapMode: overrides.snapMode ?? this.defaultOptions.snapMode,\n snapEnabled: overrides.snapEnabled ?? this.defaultOptions.snapEnabled,\n snapDistance: overrides.snapDistance ?? this.defaultOptions.snapDistance,\n targets,\n isDynamic,\n }\n }\n\n private getActiveMeasurementOptions(): MeasurementOptions | null {\n if (this.isEditMode && this.editingMeasurement) {\n return this.editingMeasurement.options\n }\n\n return this.activeInteractionOptions\n }\n\n /**\n * @deprecated Use addMeasurement(obj1, obj2, { startLocalPos, endLocalPos }) instead\n * Add a dynamic measurement between two objects\n */\n addDynamicMeasurement(\n startObject: THREE.Object3D,\n endObject: THREE.Object3D,\n startLocalPos: THREE.Vector3 = new THREE.Vector3(),\n endLocalPos: THREE.Vector3 = new THREE.Vector3()\n ): Measurement {\n const startWorld = startObject.localToWorld(startLocalPos.clone())\n const endWorld = endObject.localToWorld(endLocalPos.clone())\n\n return this.addMeasurement(startWorld, endWorld, {\n startObject,\n endObject,\n startLocalPosition: startLocalPos,\n endLocalPosition: endLocalPos,\n })\n }\n\n /**\n * @deprecated Use addMeasurement(staticPos, targetObject, { endLocalPos }) instead\n * Add a measurement from a static point to a dynamic object\n */\n addMeasurementToObject(\n staticPos: THREE.Vector3,\n targetObject: THREE.Object3D,\n objectLocalPos: THREE.Vector3 = new THREE.Vector3()\n ): Measurement {\n const endWorld = targetObject.localToWorld(objectLocalPos.clone())\n\n return this.addMeasurement(staticPos, endWorld, {\n endObject: targetObject,\n endLocalPosition: objectLocalPos,\n })\n }\n\n /**\n * Core method to add a measurement from two measurement points\n */\n private addMeasurementFromPoints(\n start: MeasurementPoint,\n end: MeasurementPoint,\n options: MeasurementOptions,\n context?: { id?: string }\n ): Measurement {\n const id = context?.id || this.generateId()\n const distance = start.position.distanceTo(end.position)\n\n // Create line geometry\n const geometry = new THREE.BufferGeometry().setFromPoints([\n start.position,\n end.position,\n ])\n const line = new THREE.Line(\n geometry,\n new THREE.LineBasicMaterial({\n color: options.lineColor,\n linewidth: options.lineWidth,\n })\n )\n\n // Create label\n const label = this.createLabel(distance, options)\n const midpoint = start.position\n .clone()\n .add(end.position)\n .multiplyScalar(0.5)\n label.position.copy(midpoint)\n\n const measurement: Measurement = {\n id,\n start,\n end,\n line,\n label,\n distance,\n options: {\n ...options,\n targets: [...options.targets],\n },\n }\n\n // Add to scene and tracking\n this.scene.add(line)\n this.scene.add(label)\n this.measurements.push(measurement)\n\n // Dispatch event\n this.dispatchEvent({\n type: 'measurementCreated',\n measurement,\n })\n\n return measurement\n }\n\n /**\n * Update all dynamic measurements in real-time\n * Call this in your animation loop to keep dynamic measurements up-to-date\n */\n updateDynamicMeasurements(): boolean {\n let updated = false\n\n for (const measurement of this.measurements) {\n if (!measurement.options.isDynamic) continue\n\n let needsUpdate = false\n\n // Update start point\n if (this.updateMeasurementPoint(measurement.start)) {\n needsUpdate = true\n }\n\n // Update end point\n if (this.updateMeasurementPoint(measurement.end)) {\n needsUpdate = true\n }\n\n if (needsUpdate) {\n // Recalculate distance\n const newDistance = measurement.start.position.distanceTo(\n measurement.end.position\n )\n measurement.distance = newDistance\n\n // Update line geometry\n const positions = [measurement.start.position, measurement.end.position]\n measurement.line.geometry.setFromPoints(positions)\n measurement.line.geometry.attributes.position.needsUpdate = true\n\n // Update label position and text\n const midpoint = measurement.start.position\n .clone()\n .add(measurement.end.position)\n .multiplyScalar(0.5)\n measurement.label.position.copy(midpoint)\n this.updateLabelText(measurement.label.element, newDistance)\n\n updated = true\n }\n }\n\n return updated\n }\n\n /**\n * Set whether interactive measurements should be dynamic or static\n */\n setDynamicMode(enabled: boolean): void {\n this.setDefaultMeasurementOptions({ isDynamic: enabled })\n }\n\n /**\n * Get the current dynamic mode state\n */\n getDynamicMode(): boolean {\n return this.defaultOptions.isDynamic\n }\n\n /**\n * Enter edit mode for a specific measurement\n * Shows edit sprites at the measurement endpoints\n * @param measurementIdOrIndex - The measurement ID or index\n * @param targets - Optional target objects for snapping during edit\n */\n enterEditMode(\n measurementIdOrIndex: string | number,\n targets?: THREE.Object3D[]\n ): void {\n // Exit any current edit mode\n this.exitEditMode()\n\n // Find the measurement\n let measurement: Measurement | undefined\n if (typeof measurementIdOrIndex === 'string') {\n measurement = this.measurements.find((m) => m.id === measurementIdOrIndex)\n } else {\n measurement = this.measurements[measurementIdOrIndex]\n }\n\n if (!measurement) {\n console.warn('Measurement not found:', measurementIdOrIndex)\n return\n }\n\n if (!this.domElement) {\n console.warn(\n 'DOM element not set. Call setDomElement() or enableInteraction() first.'\n )\n return\n }\n\n this.isEditMode = true\n this.editingMeasurement = measurement\n\n // Select targets for editing\n const resolvedTargets =\n targets && targets.length > 0\n ? targets\n : measurement.options.targets.length > 0\n ? measurement.options.targets\n : this.getAllMeshes()\n\n this.activeTargets = resolvedTargets\n\n // Persist overrides so future edits reuse them\n if (targets && targets.length > 0) {\n measurement.options.targets = [...targets]\n }\n\n // Create edit sprites at the endpoints\n this.createEditSprites()\n\n // Set up event listeners for dragging\n this.domElement.addEventListener('mousedown', this.onEditMouseDown)\n this.domElement.addEventListener('mousemove', this.onEditMouseMove)\n this.domElement.addEventListener('mouseup', this.onEditMouseUp)\n this.domElement.style.cursor = 'pointer'\n\n this.dispatchEvent({\n type: 'editModeEntered',\n measurement,\n })\n }\n\n /**\n * Exit edit mode\n */\n exitEditMode(): void {\n if (!this.isEditMode) return\n\n const measurement = this.editingMeasurement\n\n // Remove edit sprites\n this.removeEditSprites()\n\n // Remove event listeners\n if (this.domElement) {\n this.domElement.removeEventListener('mousedown', this.onEditMouseDown)\n this.domElement.removeEventListener('mousemove', this.onEditMouseMove)\n this.domElement.removeEventListener('mouseup', this.onEditMouseUp)\n this.domElement.style.cursor = this.isInteractive\n ? 'crosshair'\n : 'default'\n }\n\n this.isEditMode = false\n this.editingMeasurement = null\n this.editingPoint = null\n this.isDragging = false\n this.activeTargets = []\n\n if (measurement) {\n this.dispatchEvent({\n type: 'editModeExited',\n measurement,\n })\n }\n }\n\n /**\n * Set the DOM element for interactions (both measurement and edit mode)\n */\n setDomElement(domElement: HTMLElement): void {\n this.domElement = domElement\n }\n\n /**\n * Set the camera controls to disable during edit dragging\n */\n setControls(controls: { enabled: boolean }): void {\n this.controls = controls\n }\n\n /**\n * Set target objects for snapping (used in both interactive mode and edit mode)\n */\n setTargetObjects(targets: THREE.Object3D[]): void {\n this.defaultTargets = targets.length > 0 ? targets : this.getAllMeshes()\n this.defaultOptions.targets = [...this.defaultTargets]\n }\n\n /**\n * Update default measurement options used when none are provided explicitly.\n */\n setDefaultMeasurementOptions(\n options: Partial<MeasurementCreationOptions>\n ): void {\n if (options.lineColor !== undefined) {\n this.defaultOptions.lineColor = options.lineColor\n }\n if (options.labelColor !== undefined) {\n this.defaultOptions.labelColor = options.labelColor\n }\n if (options.lineWidth !== undefined) {\n this.defaultOptions.lineWidth = options.lineWidth\n }\n if (options.fontSize !== undefined) {\n this.defaultOptions.fontSize = options.fontSize\n }\n if (options.fontFamily !== undefined) {\n this.defaultOptions.fontFamily = options.fontFamily\n }\n if (options.snapMode !== undefined) {\n this.defaultOptions.snapMode = options.snapMode\n }\n if (options.snapEnabled !== undefined) {\n this.defaultOptions.snapEnabled = options.snapEnabled\n }\n if (options.snapDistance !== undefined) {\n this.defaultOptions.snapDistance = options.snapDistance\n }\n if (options.targets !== undefined) {\n this.defaultTargets = options.targets\n this.defaultOptions.targets = [...options.targets]\n if (this.activeInteractionOptions) {\n this.activeInteractionOptions.targets = [...options.targets]\n this.activeTargets = options.targets\n }\n }\n if (options.isDynamic !== undefined) {\n this.defaultOptions.isDynamic = options.isDynamic\n if (this.activeInteractionOptions) {\n this.activeInteractionOptions.isDynamic = options.isDynamic\n }\n }\n\n if (this.activeInteractionOptions) {\n if (options.lineColor !== undefined) {\n this.activeInteractionOptions.lineColor = options.lineColor\n }\n if (options.labelColor !== undefined) {\n this.activeInteractionOptions.labelColor = options.labelColor\n }\n if (options.lineWidth !== undefined) {\n this.activeInteractionOptions.lineWidth = options.lineWidth\n }\n if (options.fontSize !== undefined) {\n this.activeInteractionOptions.fontSize = options.fontSize\n }\n if (options.fontFamily !== undefined) {\n this.activeInteractionOptions.fontFamily = options.fontFamily\n }\n if (options.snapMode !== undefined) {\n this.activeInteractionOptions.snapMode = options.snapMode\n }\n if (options.snapEnabled !== undefined) {\n this.activeInteractionOptions.snapEnabled = options.snapEnabled\n }\n if (options.snapDistance !== undefined) {\n this.activeInteractionOptions.snapDistance = options.snapDistance\n }\n }\n }\n\n /**\n * Disable camera controls (used during edit dragging)\n */\n private disableControls(): void {\n if (this.controls) {\n this.controls.enabled = false\n }\n }\n\n /**\n * Enable camera controls (used after edit dragging)\n */\n private enableControls(): void {\n if (this.controls) {\n this.controls.enabled = true\n }\n }\n\n /**\n * Enable interactive measurement mode\n */\n enableInteraction(options: MeasurementCreationOptions = {}): void {\n if (this.isInteractive) {\n this.disableInteraction()\n }\n\n const resolvedOptions = this.resolveMeasurementOptions(options)\n this.activeInteractionOptions = {\n ...resolvedOptions,\n targets: [...resolvedOptions.targets],\n }\n this.activeTargets =\n this.activeInteractionOptions.targets.length > 0\n ? this.activeInteractionOptions.targets\n : this.getAllMeshes()\n\n this.isInteractive = true\n\n if (this.domElement) {\n // Add event listeners\n this.domElement.addEventListener('click', this.onMouseClick)\n this.domElement.addEventListener('mousemove', this.onMouseMove)\n this.domElement.addEventListener('keydown', this.onKeyDown)\n\n // Set cursor style\n this.domElement.style.cursor = 'crosshair'\n }\n // Create snap marker\n this.createSnapMarker()\n\n this.dispatchEvent({ type: 'started' })\n }\n\n /**\n * Disable interactive measurement mode\n */\n disableInteraction(): void {\n if (!this.isInteractive || !this.domElement) return\n\n // Exit edit mode if active\n this.exitEditMode()\n\n // Remove event listeners\n this.domElement.removeEventListener('click', this.onMouseClick)\n this.domElement.removeEventListener('mousemove', this.onMouseMove)\n this.domElement.removeEventListener('keydown', this.onKeyDown)\n\n // Reset cursor\n this.showCursor()\n this.domElement.style.cursor = 'default'\n\n // Clean up current measurement\n this.cancelCurrentMeasurement()\n\n // Remove snap marker\n this.removeSnapMarker()\n\n this.isInteractive = false\n this.activeInteractionOptions = null\n this.activeTargets = []\n\n this.dispatchEvent({ type: 'ended' })\n }\n\n /**\n * Remove the last measurement (undo)\n */\n undoLast(): void {\n if (this.measurements.length === 0) return\n\n const lastMeasurement = this.measurements.pop()!\n this.removeMeasurementFromScene(lastMeasurement)\n\n this.dispatchEvent({\n type: 'measurementRemoved',\n measurement: lastMeasurement,\n })\n }\n\n /**\n * Remove a specific measurement\n */\n removeMeasurement(measurement: Measurement): void {\n const index = this.measurements.indexOf(measurement)\n if (index === -1) return\n\n this.measurements.splice(index, 1)\n this.removeMeasurementFromScene(measurement)\n\n this.dispatchEvent({\n type: 'measurementRemoved',\n measurement,\n })\n }\n\n /**\n * Clear all measurements\n */\n clearAll(): void {\n const count = this.measurements.length\n\n this.measurements.forEach((measurement) => {\n this.removeMeasurementFromScene(measurement)\n })\n\n this.measurements = []\n\n this.dispatchEvent({\n type: 'measurementsCleared',\n count,\n })\n }\n\n /**\n * Get all measurements\n */\n getMeasurements(): Measurement[] {\n return [...this.measurements]\n }\n\n /**\n * Convert a MeasurementPoint to serializable data\n */\n private serializeMeasurementPoint(\n point: MeasurementPoint\n ): MeasurementPointData {\n return {\n position: point.position.toArray() as [number, number, number],\n anchorObjectId: point.anchor?.object.uuid,\n anchorLocalPosition: point.anchor\n ? (point.anchor.localPosition.toArray() as [number, number, number])\n : undefined,\n }\n }\n\n /**\n * Serialize measurements to JSON-compatible format\n * Note: Dynamic measurements will lose their object references and become static when deserialized\n */\n serialize(): MeasurementData[] {\n return this.measurements.map((measurement) => ({\n id: measurement.id,\n start: this.serializeMeasurementPoint(measurement.start),\n end: this.serializeMeasurementPoint(measurement.end),\n distance: measurement.distance,\n options: {\n snapMode: measurement.options.snapMode,\n snapEnabled: measurement.options.snapEnabled,\n snapDistance: measurement.options.snapDistance,\n lineColor: measurement.options.lineColor,\n labelColor: measurement.options.labelColor,\n lineWidth: measurement.options.lineWidth,\n fontSize: measurement.options.fontSize,\n fontFamily: measurement.options.fontFamily,\n isDynamic: measurement.options.isDynamic,\n targetObjectIds: measurement.options.targets.map((obj) => obj.uuid),\n },\n }))\n }\n\n /**\n * Deserialize measurements from JSON data\n * Note: Dynamic measurements become static since object references are lost\n */\n deserialize(data: MeasurementData[]): void {\n // Clear existing measurements\n this.clearAll()\n\n data.forEach((item) => {\n const start = new THREE.Vector3().fromArray(item.start.position)\n const end = new THREE.Vector3().fromArray(item.end.position)\n\n const startObject = item.start.anchorObjectId\n ? (this.scene.getObjectByProperty(\n 'uuid',\n item.start.anchorObjectId\n ) as THREE.Object3D | null)\n : null\n const endObject = item.end.anchorObjectId\n ? (this.scene.getObjectByProperty(\n 'uuid',\n item.end.anchorObjectId\n ) as THREE.Object3D | null)\n : null\n\n const restoredTargets =\n item.options.targetObjectIds && item.options.targetObjectIds.length > 0\n ? (item.options.targetObjectIds\n .map((uuid) => this.scene.getObjectByProperty('uuid', uuid))\n .filter((obj) => obj !== undefined) as THREE.Object3D[])\n : undefined\n\n this.addMeasurement(start, end, {\n id: item.id,\n targets:\n restoredTargets && restoredTargets.length > 0\n ? restoredTargets\n : undefined,\n snapMode: item.options.snapMode,\n snapEnabled: item.options.snapEnabled,\n snapDistance: item.options.snapDistance,\n lineColor: item.options.lineColor,\n labelColor: item.options.labelColor,\n lineWidth: item.options.lineWidth,\n fontSize: item.options.fontSize,\n fontFamily: item.options.fontFamily,\n isDynamic: item.options.isDynamic,\n startObject: startObject || undefined,\n startLocalPosition: item.start.anchorLocalPosition\n ? new THREE.Vector3().fromArray(item.start.anchorLocalPosition)\n : undefined,\n endObject: endObject || undefined,\n endLocalPosition: item.end.anchorLocalPosition\n ? new THREE.Vector3().fromArray(item.end.anchorLocalPosition)\n : undefined,\n })\n })\n }\n\n /**\n * Dispose of all resources\n */\n dispose(): void {\n this.exitEditMode()\n this.disableInteraction()\n this.clearAll()\n\n this.previewMaterial.dispose()\n\n if (this.markerMaterial.map) {\n this.markerMaterial.map.dispose()\n }\n this.markerMaterial.dispose()\n\n if (this.editSpriteMaterial) {\n if (this.editSpriteMaterial.map) {\n this.editSpriteMaterial.map.dispose()\n }\n this.editSpriteMaterial.dispose()\n }\n }\n\n // Private methods\n\n private onMouseClick = (event: MouseEvent): void => {\n if (!this.isInteractive) return\n\n const snapResult = this.getSnapResult(event)\n if (!snapResult) return\n\n if (!this.currentMeasurement) {\n // Start new measurement\n this.startMeasurement(snapResult)\n } else {\n // Complete measurement\n this.completeMeasurement(snapResult)\n }\n }\n\n private onMouseMove = (event: MouseEvent): void => {\n if (!this.isInteractive) return\n\n const snapResult = this.getSnapResult(event)\n if (!snapResult) {\n this.hideSnapMarker()\n this.showCursor()\n return\n }\n\n if (!this.currentMeasurement) {\n // Show marker for potential start point and hide cursor\n this.updateSnapMarker(snapResult.point, true)\n this.hideCursor()\n } else {\n // Show marker for potential end point and hide cursor\n this.updateSnapMarker(snapResult.point, true)\n this.hideCursor()\n this.updatePreview(snapResult.point)\n }\n }\n\n private onKeyDown = (event: KeyboardEvent): void => {\n if (!this.isInteractive) return\n\n if (event.key === 'Escape') {\n this.cancelCurrentMeasurement()\n }\n }\n\n private hideCursor(): void {\n if (!this.domElement || this.cursorHidden) return\n\n this.originalCursor = this.domElement.style.cursor\n this.domElement.style.cursor = 'none'\n this.cursorHidden = true\n }\n\n private showCursor(): void {\n if (!this.domElement || !this.cursorHidden) return\n\n this.domElement.style.cursor = this.originalCursor || 'crosshair'\n this.cursorHidden = false\n }\n\n private createSnapMarker(): void {\n if (!this.markerVisible) return\n\n // Remove existing snap marker if it exists\n if (this.snapMarker) {\n this.scene.remove(this.snapMarker)\n }\n\n this.snapMarker = new THREE.Sprite(this.markerMaterial)\n this.snapMarker.scale.setScalar(this.markerSize)\n this.snapMarker.visible = false\n\n // Ensure marker always renders in front\n this.snapMarker.renderOrder = 999\n this.snapMarker.material.depthTest = false\n\n this.scene.add(this.snapMarker)\n }\n\n private updateSnapMarker(\n point: THREE.Vector3,\n visible: boolean = true\n ): void {\n if (!this.snapMarker || !this.markerVisible) return\n\n this.snapMarker.position.copy(point)\n this.snapMarker.visible = visible\n }\n\n private hideSnapMarker(): void {\n if (this.snapMarker) {\n this.snapMarker.visible = false\n }\n }\n\n private removeSnapMarker(): void {\n if (this.snapMarker) {\n this.scene.remove(this.snapMarker)\n this.snapMarker = null\n }\n }\n\n private startMeasurement(snapResult: SnapResult): void {\n const id = this.generateId()\n const point = snapResult.point\n const baseOptions =\n this.activeInteractionOptions ?? this.defaultOptions\n const measurementOptions: MeasurementOptions = {\n ...baseOptions,\n targets: [...baseOptions.targets],\n }\n this.pendingMeasurementOptions = measurementOptions\n\n // Hide snap marker temporarily while creating preview\n this.hideSnapMarker()\n\n // Create preview line\n const geometry = new THREE.BufferGeometry().setFromPoints([point, point])\n this.previewLine = new THREE.Line(geometry, this.previewMaterial)\n this.previewLine.computeLineDistances() // For dashed material\n this.scene.add(this.previewLine)\n\n // Create preview label\n this.previewLabel = this.createLabel(0, measurementOptions)\n this.previewLabel.position.copy(point)\n this.scene.add(this.previewLabel)\n\n const startPoint =\n measurementOptions.isDynamic && snapResult.object\n ? this.createMeasurementPoint(point, snapResult.object)\n : this.createMeasurementPoint(point)\n\n this.currentMeasurement = {\n id,\n start: startPoint,\n }\n }\n\n private updatePreview(point: THREE.Vector3): void {\n if (!this.currentMeasurement || !this.previewLine || !this.previewLabel)\n return\n\n const start = this.currentMeasurement.start!\n const distance = start.position.distanceTo(point)\n\n // Update preview line\n const geometry = new THREE.BufferGeometry().setFromPoints([\n start.position,\n point,\n ])\n this.previewLine.geometry.dispose()\n this.previewLine.geometry = geometry\n this.previewLine.computeLineDistances()\n\n // Update preview label\n const midpoint = start.position.clone().add(point).multiplyScalar(0.5)\n this.previewLabel.position.copy(midpoint)\n this.updateLabelText(this.previewLabel.element, distance)\n\n this.dispatchEvent({\n type: 'previewUpdated',\n start: start.position,\n current: point,\n distance,\n })\n }\n\n private completeMeasurement(snapResult: SnapResult): void {\n if (!this.currentMeasurement) return\n\n const start = this.currentMeasurement.start!\n const point = snapResult.point\n const options =\n this.pendingMeasurementOptions ??\n this.activeInteractionOptions ??\n this.defaultOptions\n\n this.disableInteraction()\n\n // Clean up preview\n this.cleanupPreview()\n\n const resolvedOptions: MeasurementOptions = {\n ...options,\n targets: [...options.targets],\n }\n\n const endPoint =\n resolvedOptions.isDynamic && snapResult.object\n ? this.createMeasurementPoint(point, snapResult.object)\n : this.createMeasurementPoint(point)\n\n // Create actual measurement using the measurement points\n this.addMeasurementFromPoints(start, endPoint, resolvedOptions)\n\n // Reset current measurement and object references\n this.currentMeasurement = null\n this.pendingMeasurementOptions = null\n\n // Re-create snap marker for next measurement\n this.createSnapMarker()\n }\n\n private cancelCurrentMeasurement(): void {\n this.cleanupPreview()\n this.currentMeasurement = null\n this.pendingMeasurementOptions = null\n\n // Re-create snap marker after canceling\n this.createSnapMarker()\n }\n\n private cleanupPreview(): void {\n if (this.previewLine) {\n this.scene.remove(this.previewLine)\n this.previewLine.geometry.dispose()\n this.previewLine = null\n }\n\n if (this.previewLabel) {\n this.scene.remove(this.previewLabel)\n // CSS2DObject cleanup - remove the HTML element from DOM\n if (this.previewLabel.element.parentNode) {\n this.previewLabel.element.parentNode.removeChild(\n this.previewLabel.element\n )\n }\n this.previewLabel = null\n }\n }\n\n private getSnapResult(event: MouseEvent): SnapResult | null {\n const mouse = new THREE.Vector2()\n const rect = this.domElement!.getBoundingClientRect()\n\n mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1\n mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1\n\n const options =\n this.getActiveMeasurementOptions() ?? this.defaultOptions\n const targets =\n this.activeTargets.length > 0\n ? this.activeTargets\n : options.targets.length > 0\n ? options.targets\n : this.getAllMeshes()\n\n this.raycaster.setFromCamera(mouse, this.camera)\n const intersects = this.raycaster.intersectObjects(targets, true)\n\n if (intersects.length === 0) return null\n\n const intersection = intersects[0]\n let snapPoint = intersection.point.clone()\n let snapped = false\n let snapMode = SnapMode.DISABLED\n\n if (options.snapEnabled) {\n const snapResult = this.performSnapping(intersection, options)\n snapPoint = snapResult.point\n snapped = snapResult.snapped\n snapMode = snapResult.snapMode\n }\n\n return {\n point: snapPoint,\n originalPoint: intersection.point,\n snapped,\n snapMode,\n object: intersection.object,\n }\n }\n\n private performSnapping(\n intersection: THREE.Intersection,\n options: MeasurementOptions\n ): SnapResult {\n const originalPoint = intersection.point\n let snapPoint = originalPoint.clone()\n let snapped = false\n let snapMode = SnapMode.DISABLED\n\n if (options.snapMode === SnapMode.VERTEX) {\n const vertexSnap = this.snapToVertex(intersection, options.snapDistance)\n if (vertexSnap) {\n snapPoint = vertexSnap\n snapped = true\n snapMode = SnapMode.VERTEX\n }\n } else if (options.snapMode === SnapMode.FACE) {\n // Face snapping uses the intersection point (already on face)\n snapped = true\n snapMode = SnapMode.FACE\n }\n\n return {\n point: snapPoint,\n originalPoint,\n snapped,\n snapMode,\n object: intersection.object,\n }\n }\n\n private snapToVertex(\n intersection: THREE.Intersection,\n snapDistance: number\n ): THREE.Vector3 | null {\n const geometry = (intersection.object as THREE.Mesh).geometry\n if (!geometry.attributes.position) return null\n\n const positions = geometry.attributes.position\n const worldMatrix = intersection.object.matrixWorld\n const closestVertex = new THREE.Vector3()\n let minDistance = Infinity\n let found = false\n\n // Check all vertices\n for (let i = 0; i < positions.count; i++) {\n const vertex = new THREE.Vector3()\n vertex.fromBufferAttribute(positions, i)\n vertex.applyMatrix4(worldMatrix)\n\n const distance = vertex.distanceTo(intersection.point)\n if (distance < snapDistance && distance < minDistance) {\n minDistance = distance\n closestVertex.copy(vertex)\n found = true\n }\n }\n\n return found ? closestVertex : null\n }\n\n private createLabel(\n distance: number,\n options: MeasurementOptions\n ): CSS2DObject {\n // Create HTML element for the label\n const labelDiv = document.createElement('div')\n labelDiv.className = 'measurement-label'\n\n // Set styles for the label\n Object.assign(labelDiv.style, {\n color: options.labelColor,\n fontSize: `${options.fontSize}px`,\n fontFamily: options.fontFamily,\n fontWeight: 'bold',\n background: 'rgba(0, 0, 0, 0.9)',\n padding: '8px 12px',\n borderRadius: '8px',\n border: '2px solid rgba(255, 255, 255, 0.3)',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n pointerEvents: 'auto', // Enable pointer events for double-click\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.5)',\n textShadow: '1px 1px 2px rgba(0, 0, 0, 0.8)',\n zIndex: '1000',\n cursor: 'pointer',\n })\n\n // Update the text content\n this.updateLabelText(labelDiv, distance)\n\n // Create CSS2DObject\n const css2dObject = new CSS2DObject(labelDiv)\n\n // Add double-click event listener to enter edit mode\n labelDiv.addEventListener('dblclick', (event) => {\n event.stopPropagation()\n // Find the measurement that owns this label\n const measurement = this.measurements.find((m) => m.label === css2dObject)\n if (measurement) {\n this.enterEditMode(measurement.id)\n }\n })\n\n return css2dObject\n }\n\n private updateLabelText(element: HTMLElement, distance: number): void {\n const text = `${distance.toFixed(2)}m`\n element.textContent = text\n }\n\n private removeMeasurementFromScene(measurement: Measurement): void {\n this.scene.remove(measurement.line)\n this.scene.remove(measurement.label)\n\n measurement.line.geometry.dispose()\n if (measurement.line.material instanceof THREE.Material) {\n measurement.line.material.dispose()\n }\n\n // CSS2DObject cleanup - remove the HTML element from DOM\n if (measurement.label.element.parentNode) {\n measurement.label.element.parentNode.removeChild(\n measurement.label.element\n )\n }\n }\n\n private getAllMeshes(): THREE.Object3D[] {\n const meshes: THREE.Object3D[] = []\n\n this.scene.traverse((object) => {\n if (object instanceof THREE.Mesh) {\n meshes.push(object)\n }\n })\n\n return meshes\n }\n\n private generateId(): string {\n return `measurement_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n }\n\n // Edit mode helper methods\n\n private createEditSprites(): void {\n if (!this.editingMeasurement || !this.editSpriteMaterial) return\n\n const measurement = this.editingMeasurement\n\n // Create start point sprite\n this.startEditSprite = new THREE.Sprite(this.editSpriteMaterial.clone())\n this.startEditSprite.position.copy(measurement.start.position)\n this.startEditSprite.scale.set(this.markerSize, this.markerSize, 1)\n this.startEditSprite.userData.editPoint = 'start'\n this.scene.add(this.startEditSprite)\n\n // Create end point sprite\n this.endEditSprite = new THREE.Sprite(this.editSpriteMaterial.clone())\n this.endEditSprite.position.copy(measurement.end.position)\n this.endEditSprite.scale.set(this.markerSize, this.markerSize, 1)\n this.endEditSprite.userData.editPoint = 'end'\n this.scene.add(this.endEditSprite)\n }\n\n private removeEditSprites(): void {\n if (this.startEditSprite) {\n this.scene.remove(this.startEditSprite)\n if (this.startEditSprite.material instanceof THREE.Material) {\n this.startEditSprite.material.dispose()\n }\n this.startEditSprite = null\n }\n\n if (this.endEditSprite) {\n this.scene.remove(this.endEditSprite)\n if (this.endEditSprite.material instanceof THREE.Material) {\n this.endEditSprite.material.dispose()\n }\n this.endEditSprite = null\n }\n }\n\n private onEditMouseDown = (event: MouseEvent): void => {\n if (!this.isEditMode || !this.domElement) return\n const mouse = new THREE.Vector2()\n const rect = this.domElement.getBoundingClientRect()\n\n mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1\n mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1\n\n this.raycaster.setFromCamera(mouse, this.camera)\n\n // Check if clicking on edit sprites\n const sprites = [this.startEditSprite, this.endEditSprite].filter(\n (s) => s !== null\n ) as THREE.Sprite[]\n const spriteIntersects = this.raycaster.intersectObjects(sprites)\n\n if (spriteIntersects.length > 0) {\n const sprite = spriteIntersects[0].object\n this.editingPoint = sprite.userData.editPoint as 'start' | 'end'\n this.isDragging = true\n\n // Disable camera controls during dragging\n this.disableControls()\n\n // Hide the edit sprite being dragged and show crosshair marker\n if (this.editingPoint === 'start' && this.startEditSprite) {\n this.startEditSprite.visible = false\n } else if (this.editingPoint === 'end' && this.endEditSprite) {\n this.endEditSprite.visible = false\n }\n\n // Show snap marker\n this.createSnapMarker()\n if (this.snapMarker) {\n this.snapMarker.position.copy(sprite.position)\n this.snapMarker.visible = true\n }\n\n // Hide cursor\n this.hideCursor()\n }\n }\n\n private onEditMouseMove = (event: MouseEvent): void => {\n if (!this.isEditMode || !this.isDragging || !this.editingMeasurement) return\n\n const snapResult = this.getSnapResult(event)\n if (!snapResult) return\n\n // Update snap marker position\n if (this.snapMarker) {\n this.snapMarker.position.copy(snapResult.point)\n this.snapMarker.visible = this.markerVisible\n }\n\n // Update the measurement preview\n if (this.editingPoint === 'start') {\n this.updateMeasurementPreview(\n snapResult.point,\n this.editingMeasurement.end.position\n )\n } else if (this.editingPoint === 'end') {\n this.updateMeasurementPreview(\n this.editingMeasurement.start.position,\n snapResult.point\n )\n }\n }\n\n private onEditMouseUp = (event: MouseEvent): void => {\n if (\n !this.isEditMode ||\n !this.isDragging ||\n !this.editingMeasurement ||\n !this.editingPoint\n )\n return\n\n const snapResult = this.getSnapResult(event)\n if (!snapResult) {\n this.cancelEdit()\n return\n }\n\n // Update the measurement point\n const point =\n this.editingPoint === 'start'\n ? this.editingMeasurement.start\n : this.editingMeasurement.end\n\n // Update position\n point.position.copy(snapResult.point)\n\n // Update dynamic point data if applicable\n const measurementOptions = this.editingMeasurement.options\n if (measurementOptions.isDynamic && snapResult.object) {\n const localPos = snapResult.object.worldToLocal(snapResult.point.clone())\n point.anchor = {\n object: snapResult.object,\n localPosition: localPos,\n }\n } else {\n // Make it static\n point.anchor = undefined\n }\n\n // Recalculate distance and update geometry\n const newDistance = this.editingMeasurement.start.position.distanceTo(\n this.editingMeasurement.end.position\n )\n this.editingMeasurement.distance = newDistance\n\n // Update line geometry\n const positions = [\n this.editingMeasurement.start.position,\n this.editingMeasurement.end.position,\n ]\n this.editingMeasurement.line.geometry.setFromPoints(positions)\n this.editingMeasurement.line.geometry.attributes.position.needsUpdate = true\n\n // Update label position and text\n const midpoint = this.editingMeasurement.start.position\n .clone()\n .add(this.editingMeasurement.end.position)\n .multiplyScalar(0.5)\n this.editingMeasurement.label.position.copy(midpoint)\n this.updateLabelText(this.editingMeasurement.label.element, newDistance)\n\n // Update edit sprite positions\n if (this.startEditSprite) {\n this.startEditSprite.position.copy(this.editingMeasurement.start.position)\n this.startEditSprite.visible = true\n }\n if (this.endEditSprite) {\n this.endEditSprite.position.copy(this.editingMeasurement.end.position)\n this.endEditSprite.visible = true\n }\n\n // Remove snap marker\n this.removeSnapMarker()\n\n // Show cursor\n this.showCursor()\n\n // Re-enable camera controls\n this.enableControls()\n\n // Dispatch update event\n this.dispatchEvent({\n type: 'measurementUpdated',\n measurement: this.editingMeasurement,\n })\n\n // Reset dragging state\n this.isDragging = false\n this.editingPoint = null\n }\n\n private cancelEdit(): void {\n // Reset dragging state\n this.isDragging = false\n this.editingPoint = null\n\n // Re-enable camera controls\n this.enableControls()\n\n // Show edit sprites\n if (this.startEditSprite) {\n this.startEditSprite.visible = true\n }\n if (this.endEditSprite) {\n this.endEditSprite.visible = true\n }\n\n // Remove snap marker\n this.removeSnapMarker()\n\n // Show cursor\n this.showCursor()\n }\n\n private updateMeasurementPreview(\n startPos: THREE.Vector3,\n endPos: THREE.Vector3\n ): void {\n if (!this.editingMeasurement) return\n\n const distance = startPos.distanceTo(endPos)\n\n // Update line geometry\n const positions = [startPos, endPos]\n this.editingMeasurement.line.geometry.setFromPoints(positions)\n this.editingMeasurement.line.geometry.attributes.position.needsUpdate = true\n\n // Update label position and text\n const midpoint = startPos.clone().add(endPos).multiplyScalar(0.5)\n this.editingMeasurement.label.position.copy(midpoint)\n this.updateLabelText(this.editingMeasurement.label.element, distance)\n }\n}\n","import * as THREE from 'three'\nimport { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'\n\n/**\n * Defines how a measurement point is attached to a scene object.\n */\nexport interface MeasurementAnchor {\n /** Object the point is attached to */\n object: THREE.Object3D\n /** Local position relative to the object */\n localPosition: THREE.Vector3\n}\n\n/**\n * Represents a measurement point in world space with an optional anchor.\n */\nexport interface MeasurementPoint {\n /** World position of the measurement point */\n position: THREE.Vector3\n /** Optional attachment data for dynamic updates */\n anchor?: MeasurementAnchor\n}\n\n/**\n * Per-measurement configuration captured at creation time.\n */\nexport interface MeasurementOptions {\n /** Render color for the measurement line */\n lineColor: number\n /** Render color for the measurement label text */\n labelColor: string\n /** Width of the rendered line */\n lineWidth: number\n /** Font size for label text */\n fontSize: number\n /** Font family for label text */\n fontFamily: string\n /** Snapping mode used when the measurement was created */\n snapMode: SnapMode\n /** Whether snapping was enabled for this measurement */\n snapEnabled: boolean\n /** Snap distance threshold for this measurement */\n snapDistance: number\n /** Targets that were considered for snapping */\n targets: THREE.Object3D[]\n /** Whether this measurement should remain linked to scene objects */\n isDynamic: boolean\n}\n\n/**\n * Runtime representation of a measurement inside the scene.\n */\nexport interface Measurement {\n /** Unique identifier for the measurement */\n id: string\n /** Starting point of the measurement */\n start: MeasurementPoint\n /** Ending point of the measurement */\n end: MeasurementPoint\n /** The line geometry representing the measurement */\n line: THREE.Line\n /** The CSS2D label showing the distance */\n label: CSS2DObject\n /** The calculated distance between start and end points */\n distance: number\n /** Config that should be reused when editing this measurement */\n options: MeasurementOptions\n}\n\n/**\n * Options accepted when creating or importing a measurement.\n */\nexport interface MeasurementCreationOptions {\n /** Explicit measurement identifier */\n id?: string\n /** Objects that should be considered for snapping/intersections */\n targets?: THREE.Object3D[]\n /** Whether snapping should be enabled */\n snapEnabled?: boolean\n /** Snap distance threshold */\n snapDistance?: number\n /** Snapping mode */\n snapMode?: SnapMode\n /** Line color */\n lineColor?: number\n /** Label color */\n labelColor?: string\n /** Line width */\n lineWidth?: number\n /** Label font size */\n fontSize?: number\n /** Label font family */\n fontFamily?: string\n /** Whether the measurement should stay linked to objects */\n isDynamic?: boolean\n /** Object the start point should follow */\n startObject?: THREE.Object3D\n /** Local position relative to startObject (defaults to world position converted to local) */\n startLocalPosition?: THREE.Vector3\n /** Object the end point should follow */\n endObject?: THREE.Object3D\n /** Local position relative to endObject (defaults to world position converted to local) */\n endLocalPosition?: THREE.Vector3\n}\n\n/**\n * Serializable measurement point data for export/import.\n */\nexport interface MeasurementPointData {\n /** Position as array [x, y, z] */\n position: [number, number, number]\n /** UUID of the anchor object if the point is dynamic */\n anchorObjectId?: string\n /** Local position relative to the anchor object */\n anchorLocalPosition?: [number, number, number]\n}\n\n/**\n * Serializable measurement configuration data.\n */\nexport interface SerializedMeasurementOptions {\n /** Snapping mode used by the measurement */\n snapMode: SnapMode\n /** Whether snapping was enabled for the measurement */\n snapEnabled: boolean\n /** Snap distance threshold used by the measurement */\n snapDistance: number\n /** Measurement line color */\n lineColor: number\n /** Measurement label color */\n labelColor: string\n /** Measurement line width */\n lineWidth: number\n /** Measurement label font size */\n fontSize: number\n /** Measurement label font family */\n fontFamily: string\n /** Whether the measurement should stay linked to objects */\n isDynamic: boolean\n /** UUIDs of the target objects captured for this measurement */\n targetObjectIds?: string[]\n}\n\n/**\n * Serializable measurement data for export/import.\n */\nexport interface MeasurementData {\n /** Unique identifier */\n id: string\n /** Starting point data */\n start: MeasurementPointData\n /** Ending point data */\n end: MeasurementPointData\n /** Calculated distance */\n distance: number\n /** Captured configuration for the measurement */\n options: SerializedMeasurementOptions\n}\n\n/**\n * Configuration options for the measurement tool\n */\nexport interface MeasurementToolOptions {\n /** DOM element for mouse interactions (required for interactive mode and editing) */\n domElement?: HTMLElement\n /** Camera controls to disable during edit dragging (e.g., OrbitControls) */\n controls?: { enabled: boolean }\n}\n\n/**\n * Snapping mode options\n */\nexport enum SnapMode {\n VERTEX = 'vertex',\n FACE = 'face',\n EDGE = 'edge',\n DISABLED = 'disabled',\n}\n\n/**\n * Event types dispatched by the measurement tool\n */\nexport interface MeasurementToolEvents {\n /** Fired when a new measurement is created */\n measurementCreated: { measurement: Measurement }\n /** Fired when a measurement is removed */\n measurementRemoved: { measurement: Measurement }\n /** Fired when all measurements are cleared */\n measurementsCleared: { count: number }\n /** Fired when interactive mode starts */\n started: {}\n /** Fired when interactive mode ends */\n ended: {}\n /** Fired when preview line is updated */\n previewUpdated: {\n start: THREE.Vector3\n current: THREE.Vector3\n distance: number\n }\n /** Fired when edit mode is entered */\n editModeEntered: { measurement: Measurement }\n /** Fired when edit mode is exited */\n editModeExited: { measurement: Measurement }\n /** Fired when a measurement is updated */\n measurementUpdated: { measurement: Measurement }\n}\n\n/**\n * Raycast result with snapping information\n */\nexport interface SnapResult {\n /** The snapped position */\n point: THREE.Vector3\n /** The original intersection point */\n originalPoint: THREE.Vector3\n /** Whether snapping occurred */\n snapped: boolean\n /** The snap mode that was applied */\n snapMode: SnapMode\n /** The object that was intersected */\n object: THREE.Object3D\n}\n"],"mappings":"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","names":["SnapMode"]}