@tonybfox/threejs-tools 1.0.3 → 1.0.4

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 (61) hide show
  1. package/dist/asset-loader/index.cjs +0 -1
  2. package/dist/asset-loader/index.mjs +1 -2
  3. package/dist/camera/index.cjs +0 -1
  4. package/dist/camera/index.mjs +1 -2
  5. package/dist/{chunk-WZ6IGBSE.mjs → chunk-27WUVRGX.mjs} +0 -1
  6. package/dist/{chunk-EQDOX34V.mjs → chunk-2CDI7ORN.mjs} +0 -1
  7. package/dist/{chunk-EIROAPF7.mjs → chunk-FBTT6MU6.mjs} +0 -1
  8. package/dist/{chunk-VETFQ3IQ.mjs → chunk-IAZH4OHC.mjs} +0 -1
  9. package/dist/{chunk-5DP6WDB3.mjs → chunk-OZKJ3GAD.mjs} +0 -1
  10. package/dist/{chunk-IIAZ2WJJ.mjs → chunk-W4DAAAW6.mjs} +0 -1
  11. package/dist/{chunk-IEE7DQOU.mjs → chunk-XA7OKYSM.mjs} +0 -1
  12. package/dist/{chunk-BJKSICFA.mjs → chunk-YMMLYGHV.mjs} +0 -1
  13. package/dist/{chunk-P35QJCOG.mjs → chunk-ZNGFST7K.mjs} +15 -6
  14. package/dist/compass/index.cjs +15 -6
  15. package/dist/compass/index.d.mts +4 -2
  16. package/dist/compass/index.d.ts +4 -2
  17. package/dist/compass/index.mjs +1 -2
  18. package/dist/grid/index.cjs +0 -1
  19. package/dist/grid/index.mjs +1 -2
  20. package/dist/index.cjs +15 -6
  21. package/dist/index.mjs +9 -10
  22. package/dist/measurements/index.cjs +0 -1
  23. package/dist/measurements/index.mjs +1 -2
  24. package/dist/sunlight/index.cjs +0 -1
  25. package/dist/sunlight/index.mjs +1 -2
  26. package/dist/terrain/index.cjs +0 -1
  27. package/dist/terrain/index.mjs +1 -2
  28. package/dist/transform-controls/index.cjs +0 -1
  29. package/dist/transform-controls/index.mjs +1 -2
  30. package/dist/view-helper/index.cjs +0 -1
  31. package/dist/view-helper/index.mjs +1 -2
  32. package/package.json +1 -1
  33. package/dist/asset-loader/index.cjs.map +0 -1
  34. package/dist/asset-loader/index.mjs.map +0 -1
  35. package/dist/camera/index.cjs.map +0 -1
  36. package/dist/camera/index.mjs.map +0 -1
  37. package/dist/chunk-5DP6WDB3.mjs.map +0 -1
  38. package/dist/chunk-BJKSICFA.mjs.map +0 -1
  39. package/dist/chunk-EIROAPF7.mjs.map +0 -1
  40. package/dist/chunk-EQDOX34V.mjs.map +0 -1
  41. package/dist/chunk-IEE7DQOU.mjs.map +0 -1
  42. package/dist/chunk-IIAZ2WJJ.mjs.map +0 -1
  43. package/dist/chunk-P35QJCOG.mjs.map +0 -1
  44. package/dist/chunk-VETFQ3IQ.mjs.map +0 -1
  45. package/dist/chunk-WZ6IGBSE.mjs.map +0 -1
  46. package/dist/compass/index.cjs.map +0 -1
  47. package/dist/compass/index.mjs.map +0 -1
  48. package/dist/grid/index.cjs.map +0 -1
  49. package/dist/grid/index.mjs.map +0 -1
  50. package/dist/index.cjs.map +0 -1
  51. package/dist/index.mjs.map +0 -1
  52. package/dist/measurements/index.cjs.map +0 -1
  53. package/dist/measurements/index.mjs.map +0 -1
  54. package/dist/sunlight/index.cjs.map +0 -1
  55. package/dist/sunlight/index.mjs.map +0 -1
  56. package/dist/terrain/index.cjs.map +0 -1
  57. package/dist/terrain/index.mjs.map +0 -1
  58. package/dist/transform-controls/index.cjs.map +0 -1
  59. package/dist/transform-controls/index.mjs.map +0 -1
  60. package/dist/view-helper/index.cjs.map +0 -1
  61. package/dist/view-helper/index.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/camera/src/DualCameraControls.ts"],"sourcesContent":["import * as THREE from 'three'\nimport CameraControls from 'camera-controls'\n\ntype Vector3Tuple = [number, number, number]\ntype Vector3Like = THREE.Vector3 | Vector3Tuple\n\nexport type CameraMode = 'perspective' | 'orthographic'\n\nexport interface PerspectiveCameraConfig {\n fov?: number\n near?: number\n far?: number\n position?: Vector3Like\n zoom?: number\n}\n\nexport interface OrthographicCameraConfig {\n size?: number\n near?: number\n far?: number\n position?: Vector3Like\n zoom?: number\n}\n\nexport interface DualCameraControlsOptions {\n domElement?: HTMLElement\n initialMode?: CameraMode\n initialTarget?: Vector3Like\n perspective?: PerspectiveCameraConfig\n orthographic?: OrthographicCameraConfig\n}\n\nexport interface ModeChangedEvent {\n type: 'modechange'\n mode: CameraMode\n previousMode: CameraMode\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\n}\n\nexport interface ExternalCameraChangedEvent {\n type: 'externalcamerachange'\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\n previousCamera: THREE.PerspectiveCamera | THREE.OrthographicCamera\n}\n\nlet controlsInstalled = false\n\nconst tempVec3A = new THREE.Vector3()\nconst tempVec3B = new THREE.Vector3()\nconst tempVec2 = new THREE.Vector2()\n\nfunction ensureCameraControlsInstalled() {\n if (!controlsInstalled) {\n CameraControls.install({ THREE })\n controlsInstalled = true\n }\n}\n\nfunction toVector3(\n value: Vector3Like | undefined,\n fallback: Vector3Tuple,\n target: THREE.Vector3\n) {\n if (!value) {\n target.set(fallback[0], fallback[1], fallback[2])\n return target\n }\n\n if (Array.isArray(value)) {\n target.set(value[0], value[1], value[2])\n return target\n }\n\n target.copy(value)\n return target\n}\n\n/**\n * Camera controls that manage both perspective and orthographic cameras while\n * extending {@link CameraControls}. Provides helpers to toggle between the\n * camera types and keep the framing consistent.\n */\nexport class DualCameraControls extends CameraControls {\n readonly perspectiveCamera: THREE.PerspectiveCamera\n readonly orthographicCamera: THREE.OrthographicCamera\n\n private readonly renderer: THREE.WebGLRenderer\n private readonly domElementRef: HTMLElement\n private activeMode: CameraMode\n private readonly minOrthoHalfHeight: number\n private readonly updateClock = new THREE.Clock()\n private externalCamera:\n | THREE.PerspectiveCamera\n | THREE.OrthographicCamera\n | null = null\n\n constructor(\n renderer: THREE.WebGLRenderer,\n options: DualCameraControlsOptions = {}\n ) {\n ensureCameraControlsInstalled()\n\n const { domElement = renderer.domElement } = options\n const aspect = resolveAspect(renderer, domElement)\n\n const perspectiveConfig = options.perspective ?? {}\n const orthographicConfig = options.orthographic ?? {}\n\n const perspectiveCamera = new THREE.PerspectiveCamera(\n perspectiveConfig.fov ?? 60,\n aspect,\n perspectiveConfig.near ?? 0.1,\n perspectiveConfig.far ?? 2000\n )\n\n perspectiveCamera.position.copy(\n toVector3(perspectiveConfig.position, [12, 12, 12], new THREE.Vector3())\n )\n\n if (perspectiveConfig.zoom !== undefined) {\n perspectiveCamera.zoom = perspectiveConfig.zoom\n perspectiveCamera.updateProjectionMatrix()\n }\n\n const orthoHalfHeight = Math.max(orthographicConfig.size ?? 20, 0.001) * 0.5\n const orthoHalfWidth = orthoHalfHeight * aspect\n\n const orthographicCamera = new THREE.OrthographicCamera(\n -orthoHalfWidth,\n orthoHalfWidth,\n orthoHalfHeight,\n -orthoHalfHeight,\n orthographicConfig.near ?? 0.1,\n orthographicConfig.far ?? 2000\n )\n\n orthographicCamera.position.copy(\n toVector3(orthographicConfig.position, [12, 12, 12], new THREE.Vector3())\n )\n\n if (orthographicConfig.zoom !== undefined) {\n orthographicCamera.zoom = orthographicConfig.zoom\n orthographicCamera.updateProjectionMatrix()\n }\n\n const initialMode = options.initialMode ?? 'perspective'\n const initialCamera =\n initialMode === 'orthographic' ? orthographicCamera : perspectiveCamera\n\n super(initialCamera, domElement)\n\n const initialTarget = toVector3(\n options.initialTarget,\n [0, 0, 0],\n new THREE.Vector3()\n )\n void this.setLookAt(\n initialCamera.position.x,\n initialCamera.position.y,\n initialCamera.position.z,\n initialTarget.x,\n initialTarget.y,\n initialTarget.z,\n false\n )\n\n this.renderer = renderer\n this.domElementRef = domElement\n this.perspectiveCamera = perspectiveCamera\n this.orthographicCamera = orthographicCamera\n this.activeMode = initialMode\n this.minOrthoHalfHeight = orthoHalfHeight\n\n this.updateInputBindingsForMode(initialMode)\n }\n\n get mode(): CameraMode {\n return this.activeMode\n }\n\n /**\n * Returns the currently active camera instance.\n */\n get activeCamera(): THREE.PerspectiveCamera | THREE.OrthographicCamera {\n return this.camera\n }\n\n /**\n * Switch to the perspective camera while keeping the current framing.\n */\n switchToPerspective(enableTransition = false) {\n if (this.activeMode === 'perspective' && !this.externalCamera) {\n return\n }\n\n // Clear external camera if set\n this.externalCamera = null\n\n const target = this.getTarget(tempVec3A)\n const position = this.getPosition(tempVec3B)\n\n const aspect = resolveAspect(this.renderer, this.domElementRef)\n this.perspectiveCamera.aspect = aspect\n this.perspectiveCamera.position.copy(position)\n this.perspectiveCamera.quaternion.copy(this.camera.quaternion)\n this.perspectiveCamera.up.copy(this.camera.up)\n this.perspectiveCamera.updateProjectionMatrix()\n\n this.camera = this.perspectiveCamera\n this.activeMode = 'perspective'\n this.updateInputBindingsForMode('perspective')\n\n void this.setLookAt(\n position.x,\n position.y,\n position.z,\n target.x,\n target.y,\n target.z,\n enableTransition\n )\n\n this.dispatchEvent({\n type: 'modechange',\n mode: this.activeMode,\n previousMode: 'orthographic',\n camera: this.perspectiveCamera,\n } satisfies ModeChangedEvent)\n }\n\n /**\n * Switch to the orthographic camera while keeping the current framing.\n */\n switchToOrthographic(enableTransition = false) {\n if (this.activeMode === 'orthographic' && !this.externalCamera) {\n return\n }\n\n // Clear external camera if set\n this.externalCamera = null\n\n const target = this.getTarget(tempVec3A)\n const position = this.getPosition(tempVec3B)\n\n const aspect = resolveAspect(this.renderer, this.domElementRef)\n this.updateOrthographicFrustum(position, target, aspect)\n\n this.orthographicCamera.position.copy(position)\n this.orthographicCamera.quaternion.copy(this.camera.quaternion)\n this.orthographicCamera.up.copy(this.camera.up)\n this.orthographicCamera.updateProjectionMatrix()\n\n this.camera = this.orthographicCamera\n this.activeMode = 'orthographic'\n this.updateInputBindingsForMode('orthographic')\n\n void this.setLookAt(\n position.x,\n position.y,\n position.z,\n target.x,\n target.y,\n target.z,\n enableTransition\n )\n\n this.dispatchEvent({\n type: 'modechange',\n mode: this.activeMode,\n previousMode: 'perspective',\n camera: this.orthographicCamera,\n } satisfies ModeChangedEvent)\n }\n\n /**\n * Toggles between perspective and orthographic camera modes.\n */\n toggleCameraMode(enableTransition = false) {\n if (this.activeMode === 'perspective') {\n this.switchToOrthographic(enableTransition)\n } else {\n this.switchToPerspective(enableTransition)\n }\n }\n\n /**\n * Returns true if currently using an external camera.\n */\n get isUsingExternalCamera(): boolean {\n return this.externalCamera !== null\n }\n\n /**\n * Sets an external camera to use with the controls.\n * This allows using a camera created outside of DualCameraControls.\n * Call `clearExternalCamera()` to return to the internal cameras.\n */\n setCamera(\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera,\n target?: Vector3Like,\n enableTransition = false\n ) {\n const previousCamera = this.camera\n this.externalCamera = camera\n\n // Update aspect/frustum for the external camera\n const aspect = resolveAspect(this.renderer, this.domElementRef)\n if (camera.type === 'PerspectiveCamera') {\n ;(camera as THREE.PerspectiveCamera).aspect = aspect\n camera.updateProjectionMatrix()\n } else if (camera.type === 'OrthographicCamera') {\n // Maintain the camera's existing frustum proportions but update aspect\n const ortho = camera as THREE.OrthographicCamera\n const currentHeight = (ortho.top - ortho.bottom) * 0.5\n const newHalfWidth = currentHeight * aspect\n ortho.left = -newHalfWidth\n ortho.right = newHalfWidth\n ortho.updateProjectionMatrix()\n }\n\n this.camera = camera\n const mode: CameraMode =\n camera.type === 'PerspectiveCamera' ? 'perspective' : 'orthographic'\n this.activeMode = mode\n this.updateInputBindingsForMode(mode)\n\n // Set up the camera position and target in the controls\n const targetVec = toVector3(target, [0, 0, 0], tempVec3A)\n void this.setLookAt(\n camera.position.x,\n camera.position.y,\n camera.position.z,\n targetVec.x,\n targetVec.y,\n targetVec.z,\n enableTransition\n )\n\n this.dispatchEvent({\n type: 'externalcamerachange',\n camera,\n previousCamera,\n } satisfies ExternalCameraChangedEvent)\n }\n\n /**\n * Clears the external camera and returns to using the internal cameras.\n * Will switch to the camera matching the current mode.\n */\n clearExternalCamera(enableTransition = false) {\n if (!this.externalCamera) {\n return\n }\n\n const target = this.getTarget(tempVec3A)\n const position = this.getPosition(tempVec3B)\n\n this.externalCamera = null\n\n // Return to the internal camera matching the current mode\n const internalCamera =\n this.activeMode === 'orthographic'\n ? this.orthographicCamera\n : this.perspectiveCamera\n\n internalCamera.position.copy(position)\n internalCamera.quaternion.copy(this.camera.quaternion)\n internalCamera.up.copy(this.camera.up)\n\n const aspect = resolveAspect(this.renderer, this.domElementRef)\n if (this.activeMode === 'orthographic') {\n this.updateOrthographicFrustum(position, target, aspect)\n } else {\n this.perspectiveCamera.aspect = aspect\n this.perspectiveCamera.updateProjectionMatrix()\n }\n\n this.camera = internalCamera\n\n void this.setLookAt(\n position.x,\n position.y,\n position.z,\n target.x,\n target.y,\n target.z,\n enableTransition\n )\n\n this.dispatchEvent({\n type: 'modechange',\n mode: this.activeMode,\n previousMode: this.activeMode,\n camera: internalCamera,\n } satisfies ModeChangedEvent)\n }\n\n /**\n * Update camera projection parameters when the viewport size changes.\n */\n handleResize(width: number, height: number) {\n const aspect = height === 0 ? 1 : width / height\n\n this.perspectiveCamera.aspect = aspect\n this.perspectiveCamera.updateProjectionMatrix()\n\n const target = this.getTarget(tempVec3A)\n const position = this.getPosition(tempVec3B)\n this.updateOrthographicFrustum(position, target, aspect)\n\n if (this.activeMode === 'orthographic') {\n this.camera = this.orthographicCamera\n void this.setLookAt(\n position.x,\n position.y,\n position.z,\n target.x,\n target.y,\n target.z,\n false\n )\n }\n }\n\n /**\n * Moves the camera to a new position and target.\n */\n moveCamera(\n position: Vector3Like,\n target: Vector3Like,\n enableTransition = true\n ) {\n toVector3(position, [0, 0, 0], tempVec3A)\n toVector3(target, [0, 0, 0], tempVec3B)\n\n return this.setLookAt(\n tempVec3A.x,\n tempVec3A.y,\n tempVec3A.z,\n tempVec3B.x,\n tempVec3B.y,\n tempVec3B.z,\n enableTransition\n )\n }\n\n /**\n * Updates the controls using an internally managed clock.\n * Useful when you don't want to pass delta time each frame.\n */\n updateDelta(): ReturnType<CameraControls['update']> {\n const delta = this.updateClock.getDelta()\n return super.update(delta)\n }\n\n private updateInputBindingsForMode(mode: CameraMode) {\n const { ACTION } = CameraControls\n\n if (mode === 'orthographic') {\n this.mouseButtons.left = ACTION.TRUCK\n this.mouseButtons.right = ACTION.ROTATE\n this.mouseButtons.wheel = ACTION.ZOOM\n this.touches.one = ACTION.TOUCH_TRUCK\n this.touches.two = ACTION.TOUCH_ZOOM_TRUCK\n } else {\n this.mouseButtons.left = ACTION.ROTATE\n this.mouseButtons.right = ACTION.TRUCK\n this.mouseButtons.wheel = ACTION.DOLLY\n this.touches.one = ACTION.TOUCH_ROTATE\n this.touches.two = ACTION.TOUCH_DOLLY_TRUCK\n }\n }\n\n private updateOrthographicFrustum(\n position: THREE.Vector3,\n target: THREE.Vector3,\n aspect: number\n ) {\n const distance = Math.max(position.distanceTo(target), 0.001)\n const fov = this.perspectiveCamera.fov\n const halfHeight = Math.max(\n distance * Math.tan(THREE.MathUtils.degToRad(fov * 0.5)),\n this.minOrthoHalfHeight\n )\n const halfWidth = halfHeight * aspect\n\n this.orthographicCamera.left = -halfWidth\n this.orthographicCamera.right = halfWidth\n this.orthographicCamera.top = halfHeight\n this.orthographicCamera.bottom = -halfHeight\n this.orthographicCamera.updateProjectionMatrix()\n }\n}\n\nfunction resolveAspect(\n renderer: THREE.WebGLRenderer,\n domElement: HTMLElement\n): number {\n const size = renderer.getSize(tempVec2)\n if (size.y > 0) {\n return size.x / size.y\n }\n\n const { clientWidth, clientHeight } = domElement\n if (clientHeight > 0) {\n return clientWidth / clientHeight\n }\n\n return 1\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/sunlight/src/SunLightTool.ts"],"sourcesContent":["import * as THREE from 'three'\n\nexport type SunLightWeather = 'sunny' | 'partly-cloudy' | 'overcast'\n\nexport interface SunLightToolOptions {\n light?: THREE.DirectionalLight\n ambientLight?: THREE.Light\n latitude?: number\n longitude?: number\n dayOfYear?: number\n timeOfDay?: number\n timeZoneOffsetMinutes?: number\n referenceYear?: number\n useSystemTime?: boolean\n weather?: SunLightWeather\n showHelper?: boolean\n helperSize?: number\n helperColor?: number\n enableHemisphereLight?: boolean\n hemisphereSkyColor?: number\n hemisphereGroundColor?: number\n hemisphereIntensity?: number\n lightDistance?: number\n shadowCameraSize?: number\n shadowCameraFar?: number\n}\n\nexport interface SunLightState {\n date: Date\n latitude: number\n longitude: number\n solarAzimuth: number\n solarAltitude: number\n weather: SunLightWeather\n useSystemTime: boolean\n}\n\nexport interface SunLightToolEvents {\n stateChanged: { state: SunLightState }\n weatherChanged: { weather: SunLightWeather }\n systemTimeToggled: { useSystemTime: boolean }\n}\n\ninterface WeatherPresetConfig {\n sunIntensity: number\n sunColor: THREE.Color\n ambientIntensity: number\n ambientColor: THREE.Color\n hemisphereIntensity: number\n hemisphereSkyColor: THREE.Color\n hemisphereGroundColor: THREE.Color\n shadowBias: number\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst DEG2RAD = Math.PI / 180\nconst RAD2DEG = 180 / Math.PI\nconst J1970 = 2440588\nconst J2000 = 2451545\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max)\n\nconst normalizeLongitude = (value: number): number => {\n const normalized = ((((value + 180) % 360) + 360) % 360) - 180\n return normalized\n}\n\nconst isLeapYear = (year: number): boolean =>\n (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0\n\nconst limitDayOfYear = (day: number, year: number): number => {\n const maxDay = isLeapYear(year) ? 366 : 365\n return clamp(Math.floor(day), 1, maxDay)\n}\n\nconst normalizeHours = (hours: number): number => {\n const normalized = hours % 24\n return normalized < 0 ? normalized + 24 : normalized\n}\n\nconst TWILIGHT_SUN_COLOR = new THREE.Color(0xff8c5c)\nconst TWILIGHT_AMBIENT_COLOR = new THREE.Color(0xffb48a)\nconst TWILIGHT_SKY_COLOR = new THREE.Color(0xff9966)\nconst TWILIGHT_GROUND_COLOR = new THREE.Color(0x362a24)\n\nconst toJulian = (date: Date): number => date.valueOf() / DAY_MS - 0.5 + J1970\nconst toDays = (date: Date): number => toJulian(date) - J2000\n\nconst solarMeanAnomaly = (days: number): number =>\n DEG2RAD * (357.5291 + 0.98560028 * days)\n\nconst eclipticLongitude = (meanAnomaly: number): number => {\n const c =\n DEG2RAD *\n (1.9148 * Math.sin(meanAnomaly) +\n 0.02 * Math.sin(2 * meanAnomaly) +\n 0.0003 * Math.sin(3 * meanAnomaly))\n const p = DEG2RAD * 102.9372\n return meanAnomaly + c + p + Math.PI\n}\n\nconst declination = (longitude: number): number => {\n const e = DEG2RAD * 23.4397\n return Math.asin(Math.sin(e) * Math.sin(longitude))\n}\n\nconst rightAscension = (longitude: number): number => {\n const e = DEG2RAD * 23.4397\n return Math.atan2(Math.sin(longitude) * Math.cos(e), Math.cos(longitude))\n}\n\nconst siderealTime = (days: number, longitudeWest: number): number =>\n DEG2RAD * (280.16 + 360.9856235 * days) - longitudeWest\n\nconst solarAzimuth = (\n hourAngle: number,\n latitudeRad: number,\n declinationRad: number\n): number =>\n Math.atan2(\n Math.sin(hourAngle),\n Math.cos(hourAngle) * Math.sin(latitudeRad) -\n Math.tan(declinationRad) * Math.cos(latitudeRad)\n )\n\nconst solarAltitude = (\n hourAngle: number,\n latitudeRad: number,\n declinationRad: number\n): number =>\n Math.asin(\n Math.sin(latitudeRad) * Math.sin(declinationRad) +\n Math.cos(latitudeRad) * Math.cos(declinationRad) * Math.cos(hourAngle)\n )\n\nconst computeDayOfYear = (date: Date): number => {\n const startOfYear = Date.UTC(date.getUTCFullYear(), 0, 0)\n const diff = date.getTime() - startOfYear\n return Math.floor(diff / (24 * 60 * 60 * 1000))\n}\n\nconst buildWeatherPreset = (\n weather: SunLightWeather,\n solarAltitude: number\n): WeatherPresetConfig => {\n const altitudeFactor = clamp(Math.sin(solarAltitude) + 0.1, 0, 1)\n switch (weather) {\n case 'sunny':\n return {\n sunIntensity: 1.5 * altitudeFactor,\n sunColor: new THREE.Color(0xfff2cf),\n ambientIntensity: 0.35 + 0.45 * altitudeFactor,\n ambientColor: new THREE.Color(0xf5e7cf),\n hemisphereIntensity: 0.4 + 0.4 * altitudeFactor,\n hemisphereSkyColor: new THREE.Color(0xb8d8ff),\n hemisphereGroundColor: new THREE.Color(0xf4d1aa),\n shadowBias: -0.0005,\n }\n case 'partly-cloudy':\n return {\n sunIntensity: 1.1 * altitudeFactor,\n sunColor: new THREE.Color(0xfff6e5),\n ambientIntensity: 0.45 + 0.35 * altitudeFactor,\n ambientColor: new THREE.Color(0xe8ecf1),\n hemisphereIntensity: 0.5 + 0.3 * altitudeFactor,\n hemisphereSkyColor: new THREE.Color(0xc8d7eb),\n hemisphereGroundColor: new THREE.Color(0xe6d9c2),\n shadowBias: -0.0003,\n }\n case 'overcast':\n return {\n sunIntensity: 0.35 * altitudeFactor,\n sunColor: new THREE.Color(0xf0f4ff),\n ambientIntensity: 0.75,\n ambientColor: new THREE.Color(0xd9dde5),\n hemisphereIntensity: 0.8,\n hemisphereSkyColor: new THREE.Color(0xcdd5df),\n hemisphereGroundColor: new THREE.Color(0xc8c8c8),\n shadowBias: -0.0001,\n }\n default:\n return {\n sunIntensity: 1.0 * altitudeFactor,\n sunColor: new THREE.Color(0xffd9b3),\n ambientIntensity: 0.35 + 0.25 * altitudeFactor,\n ambientColor: new THREE.Color(0xffe3c4),\n hemisphereIntensity: 0.4 + 0.3 * altitudeFactor,\n hemisphereSkyColor: new THREE.Color(0xffc8a3),\n hemisphereGroundColor: new THREE.Color(0x5f463a),\n shadowBias: -0.0002,\n }\n }\n}\n\nexport class SunLightTool extends THREE.EventDispatcher<SunLightToolEvents> {\n private scene: THREE.Scene\n private light: THREE.DirectionalLight\n private ambientLight?: THREE.Light\n private hemisphereLight?: THREE.HemisphereLight\n private helper?: THREE.DirectionalLightHelper\n private createdLight: boolean = false\n private createdHemisphere: boolean = false\n private latitude: number\n private longitude: number\n private dayOfYear: number\n private timeOfDay: number\n private timeZoneOffsetMinutes: number\n private referenceYear: number\n private weather: SunLightWeather\n private useSystemTime: boolean\n private lightDistance: number\n private shadowCameraSize: number\n private shadowCameraFar: number\n private state: SunLightState\n\n constructor(scene: THREE.Scene, options: SunLightToolOptions = {}) {\n super()\n this.scene = scene\n const defaultLatitude = options.latitude ?? 51.5072\n const defaultLongitude = options.longitude ?? -0.1276\n this.latitude = clamp(defaultLatitude, -90, 90)\n this.longitude = normalizeLongitude(defaultLongitude)\n this.referenceYear = options.referenceYear ?? new Date().getUTCFullYear()\n this.dayOfYear = limitDayOfYear(\n options.dayOfYear ?? 172,\n this.referenceYear\n )\n this.timeOfDay = normalizeHours(options.timeOfDay ?? 12)\n this.timeZoneOffsetMinutes = options.timeZoneOffsetMinutes ?? 0\n this.weather = options.weather ?? 'sunny'\n this.useSystemTime = options.useSystemTime ?? false\n this.lightDistance = options.lightDistance ?? 150\n this.shadowCameraSize = options.shadowCameraSize ?? 100\n this.shadowCameraFar = options.shadowCameraFar ?? 600\n\n const existingDirectional = options.light\n this.light =\n existingDirectional ?? new THREE.DirectionalLight(0xffffff, 1.0)\n this.createdLight = !existingDirectional\n\n if (!this.light.parent) {\n this.scene.add(this.light)\n }\n\n if (!this.light.target.parent) {\n this.scene.add(this.light.target)\n }\n\n this.configureShadowMap(this.light)\n\n if (options.ambientLight) {\n this.ambientLight = options.ambientLight\n }\n\n if (options.enableHemisphereLight ?? true) {\n this.hemisphereLight = new THREE.HemisphereLight(\n options.hemisphereSkyColor ?? 0xbad4ff,\n options.hemisphereGroundColor ?? 0x74675a,\n options.hemisphereIntensity ?? 0.45\n )\n this.scene.add(this.hemisphereLight)\n this.createdHemisphere = true\n }\n\n if (options.showHelper) {\n this.helper = new THREE.DirectionalLightHelper(\n this.light,\n options.helperSize ?? 25,\n options.helperColor ?? 0xffd27f\n )\n this.scene.add(this.helper)\n }\n\n this.state = {\n date: new Date(),\n latitude: this.latitude,\n longitude: this.longitude,\n solarAzimuth: 0,\n solarAltitude: 0,\n weather: this.weather,\n useSystemTime: this.useSystemTime,\n }\n\n this.update()\n }\n\n getLight(): THREE.DirectionalLight {\n return this.light\n }\n\n getHemisphereLight(): THREE.HemisphereLight | undefined {\n return this.hemisphereLight\n }\n\n getState(): SunLightState {\n return { ...this.state, date: new Date(this.state.date.getTime()) }\n }\n\n getDayOfYear(): number {\n return this.dayOfYear\n }\n\n getTimeOfDay(): number {\n return this.timeOfDay\n }\n\n getTimeZoneOffset(): number {\n return this.timeZoneOffsetMinutes\n }\n\n getWeather(): SunLightWeather {\n return this.weather\n }\n\n usesSystemTime(): boolean {\n return this.useSystemTime\n }\n\n setLatitude(latitude: number): void {\n const clamped = clamp(latitude, -90, 90)\n if (clamped === this.latitude) return\n this.latitude = clamped\n this.update()\n }\n\n setLongitude(longitude: number): void {\n const normalized = normalizeLongitude(longitude)\n if (normalized === this.longitude) return\n this.longitude = normalized\n this.update()\n }\n\n setLocation(latitude: number, longitude: number): void {\n let changed = false\n const lat = clamp(latitude, -90, 90)\n if (lat !== this.latitude) {\n this.latitude = lat\n changed = true\n }\n const lon = normalizeLongitude(longitude)\n if (lon !== this.longitude) {\n this.longitude = lon\n changed = true\n }\n if (changed) {\n this.update()\n }\n }\n\n setDayOfYear(day: number): void {\n const limited = limitDayOfYear(day, this.referenceYear)\n if (limited === this.dayOfYear) return\n this.dayOfYear = limited\n this.update()\n }\n\n setTimeOfDay(hours: number): void {\n const normalized = normalizeHours(hours)\n if (normalized === this.timeOfDay) return\n this.timeOfDay = normalized\n this.update()\n }\n\n setTimeZoneOffset(minutes: number): void {\n if (minutes === this.timeZoneOffsetMinutes) return\n this.timeZoneOffsetMinutes = minutes\n this.update()\n }\n\n setWeather(weather: SunLightWeather): void {\n if (weather === this.weather) return\n this.weather = weather\n this.dispatchEvent({ type: 'weatherChanged', weather })\n this.update()\n }\n\n setUseSystemTime(enabled: boolean): void {\n if (enabled === this.useSystemTime) return\n this.useSystemTime = enabled\n this.dispatchEvent({\n type: 'systemTimeToggled',\n useSystemTime: this.useSystemTime,\n })\n this.update()\n }\n\n update(dateOverride?: Date): void {\n const date = this.computeDate(dateOverride)\n const position = this.computeSunPosition(date)\n this.applySunPosition(position, date)\n this.state = {\n date,\n latitude: this.latitude,\n longitude: this.longitude,\n solarAzimuth: position.azimuth,\n solarAltitude: position.altitude,\n weather: this.weather,\n useSystemTime: this.useSystemTime,\n }\n this.applyWeather(position.altitude)\n this.dispatchEvent({ type: 'stateChanged', state: this.getState() })\n }\n\n dispose(): void {\n if (this.helper) {\n this.scene.remove(this.helper)\n this.helper.dispose()\n this.helper = undefined\n }\n\n if (this.createdHemisphere && this.hemisphereLight) {\n this.scene.remove(this.hemisphereLight)\n this.hemisphereLight.dispose()\n this.hemisphereLight = undefined\n }\n\n if (this.createdLight && this.light) {\n this.scene.remove(this.light)\n if (this.light.shadow?.map) {\n this.light.shadow.map.dispose()\n }\n this.light.dispose()\n }\n }\n\n private configureShadowMap(light: THREE.DirectionalLight): void {\n light.castShadow = true\n const camera = light.shadow.camera\n if (\n camera instanceof THREE.OrthographicCamera &&\n typeof this.shadowCameraSize === 'number'\n ) {\n const size = this.shadowCameraSize\n camera.left = -size\n camera.right = size\n camera.top = size\n camera.bottom = -size\n camera.near = 0.5\n camera.far = this.shadowCameraFar\n camera.updateProjectionMatrix()\n }\n light.shadow.mapSize.set(2048, 2048)\n light.shadow.bias = -0.0005\n }\n\n private computeDate(dateOverride?: Date): Date {\n if (dateOverride) {\n return new Date(dateOverride.getTime())\n }\n\n if (this.useSystemTime) {\n const now = new Date()\n this.referenceYear = now.getUTCFullYear()\n this.dayOfYear = computeDayOfYear(now)\n this.timeOfDay =\n now.getUTCHours() +\n now.getUTCMinutes() / 60 +\n now.getUTCSeconds() / 3600\n return now\n }\n\n const daysInYear = isLeapYear(this.referenceYear) ? 366 : 365\n const dayIndex = clamp(this.dayOfYear, 1, daysInYear)\n const date = new Date(Date.UTC(this.referenceYear, 0, 1, 0, 0, 0, 0))\n const minutesFromYearStart =\n (dayIndex - 1) * 24 * 60 +\n this.timeOfDay * 60 -\n this.timeZoneOffsetMinutes\n date.setUTCMinutes(minutesFromYearStart)\n return date\n }\n\n private computeSunPosition(date: Date): {\n azimuth: number\n altitude: number\n } {\n const days = toDays(date)\n const lw = -this.longitude * DEG2RAD\n const phi = this.latitude * DEG2RAD\n const meanAnomaly = solarMeanAnomaly(days)\n const longitude = eclipticLongitude(meanAnomaly)\n const decl = declination(longitude)\n const ra = rightAscension(longitude)\n const sidereal = siderealTime(days, lw)\n const hourAngle = sidereal - ra\n return {\n azimuth: solarAzimuth(hourAngle, phi, decl),\n altitude: solarAltitude(hourAngle, phi, decl),\n }\n }\n\n private applySunPosition(\n position: { azimuth: number; altitude: number },\n date: Date\n ): void {\n const radius = this.lightDistance\n const altitude = position.altitude\n // Astronomical azimuth: 0° = South, increasing clockwise (East=90°, North=180°, West=270°)\n // Three.js coordinates: +X = East, +Z = North, -X = West, -Z = South\n // The solarAzimuth function returns azimuth measured from South, clockwise\n const azimuth = position.azimuth\n\n // Correct coordinate mapping for Three.js\n // sin(azimuth) gives us the East-West component, but we need to negate it:\n // azimuth 90° (East) should give +X, azimuth 270° (West) should give -X\n // cos(azimuth) gives us the North-South component, also needs to be positive for correct clockwise motion:\n // azimuth 0° (South) should give -Z, azimuth 180° (North) should give +Z\n const x = -radius * Math.cos(altitude) * Math.sin(azimuth)\n const y = radius * Math.sin(altitude)\n const z = radius * Math.cos(altitude) * Math.cos(azimuth)\n\n this.light.position.set(x, y, z)\n this.light.target.position.set(0, 0, 0)\n this.light.target.updateMatrixWorld()\n\n if (this.helper) {\n this.helper.update()\n }\n\n // Update cached time properties when not using real-time\n if (!this.useSystemTime) {\n this.referenceYear = date.getUTCFullYear()\n }\n }\n\n private applyWeather(solarAltitude: number): void {\n const preset = buildWeatherPreset(this.weather, solarAltitude)\n const altitudeDeg = solarAltitude * RAD2DEG\n const twilightStartDeg = -6\n const daylightDeg = 4\n const daylightRange = daylightDeg - twilightStartDeg\n\n const daylightFactor = clamp(\n (altitudeDeg - twilightStartDeg) / daylightRange,\n 0,\n 1\n )\n const twilightFactor = 1 - daylightFactor\n\n const sunColor = preset.sunColor.lerp(TWILIGHT_SUN_COLOR, twilightFactor)\n const ambientColor = preset.ambientColor.lerp(\n TWILIGHT_AMBIENT_COLOR,\n twilightFactor\n )\n const skyColor = preset.hemisphereSkyColor.lerp(\n TWILIGHT_SKY_COLOR,\n twilightFactor\n )\n const groundColor = preset.hemisphereGroundColor.lerp(\n TWILIGHT_GROUND_COLOR,\n twilightFactor\n )\n\n const adjustedSunIntensity = preset.sunIntensity * daylightFactor\n const adjustedAmbientIntensity = Math.max(\n 0.05,\n preset.ambientIntensity * (0.3 + 0.7 * daylightFactor)\n )\n const adjustedHemisphereIntensity = Math.max(\n 0.05,\n preset.hemisphereIntensity * (0.35 + 0.65 * daylightFactor)\n )\n\n this.light.intensity = adjustedSunIntensity\n this.light.visible = adjustedSunIntensity > 0.001\n this.light.color.copy(sunColor)\n this.light.shadow.bias = preset.shadowBias\n\n if (this.ambientLight) {\n this.ambientLight.intensity = adjustedAmbientIntensity\n this.ambientLight.color.copy(ambientColor)\n }\n\n if (this.hemisphereLight) {\n this.hemisphereLight.intensity = adjustedHemisphereIntensity\n this.hemisphereLight.color.copy(skyColor)\n this.hemisphereLight.groundColor.copy(groundColor)\n }\n }\n}\n"],"mappings":"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","names":["solarAltitude"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/compass/src/CompassOverlay.ts"],"sourcesContent":["import * as THREE from 'three'\n\nexport interface CompassOverlayOptions {\n container?: HTMLElement\n size?: number\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n offset?: { x: number; y: number }\n colors?: {\n background: string\n border: string\n arrow: string\n text: string\n ticks: string\n }\n}\n\n// Define event types for the compass\ninterface CompassOverlayEventMap {\n resetToNorth: {}\n}\n\nexport interface CompassOverlayEvent {\n type: 'resetToNorth'\n}\n\nexport class CompassOverlay extends THREE.EventDispatcher<CompassOverlayEventMap> {\n private camera: THREE.Camera\n private container: HTMLElement\n private compassElement!: HTMLElement\n private arrowElement!: HTMLElement\n private isActive: boolean = false\n private options: Required<CompassOverlayOptions>\n private currentRotation: number = 0\n\n constructor(camera: THREE.Camera, options: CompassOverlayOptions = {}) {\n super()\n this.camera = camera\n\n // Set default options\n this.options = {\n container: options.container || document.body,\n size: options.size || 100,\n position: options.position || 'bottom-right',\n offset: options.offset || { x: 20, y: 20 },\n colors: {\n background: options.colors?.background || '#1a1a1a',\n border: options.colors?.border || '#333333',\n arrow: options.colors?.arrow || '#ff4444',\n text: options.colors?.text || '#ffffff',\n ticks: options.colors?.ticks || '#666666',\n ...options.colors,\n },\n }\n\n this.container = this.options.container\n this.createCompassElement()\n this.setupStyles()\n }\n\n private createCompassElement(): void {\n // Create main compass container\n this.compassElement = document.createElement('div')\n this.compassElement.className = 'threejs-compass-overlay'\n\n // Create compass background with ticks\n const compassBg = document.createElement('div')\n compassBg.className = 'compass-background'\n\n // Create cardinal direction labels\n const directions = [\n { label: 'N', angle: 0 },\n { label: 'E', angle: 90 },\n { label: 'S', angle: 180 },\n { label: 'W', angle: 270 },\n ]\n\n // Create tick marks\n for (let i = 0; i < 360; i += 11.25) {\n const tick = document.createElement('div')\n tick.className =\n i % 45 === 0 ? 'compass-tick-major' : 'compass-tick-minor'\n const tickLength = i % 30 === 0 ? 8 : 4\n tick.style.transform = `rotate(${i}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n compassBg.appendChild(tick)\n }\n\n directions.forEach(({ label, angle }) => {\n const dirLabel = document.createElement('div')\n dirLabel.className = 'compass-label'\n dirLabel.textContent = label\n dirLabel.style.transform = `rotate(${angle}deg) translateY(-${this.options.size / 2 - 15}px) rotate(-${angle}deg)`\n compassBg.appendChild(dirLabel)\n })\n\n // Create arrow element\n this.arrowElement = document.createElement('div')\n this.arrowElement.className = 'compass-arrow'\n\n // Arrow SVG\n this.arrowElement.innerHTML = `\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <polygon points=\"10,2 14,12 10,10 6,12\" fill=\"${this.options.colors.arrow}\" stroke=\"#000\" stroke-width=\"0.5\"/>\n </svg>\n `\n\n this.compassElement.appendChild(compassBg)\n this.compassElement.appendChild(this.arrowElement)\n this.container.appendChild(this.compassElement)\n\n // Add double-click event to reset camera to north\n this.setupDoubleClickHandler()\n }\n\n private setupStyles(): void {\n // Check if styles already exist\n if (document.getElementById('threejs-compass-styles')) {\n return\n }\n\n const style = document.createElement('style')\n style.id = 'threejs-compass-styles'\n style.textContent = `\n .threejs-compass-overlay {\n position: fixed;\n width: ${this.options.size}px;\n height: ${this.options.size}px;\n background: ${this.options.colors.background};\n border: 2px solid ${this.options.colors.border};\n border-radius: 50%;\n z-index: 1000;\n pointer-events: none;\n user-select: none;\n transition: opacity 0.2s ease;\n ${this.getPositionStyles()};\n }\n\n .compass-background {\n position: relative;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n }\n\n .compass-label {\n position: absolute;\n top: 50%;\n left: 50%;\n transform-origin: center;\n color: ${this.options.colors.text};\n font-family: Arial, sans-serif;\n font-size: 12px;\n font-weight: bold;\n text-align: center;\n width: 12px;\n height: 12px;\n line-height: 12px;\n margin-left: -6px;\n margin-top: -6px;\n }\n\n .compass-tick-major {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 2px;\n height: 8px;\n background: ${this.options.colors.ticks};\n transform-origin: center top;\n margin-left: -1px;\n margin-top: -4px;\n }\n\n .compass-tick-minor {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 1px;\n height: 4px;\n background: ${this.options.colors.ticks};\n transform-origin: center top;\n margin-left: -0.5px;\n margin-top: -2px;\n opacity: 0.6;\n }\n\n .compass-arrow {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: transform 0.1s ease-out;\n z-index: 1;\n }\n\n .compass-arrow svg {\n display: block;\n filter: drop-shadow(0 0 2px rgba(0,0,0,0.5));\n }\n `\n\n document.head.appendChild(style)\n }\n\n private setupDoubleClickHandler(): void {\n // Enable pointer events for interaction\n this.compassElement.style.pointerEvents = 'auto'\n this.compassElement.style.cursor = 'pointer'\n\n // Add double-click event listener\n this.compassElement.addEventListener('dblclick', (event) => {\n event.preventDefault()\n event.stopPropagation()\n\n // Dispatch custom event\n this.dispatchEvent({ type: 'resetToNorth' })\n })\n\n // Add visual feedback on hover\n this.compassElement.addEventListener('mouseenter', () => {\n this.compassElement.style.opacity = '0.8'\n })\n\n this.compassElement.addEventListener('mouseleave', () => {\n this.compassElement.style.opacity = '1'\n })\n }\n\n private updateTicksAndLabels(): void {\n // Update compass labels\n\n // Update tick marks\n const majorTicks = this.compassElement.querySelectorAll(\n '.compass-tick-major'\n )\n const minorTicks = this.compassElement.querySelectorAll(\n '.compass-tick-minor'\n )\n\n majorTicks.forEach((tick, index) => {\n const angle = (index * 30) % 360 // Major ticks every 30 degrees\n const tickLength = 8\n ;(tick as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n })\n\n minorTicks.forEach((tick, index) => {\n // Minor ticks are at angles that aren't multiples of 30\n const allAngles = []\n for (let i = 0; i < 360; i += 10) {\n if (i % 30 !== 0) {\n // Skip major tick positions\n allAngles.push(i)\n }\n }\n const angle = allAngles[index] || 0\n const tickLength = 4\n ;(tick as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n })\n\n const labels = this.compassElement.querySelectorAll('.compass-label')\n labels.forEach((label, index) => {\n const directions = [\n { label: 'N', angle: 0 },\n { label: 'E', angle: 90 },\n { label: 'S', angle: 180 },\n { label: 'W', angle: 270 },\n ]\n const { angle } = directions[index]\n ;(label as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - 15}px) rotate(-${angle}deg)`\n })\n }\n\n private getPositionStyles(): string {\n const { position, offset } = this.options\n\n switch (position) {\n case 'top-left':\n return `top: ${offset.y}px; left: ${offset.x}px;`\n case 'top-right':\n return `top: ${offset.y}px; right: ${offset.x}px;`\n case 'bottom-left':\n return `bottom: ${offset.y}px; left: ${offset.x}px;`\n case 'bottom-right':\n default:\n return `bottom: ${offset.y}px; right: ${offset.x}px;`\n }\n }\n\n public start(): void {\n if (this.isActive) return\n this.isActive = true\n this.update()\n }\n\n public stop(): void {\n this.isActive = false\n }\n\n public update(): void {\n if (!this.isActive) return\n\n // Get the camera's world matrix\n const cameraMatrix = new THREE.Matrix4()\n this.camera.updateMatrixWorld()\n cameraMatrix.copy(this.camera.matrixWorld)\n\n // Extract the camera's forward direction (negative Z in camera space)\n const forward = new THREE.Vector3(0, 0, -1)\n forward.transformDirection(cameraMatrix)\n\n // Project to XZ plane (ignore Y component for top-down compass view)\n forward.y = 0\n forward.normalize()\n\n // Calculate angle from world -Z axis (north)\n // World Z- is \"north\", X- is \"east\" (flipped X axis)\n const targetAngle = Math.atan2(-forward.x, -forward.z) * (180 / Math.PI)\n\n // Calculate the shortest rotation path to avoid spinning around\n let angleDiff = targetAngle - this.currentRotation\n\n // Normalize the angle difference to be between -180 and 180\n while (angleDiff > 180) angleDiff -= 360\n while (angleDiff < -180) angleDiff += 360\n\n // Update current rotation with smooth transition\n this.currentRotation += angleDiff\n\n // Update arrow rotation (negative because we want the arrow to point to north, not where camera is facing)\n this.arrowElement.style.transform = `translate(-50%, -50%) rotate(${-this.currentRotation}deg)`\n\n // Continue updating if active\n if (this.isActive) {\n requestAnimationFrame(() => this.update())\n }\n }\n\n public setCamera(camera: THREE.Camera): void {\n this.camera = camera\n }\n\n public setSize(size: number): void {\n this.options.size = size\n this.compassElement.style.width = `${size}px`\n this.compassElement.style.height = `${size}px`\n this.updateTicksAndLabels()\n this.updateStyles()\n }\n\n public setPosition(\n position: CompassOverlayOptions['position'],\n offset?: { x: number; y: number }\n ): void {\n if (position) {\n this.options.position = position\n }\n if (offset) {\n this.options.offset = offset\n }\n\n // Update position styles\n const positionStyles = this.getPositionStyles()\n const styles = positionStyles.split(';').filter((s) => s.trim())\n\n // Clear existing position styles\n this.compassElement.style.top = ''\n this.compassElement.style.right = ''\n this.compassElement.style.bottom = ''\n this.compassElement.style.left = ''\n\n // Apply new position styles\n styles.forEach((style) => {\n const [property, value] = style.split(':').map((s) => s.trim())\n if (property && value) {\n ;(this.compassElement.style as any)[property] = value\n }\n })\n }\n\n public setColors(colors: Partial<CompassOverlayOptions['colors']>): void {\n this.options.colors = { ...this.options.colors, ...colors }\n this.updateStyles()\n }\n\n /**\n * Helper method to reset camera to look north (world Z- direction)\n * This is a convenience method that can be called when handling the 'resetToNorth' event\n */\n public static resetCameraToNorth(\n camera: THREE.Camera,\n smooth: boolean = true\n ): void {\n if (smooth) {\n // Create a smooth rotation animation to north\n const startQuaternion = camera.quaternion.clone()\n const targetQuaternion = new THREE.Quaternion()\n\n // Set target rotation to look at north (Z-)\n const targetMatrix = new THREE.Matrix4()\n targetMatrix.lookAt(\n camera.position,\n camera.position.clone().add(new THREE.Vector3(0, 0, -1)),\n new THREE.Vector3(0, 1, 0)\n )\n targetQuaternion.setFromRotationMatrix(targetMatrix)\n\n // Animate rotation\n const duration = 500 // milliseconds\n const startTime = Date.now()\n\n const animate = () => {\n const elapsed = Date.now() - startTime\n const progress = Math.min(elapsed / duration, 1)\n\n // Use smooth easing\n const easeProgress = 1 - Math.pow(1 - progress, 3)\n\n camera.quaternion.slerpQuaternions(\n startQuaternion,\n targetQuaternion,\n easeProgress\n )\n\n if (progress < 1) {\n requestAnimationFrame(animate)\n }\n }\n animate()\n } else {\n // Instant rotation to north\n camera.lookAt(camera.position.clone().add(new THREE.Vector3(0, 0, -1)))\n }\n }\n\n private updateStyles(): void {\n // Remove existing styles\n const existingStyle = document.getElementById('threejs-compass-styles')\n if (existingStyle) {\n existingStyle.remove()\n }\n\n // Recreate styles with new values\n this.setupStyles()\n }\n\n public dispose(): void {\n this.stop()\n\n if (this.compassElement && this.compassElement.parentNode) {\n this.compassElement.parentNode.removeChild(this.compassElement)\n }\n\n // Remove styles if no other compass instances exist\n const compassElements = document.querySelectorAll(\n '.threejs-compass-overlay'\n )\n if (compassElements.length === 0) {\n const style = document.getElementById('threejs-compass-styles')\n if (style) {\n style.remove()\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/view-helper/src/ViewHelper.ts"],"sourcesContent":["import * as THREE from 'three'\n\n// Hard-coded axis colors matching TransformControls\nconst AXIS_COLORS = {\n x: '#ed4358',\n y: '#82cc19',\n z: '#3185eb',\n} as const\n\nexport interface ViewHelperCameraController {\n camera: THREE.Camera\n getPosition(target: THREE.Vector3): THREE.Vector3\n getTarget(target: THREE.Vector3): THREE.Vector3\n setLookAt(\n positionX: number,\n positionY: number,\n positionZ: number,\n targetX: number,\n targetY: number,\n targetZ: number,\n enableTransition?: boolean\n ): Promise<void> | void\n stop?(): void\n enabled: boolean\n}\n\nexport interface ViewHelperOptions {\n container?: HTMLElement\n size?: number\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n offset?: { x: number; y: number }\n center?: THREE.Vector3\n labels?: {\n x?: string\n y?: string\n z?: string\n }\n controls?: ViewHelperCameraController\n}\n\n// Define event types for the view helper\ninterface ViewHelperEventMap {\n animationStart: {}\n animationEnd: {}\n}\n\nexport interface ViewHelperEvent {\n type: 'animationStart' | 'animationEnd'\n}\n\nexport class ViewHelper extends THREE.EventDispatcher<ViewHelperEventMap> {\n private camera: THREE.Camera\n private domElement: HTMLElement\n private options: Required<Omit<ViewHelperOptions, 'controls'>>\n private scene: THREE.Scene\n private orthoCamera: THREE.OrthographicCamera\n private renderer?: THREE.WebGLRenderer\n private viewport: THREE.Vector4 = new THREE.Vector4()\n private controls?: ViewHelperCameraController\n private pointerDownHandler!: (event: PointerEvent) => void\n\n // Animation properties\n public animating: boolean = false\n public center: THREE.Vector3\n private targetPosition: THREE.Vector3 = new THREE.Vector3()\n private targetQuaternion: THREE.Quaternion = new THREE.Quaternion()\n private q1: THREE.Quaternion = new THREE.Quaternion()\n private q2: THREE.Quaternion = new THREE.Quaternion()\n private dummy: THREE.Object3D = new THREE.Object3D()\n private radius: number = 0\n private turnRate: number = 2 * Math.PI // turn rate in angles per second\n private tempVecA: THREE.Vector3 = new THREE.Vector3()\n\n // Interactive elements\n private interactiveObjects: THREE.Object3D[] = []\n private raycaster: THREE.Raycaster = new THREE.Raycaster()\n private mouse: THREE.Vector2 = new THREE.Vector2()\n\n // Helper objects\n private axes!: {\n x: THREE.Mesh\n y: THREE.Mesh\n z: THREE.Mesh\n }\n private sprites!: {\n posX: THREE.Sprite\n posY: THREE.Sprite\n posZ: THREE.Sprite\n negX: THREE.Sprite\n negY: THREE.Sprite\n negZ: THREE.Sprite\n }\n\n constructor(\n camera: THREE.Camera,\n domElement?: HTMLElement,\n options: ViewHelperOptions = {}\n ) {\n super()\n this.controls = options.controls\n this.camera = this.controls?.camera ?? camera\n this.domElement = domElement || document.body\n\n const defaultCenter =\n options.center ??\n (this.controls\n ? this.controls.getTarget(new THREE.Vector3())\n : new THREE.Vector3())\n\n // Set default options\n this.options = {\n container: options.container || document.body,\n size: options.size || 128,\n position: options.position || 'bottom-right',\n offset: options.offset || { x: 20, y: 20 },\n center: defaultCenter.clone(),\n labels: {\n x: options.labels?.x || 'x',\n y: options.labels?.y || 'y',\n z: options.labels?.z || 'z',\n ...options.labels,\n },\n }\n\n this.center = this.options.center.clone()\n\n this.scene = new THREE.Scene()\n this.scene.background = null // Make background transparent\n this.orthoCamera = new THREE.OrthographicCamera(-2, 2, 2, -2, 0, 4)\n this.orthoCamera.position.set(0, 0, 2)\n\n this.createAxes()\n this.createSprites()\n this.setupEventListeners()\n }\n\n private syncActiveCamera(): THREE.Camera {\n if (this.controls) {\n this.camera = this.controls.camera\n }\n return this.camera\n }\n\n private getCameraPosition(target: THREE.Vector3): THREE.Vector3 {\n if (this.controls) {\n return this.controls.getPosition(target)\n }\n return target.copy(this.camera.position)\n }\n\n private createAxes(): void {\n const geometry = new THREE.CylinderGeometry(0.04, 0.04, 0.8, 5)\n .rotateZ(-Math.PI / 2)\n .translate(0.4, 0, 0)\n\n const xAxisMaterial = new THREE.MeshBasicMaterial({\n color: AXIS_COLORS.x,\n toneMapped: false,\n })\n const yAxisMaterial = new THREE.MeshBasicMaterial({\n color: AXIS_COLORS.y,\n toneMapped: false,\n })\n const zAxisMaterial = new THREE.MeshBasicMaterial({\n color: AXIS_COLORS.z,\n toneMapped: false,\n })\n\n this.axes = {\n x: new THREE.Mesh(geometry.clone(), xAxisMaterial),\n y: new THREE.Mesh(geometry.clone(), yAxisMaterial),\n z: new THREE.Mesh(geometry.clone(), zAxisMaterial),\n }\n\n this.axes.y.rotation.z = Math.PI / 2\n this.axes.z.rotation.y = -Math.PI / 2\n\n this.scene.add(this.axes.x)\n this.scene.add(this.axes.y)\n this.scene.add(this.axes.z)\n }\n\n private createSprites(): void {\n const posXSprite = this.createSprite(AXIS_COLORS.x, this.options.labels.x)\n const posYSprite = this.createSprite(AXIS_COLORS.y, this.options.labels.y)\n const posZSprite = this.createSprite(AXIS_COLORS.z, this.options.labels.z)\n // Use faded versions of the axis colors for negative axes\n const negXSprite = this.createSprite(AXIS_COLORS.x)\n const negYSprite = this.createSprite(AXIS_COLORS.y)\n const negZSprite = this.createSprite(AXIS_COLORS.z)\n\n this.sprites = {\n posX: posXSprite,\n posY: posYSprite,\n posZ: posZSprite,\n negX: negXSprite,\n negY: negYSprite,\n negZ: negZSprite,\n }\n\n // Position sprites\n this.sprites.posX.position.set(1, 0, 0)\n this.sprites.posY.position.set(0, 1, 0)\n this.sprites.posZ.position.set(0, 0, 1)\n this.sprites.negX.position.set(-1, 0, 0)\n this.sprites.negY.position.set(0, -1, 0)\n this.sprites.negZ.position.set(0, 0, -1)\n\n // Set opacity for negative axes\n this.sprites.negX.material.opacity = 0.2\n this.sprites.negY.material.opacity = 0.2\n this.sprites.negZ.material.opacity = 0.2\n\n // Set user data for interaction\n this.sprites.posX.userData.type = 'posX'\n this.sprites.posY.userData.type = 'posY'\n this.sprites.posZ.userData.type = 'posZ'\n this.sprites.negX.userData.type = 'negX'\n this.sprites.negY.userData.type = 'negY'\n this.sprites.negZ.userData.type = 'negZ'\n\n // Add to scene and interactive objects\n Object.values(this.sprites).forEach((sprite) => {\n this.scene.add(sprite)\n this.interactiveObjects.push(sprite)\n })\n }\n\n private createSprite(color: string, text?: string): THREE.Sprite {\n // Use higher resolution for crisp rendering\n const pixelRatio = window.devicePixelRatio || 1\n const size = 128 * pixelRatio\n const radius = 28 * pixelRatio\n\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n\n const context = canvas.getContext('2d')!\n\n // Enable high-quality rendering\n context.imageSmoothingEnabled = true\n context.imageSmoothingQuality = 'high'\n\n // Scale for device pixel ratio\n context.scale(pixelRatio, pixelRatio)\n\n const center = size / (2 * pixelRatio)\n const actualRadius = radius / pixelRatio\n\n // Draw circle background\n context.beginPath()\n context.arc(center, center, actualRadius, 0, 2 * Math.PI)\n context.closePath()\n context.fillStyle = color\n context.fill()\n\n if (text) {\n context.font = 'bold 48px Arial, sans-serif'\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillStyle = '#ffffff'\n\n // Add text stroke for better visibility\n context.strokeStyle = '#000000'\n context.lineWidth = 2\n context.strokeText(text, center, center)\n context.fillText(text, center, center)\n }\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.colorSpace = THREE.SRGBColorSpace\n texture.generateMipmaps = false\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n\n return new THREE.Sprite(\n new THREE.SpriteMaterial({\n map: texture,\n toneMapped: false,\n alphaTest: 0.1,\n })\n )\n }\n\n private setupEventListeners(): void {\n this.pointerDownHandler = (event: PointerEvent) => {\n this.handleClick(event)\n }\n this.domElement.addEventListener('pointerdown', this.pointerDownHandler)\n }\n\n public render(renderer: THREE.WebGLRenderer): void {\n this.renderer = renderer\n\n const activeCamera = this.syncActiveCamera()\n\n // Update helper orientation to match camera\n this.scene.quaternion.copy(activeCamera.quaternion).invert()\n this.scene.updateMatrixWorld()\n\n const size = this.options.size\n const canvasWidth = renderer.domElement.width / renderer.getPixelRatio()\n const canvasHeight = renderer.domElement.height / renderer.getPixelRatio()\n\n let x: number, y: number\n\n switch (this.options.position) {\n case 'top-left':\n x = this.options.offset.x\n y = canvasHeight - size - this.options.offset.y\n break\n case 'top-right':\n x = canvasWidth - size - this.options.offset.x\n y = canvasHeight - size - this.options.offset.y\n break\n case 'bottom-left':\n x = this.options.offset.x\n y = this.options.offset.y\n break\n case 'bottom-right':\n default:\n x = canvasWidth - size - this.options.offset.x\n y = this.options.offset.y\n break\n }\n\n // Store current viewport and autoClear setting\n renderer.getViewport(this.viewport)\n const autoClear = renderer.autoClear\n\n // Set viewport for helper and disable autoClear\n renderer.setViewport(x, y, size, size)\n renderer.autoClear = false\n\n // Clear only depth buffer in the helper area\n renderer.setScissorTest(true)\n renderer.setScissor(x, y, size, size)\n renderer.clearDepth()\n\n // Render helper\n renderer.render(this.scene, this.orthoCamera)\n // Restore settings\n renderer.setScissorTest(false)\n renderer.autoClear = autoClear\n renderer.setViewport(\n this.viewport.x,\n this.viewport.y,\n this.viewport.z,\n this.viewport.w\n )\n }\n\n public handleClick(event: PointerEvent): boolean {\n if (this.animating || !this.renderer) return false\n\n this.syncActiveCamera()\n\n const rect = this.domElement.getBoundingClientRect()\n const size = this.options.size // Use logical size for click detection\n\n // Calculate helper viewport bounds\n let offsetX: number, offsetY: number\n\n switch (this.options.position) {\n case 'top-left':\n offsetX = rect.left + this.options.offset.x\n offsetY = rect.top + this.options.offset.y\n break\n case 'top-right':\n offsetX = rect.left + rect.width - size - this.options.offset.x\n offsetY = rect.top + this.options.offset.y\n break\n case 'bottom-left':\n offsetX = rect.left + this.options.offset.x\n offsetY = rect.top + rect.height - size - this.options.offset.y\n break\n case 'bottom-right':\n default:\n offsetX = rect.left + rect.width - size - this.options.offset.x\n offsetY = rect.top + rect.height - size - this.options.offset.y\n break\n }\n\n // Convert click coordinates to helper-relative coordinates\n this.mouse.x = ((event.clientX - offsetX) / size) * 2 - 1\n this.mouse.y = -((event.clientY - offsetY) / size) * 2 + 1\n\n // Check if click is within helper bounds\n if (\n this.mouse.x < -1 ||\n this.mouse.x > 1 ||\n this.mouse.y < -1 ||\n this.mouse.y > 1\n ) {\n return false\n }\n\n this.raycaster.setFromCamera(this.mouse, this.orthoCamera)\n const intersects = this.raycaster.intersectObjects(this.interactiveObjects)\n\n if (intersects.length > 0) {\n const intersection = intersects[0]\n const object = intersection.object\n this.prepareAnimationData(object, this.center)\n if (this.controls) {\n this.startControlsAnimation(this.center)\n } else {\n this.animating = true\n this.dispatchEvent({ type: 'animationStart' })\n }\n return true\n }\n\n return false\n }\n\n private prepareAnimationData(\n object: THREE.Object3D,\n focusPoint: THREE.Vector3\n ): void {\n switch (object.userData.type) {\n case 'posX':\n this.targetPosition.set(1, 0, 0)\n this.targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI * 0.5, 0))\n break\n case 'posY':\n this.targetPosition.set(0, 1, 0)\n this.targetQuaternion.setFromEuler(\n new THREE.Euler(-Math.PI * 0.5, 0, 0)\n )\n break\n case 'posZ':\n this.targetPosition.set(0, 0, 1)\n this.targetQuaternion.setFromEuler(new THREE.Euler())\n break\n case 'negX':\n this.targetPosition.set(-1, 0, 0)\n this.targetQuaternion.setFromEuler(\n new THREE.Euler(0, -Math.PI * 0.5, 0)\n )\n break\n case 'negY':\n this.targetPosition.set(0, -1, 0)\n this.targetQuaternion.setFromEuler(new THREE.Euler(Math.PI * 0.5, 0, 0))\n break\n case 'negZ':\n this.targetPosition.set(0, 0, -1)\n this.targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI, 0))\n break\n default:\n console.error('ViewHelper: Invalid axis.')\n return\n }\n\n const cameraPosition = this.getCameraPosition(this.tempVecA)\n this.radius = cameraPosition.distanceTo(focusPoint)\n this.targetPosition.multiplyScalar(this.radius).add(focusPoint)\n\n this.dummy.position.copy(focusPoint)\n this.dummy.lookAt(cameraPosition)\n this.q1.copy(this.dummy.quaternion)\n\n this.dummy.lookAt(this.targetPosition)\n this.q2.copy(this.dummy.quaternion)\n }\n\n private startControlsAnimation(focusPoint: THREE.Vector3): void {\n const controls = this.controls\n if (!controls) {\n return\n }\n controls.enabled = false\n controls.stop?.()\n this.animating = true\n this.dispatchEvent({ type: 'animationStart' })\n\n const applyLookAt = () => {\n controls.setLookAt(\n this.targetPosition.x,\n this.targetPosition.y,\n this.targetPosition.z,\n focusPoint.x,\n focusPoint.y,\n focusPoint.z,\n true\n )\n\n setTimeout(() => {\n controls.enabled = true\n }, 100)\n }\n try {\n void Promise.resolve(applyLookAt())\n .catch((error) => {\n console.warn('ViewHelper: Unable to set camera look-at.', error)\n })\n .finally(() => {\n this.animating = false\n this.dispatchEvent({ type: 'animationEnd' })\n })\n } catch (error) {\n console.warn('ViewHelper: Unable to set camera look-at.', error)\n this.animating = false\n this.dispatchEvent({ type: 'animationEnd' })\n }\n }\n\n public update(delta: number): void {\n this.syncActiveCamera()\n\n if (this.controls) {\n return\n }\n\n if (!this.animating) return\n\n const step = delta * this.turnRate\n\n // Animate position by doing a slerp and then scaling the position on the unit sphere\n this.q1.rotateTowards(this.q2, step)\n this.camera.position\n .set(0, 0, 1)\n .applyQuaternion(this.q1)\n .multiplyScalar(this.radius)\n .add(this.center)\n\n // Animate orientation\n this.camera.quaternion.rotateTowards(this.targetQuaternion, step)\n\n if (this.q1.angleTo(this.q2) === 0) {\n this.animating = false\n this.dispatchEvent({ type: 'animationEnd' })\n }\n }\n\n public setLabels(labelX?: string, labelY?: string, labelZ?: string): void {\n if (labelX !== undefined) this.options.labels.x = labelX\n if (labelY !== undefined) this.options.labels.y = labelY\n if (labelZ !== undefined) this.options.labels.z = labelZ\n\n this.updateLabels()\n }\n\n private updateLabels(): void {\n // Dispose old materials\n this.sprites.posX.material.map?.dispose()\n this.sprites.posY.material.map?.dispose()\n this.sprites.posZ.material.map?.dispose()\n this.sprites.posX.material.dispose()\n this.sprites.posY.material.dispose()\n this.sprites.posZ.material.dispose()\n\n // Create new sprites with updated labels\n const newPosX = this.createSprite(AXIS_COLORS.x, this.options.labels.x)\n const newPosY = this.createSprite(AXIS_COLORS.y, this.options.labels.y)\n const newPosZ = this.createSprite(AXIS_COLORS.z, this.options.labels.z)\n\n // Update sprites\n this.sprites.posX.material = newPosX.material\n this.sprites.posY.material = newPosY.material\n this.sprites.posZ.material = newPosZ.material\n }\n\n public dispose(): void {\n // Dispose geometries\n this.axes.x.geometry.dispose()\n this.axes.y.geometry.dispose()\n this.axes.z.geometry.dispose()\n\n // Dispose materials\n ;(this.axes.x.material as THREE.Material).dispose()\n ;(this.axes.y.material as THREE.Material).dispose()\n ;(this.axes.z.material as THREE.Material).dispose()\n\n // Dispose sprite materials and textures\n Object.values(this.sprites).forEach((sprite) => {\n sprite.material.map?.dispose()\n sprite.material.dispose()\n })\n\n // Remove event listeners\n if (this.pointerDownHandler) {\n this.domElement.removeEventListener(\n 'pointerdown',\n this.pointerDownHandler\n )\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/asset-loader/src/AssetLoader.ts"],"sourcesContent":["import * as THREE from 'three'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\nimport { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'\nimport { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'\nimport { USDLoader } from 'three/examples/jsm/loaders/USDLoader.js'\n\n// Event types for asset loading\ninterface AssetLoaderEventMap {\n progress: { loaded: number; total: number; percentage: number }\n loaded: { asset: THREE.Object3D }\n error: { error: Error }\n placeholderCreated: { placeholder: THREE.Object3D }\n lowResLoaded: { lowRes: THREE.Object3D }\n}\n\nexport type AssetType = 'gltf' | 'fbx' | 'obj' | 'usd' | 'usdz'\n\nexport interface AssetLoaderOptions {\n type: AssetType\n url: string\n size?: [number, number, number] // Optional size for placeholder\n lowResUrl?: string // Optional low-res model URL\n enableCaching?: boolean\n placeholderColor?: number\n placeholderOpacity?: number\n errorColor?: number // Color to use when loading fails\n errorOpacity?: number // Opacity to use when loading fails\n}\n\nexport class AssetLoader extends THREE.EventDispatcher<AssetLoaderEventMap> {\n private cache: Map<string, THREE.Object3D> = new Map()\n private gltfLoader: GLTFLoader\n private fbxLoader: FBXLoader\n private objLoader: OBJLoader\n private usdLoader: USDLoader\n private placeholder: THREE.Object3D | null = null\n private loadedAsset: THREE.Object3D | null = null\n private lowResAsset: THREE.Object3D | null = null\n private errorColor: number = 0xff4444\n private errorOpacity: number = 0.5\n\n constructor() {\n super()\n this.gltfLoader = new GLTFLoader()\n this.fbxLoader = new FBXLoader()\n this.objLoader = new OBJLoader()\n this.usdLoader = new USDLoader()\n }\n\n /**\n * Create a placeholder cube with shader effect\n */\n private createPlaceholder(\n size: [number, number, number],\n color: number = 0x4fc3f7,\n opacity: number = 0.3\n ): THREE.Object3D {\n const [width, height, depth] = size\n const geometry = new THREE.BoxGeometry(width, height, depth)\n\n // Custom shader material with fill-up effect\n const material = new THREE.ShaderMaterial({\n side: THREE.DoubleSide,\n depthWrite: false,\n blending: THREE.AdditiveBlending,\n transparent: true,\n uniforms: {\n color: { value: new THREE.Color(color) },\n opacity: { value: opacity },\n fillProgress: { value: 0.0 },\n time: { value: 0.0 },\n isError: { value: 0.0 },\n yMin: { value: -height / 2 },\n yMax: { value: height / 2 },\n },\n vertexShader: `\n varying vec3 vPosition;\n void main() {\n vPosition = position;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform vec3 color;\n uniform float opacity;\n uniform float fillProgress;\n uniform float time;\n uniform float isError;\n uniform float yMin;\n uniform float yMax;\n varying vec3 vPosition;\n \n void main() {\n float normalizedY = (vPosition.y - yMin) / (yMax - yMin); // Normalize based on actual geometry bounds\n float alpha = opacity;\n \n // Create fill-up effect\n if (normalizedY > fillProgress) {\n alpha *= 0.1; // Reduce opacity for unfilled parts\n }\n \n // Error state effects\n if (isError > 0.5) {\n // Add pulsing effect for error state\n float pulse = 0.5 + 0.5 * sin(time * 4.0);\n alpha *= (0.3 + 0.4 * pulse);\n \n // Add error pattern\n float stripe = sin(vPosition.y * 20.0 + time * 2.0);\n alpha *= (0.7 + 0.3 * step(0.0, stripe));\n }\n \n // Add edge glow\n vec3 viewDirection = normalize(cameraPosition - vPosition);\n float edgeIntensity = pow(1.0 - abs(dot(viewDirection, normalize(vPosition))), 2.0);\n \n vec3 finalColor = color + edgeIntensity * 0.5;\n gl_FragColor = vec4(finalColor, alpha);\n }\n `,\n })\n\n const mesh = new THREE.Mesh(geometry, material)\n this.positionAssetAtBottomCenter(mesh)\n return mesh\n }\n\n /**\n * Update placeholder fill progress based on loading progress\n */\n private updatePlaceholder(progress: number) {\n if (this.placeholder && this.placeholder instanceof THREE.Mesh) {\n const material = this.placeholder.material as THREE.ShaderMaterial\n if (material.uniforms && material.uniforms.fillProgress) {\n material.uniforms.fillProgress.value = progress\n }\n }\n }\n\n /**\n * Set placeholder to error state with configurable color and opacity\n */\n private setPlaceholderError() {\n if (this.placeholder && this.placeholder instanceof THREE.Mesh) {\n const material = this.placeholder.material as THREE.ShaderMaterial\n if (material.uniforms) {\n // Change to error color\n if (material.uniforms.color) {\n material.uniforms.color.value = new THREE.Color(this.errorColor)\n }\n // Set error opacity\n if (material.uniforms.opacity) {\n material.uniforms.opacity.value = this.errorOpacity\n }\n // Set fill progress to indicate failure\n if (material.uniforms.fillProgress) {\n material.uniforms.fillProgress.value = 0.0\n }\n // Enable error state\n if (material.uniforms.isError) {\n material.uniforms.isError.value = 1.0\n }\n }\n }\n }\n\n /**\n * Update placeholder animation time (call this in your render loop)\n */\n public updatePlaceholderAnimation(deltaTime: number) {\n if (this.placeholder && this.placeholder instanceof THREE.Mesh) {\n const material = this.placeholder.material as THREE.ShaderMaterial\n if (material.uniforms && material.uniforms.time) {\n material.uniforms.time.value += deltaTime\n }\n }\n }\n\n /**\n * Reposition an asset so that its bottom-center sits at the local origin.\n */\n private positionAssetAtBottomCenter(object: THREE.Object3D) {\n object.updateMatrixWorld(true)\n\n const boundingBox = new THREE.Box3().setFromObject(object)\n if (boundingBox.isEmpty()) {\n return\n }\n\n const center = boundingBox.getCenter(new THREE.Vector3())\n const bottomCenter = new THREE.Vector3(\n center.x,\n boundingBox.min.y + 0.01,\n center.z\n )\n\n if (\n !Number.isFinite(bottomCenter.x) ||\n !Number.isFinite(bottomCenter.y) ||\n !Number.isFinite(bottomCenter.z)\n ) {\n return\n }\n\n object.position.sub(bottomCenter)\n object.userData.bottomCenterOffset = bottomCenter\n object.updateMatrixWorld(true)\n }\n\n private ensureBottomCenterOffset(\n object: THREE.Object3D\n ): THREE.Vector3 | null {\n const offset = object.userData.bottomCenterOffset\n if (offset instanceof THREE.Vector3) {\n return offset\n }\n\n if (offset && typeof offset === 'object') {\n const {\n x = 0,\n y = 0,\n z = 0,\n } = offset as Partial<Record<'x' | 'y' | 'z', number>>\n const normalized = new THREE.Vector3(x ?? 0, y ?? 0, z ?? 0)\n object.userData.bottomCenterOffset = normalized\n return normalized\n }\n\n return null\n }\n\n private normalizeBottomCenterData(object: THREE.Object3D): boolean {\n const hasOffset = this.ensureBottomCenterOffset(object) !== null\n object.children.forEach((child) => this.normalizeBottomCenterData(child))\n return hasOffset\n }\n\n /**\n * Load an asset with the specified options\n */\n async load(options: AssetLoaderOptions): Promise<THREE.Object3D> {\n const {\n type,\n url,\n size,\n lowResUrl,\n enableCaching = true,\n placeholderColor = 0x4fc3f7,\n placeholderOpacity = 0.4,\n errorColor = 0xff4444,\n errorOpacity = 0.5,\n } = options\n\n // Check cache first\n if (enableCaching && this.cache.has(url)) {\n const cachedClone = this.cache.get(url)!.clone(true)\n const hasOffset = this.normalizeBottomCenterData(cachedClone)\n\n if (!hasOffset) {\n this.positionAssetAtBottomCenter(cachedClone)\n } else {\n cachedClone.updateMatrixWorld(true)\n }\n\n this.loadedAsset = cachedClone\n this.dispatchEvent({ type: 'loaded', asset: cachedClone })\n return cachedClone\n }\n\n // Store error styling options\n this.errorColor = errorColor\n this.errorOpacity = errorOpacity\n\n // Create placeholder if size is provided\n if (size) {\n this.placeholder = this.createPlaceholder(\n size,\n placeholderColor,\n placeholderOpacity\n )\n this.dispatchEvent({\n type: 'placeholderCreated',\n placeholder: this.placeholder,\n })\n }\n\n try {\n // Load low-res model first if provided\n if (lowResUrl) {\n const lowRes = await this.loadModel(type, lowResUrl, true)\n this.lowResAsset = lowRes\n this.dispatchEvent({ type: 'lowResLoaded', lowRes })\n }\n\n // Load main asset\n const asset = await this.loadModel(type, url, false)\n this.loadedAsset = asset\n\n // Cache if enabled\n if (enableCaching) {\n const cacheEntry = asset.clone(true)\n const hasOffset = this.normalizeBottomCenterData(cacheEntry)\n if (!hasOffset) {\n this.positionAssetAtBottomCenter(cacheEntry)\n } else {\n cacheEntry.updateMatrixWorld(true)\n }\n this.cache.set(url, cacheEntry)\n }\n\n this.dispatchEvent({ type: 'loaded', asset })\n return asset\n } catch (error) {\n // Set placeholder to error state\n this.setPlaceholderError()\n this.dispatchEvent({ type: 'error', error: error as Error })\n throw error\n }\n }\n\n /**\n * Load a model based on type\n */\n private loadModel(\n type: AssetType,\n url: string,\n isLowRes: boolean\n ): Promise<THREE.Object3D> {\n return new Promise((resolve, reject) => {\n const onProgress = (event: ProgressEvent) => {\n // Handle cases where loaded > total (compressed content mismatch)\n // or total is 0 (unknown content length)\n let percentage = -1 // Default to indeterminate\n\n // Only calculate percentage if lengthComputable is true AND loaded <= total\n // This prevents false 100% when server reports compressed size\n if (\n event.lengthComputable &&\n event.total > 0 &&\n event.loaded <= event.total\n ) {\n percentage = (event.loaded / event.total) * 100\n }\n\n this.dispatchEvent({\n type: 'progress',\n loaded: event.loaded,\n total: event.total,\n percentage,\n })\n\n // Update placeholder fill only with valid percentage\n if (!isLowRes && percentage >= 0) {\n this.updatePlaceholder(percentage / 100)\n }\n }\n\n const onError = (error: unknown) => {\n reject(error)\n }\n\n switch (type) {\n case 'gltf':\n this.gltfLoader.load(\n url,\n (gltf) => {\n const scene = gltf.scene\n this.positionAssetAtBottomCenter(scene)\n resolve(scene)\n },\n onProgress,\n onError\n )\n break\n\n case 'fbx':\n this.fbxLoader.load(\n url,\n (fbx) => {\n this.positionAssetAtBottomCenter(fbx)\n resolve(fbx)\n },\n onProgress,\n onError\n )\n break\n\n case 'obj':\n this.objLoader.load(\n url,\n (obj) => {\n this.positionAssetAtBottomCenter(obj)\n resolve(obj)\n },\n onProgress,\n onError\n )\n break\n\n case 'usd':\n case 'usdz':\n this.usdLoader.load(\n url,\n (usd) => {\n this.positionAssetAtBottomCenter(usd)\n resolve(usd)\n },\n onProgress,\n onError\n )\n break\n\n default:\n reject(new Error(`Unsupported asset type: ${type}`))\n }\n })\n }\n\n /**\n * Get the placeholder object\n */\n getPlaceholder(): THREE.Object3D | null {\n return this.placeholder\n }\n\n /**\n * Get the loaded asset\n */\n getAsset(): THREE.Object3D | null {\n return this.loadedAsset\n }\n\n /**\n * Get the low-res asset\n */\n getLowResAsset(): THREE.Object3D | null {\n return this.lowResAsset\n }\n\n /**\n * Clear the cache\n */\n clearCache() {\n this.cache.clear()\n }\n\n /**\n * Remove an item from cache\n */\n removeFromCache(url: string) {\n this.cache.delete(url)\n }\n\n /**\n * Get cache size\n */\n getCacheSize(): number {\n return this.cache.size\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../packages/compass/src/index.ts","../../packages/compass/src/CompassOverlay.ts"],"sourcesContent":["export * from './CompassOverlay'\n","import * as THREE from 'three'\n\nexport interface CompassOverlayOptions {\n container?: HTMLElement\n size?: number\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n offset?: { x: number; y: number }\n colors?: {\n background: string\n border: string\n arrow: string\n text: string\n ticks: string\n }\n}\n\n// Define event types for the compass\ninterface CompassOverlayEventMap {\n resetToNorth: {}\n}\n\nexport interface CompassOverlayEvent {\n type: 'resetToNorth'\n}\n\nexport class CompassOverlay extends THREE.EventDispatcher<CompassOverlayEventMap> {\n private camera: THREE.Camera\n private container: HTMLElement\n private compassElement!: HTMLElement\n private arrowElement!: HTMLElement\n private isActive: boolean = false\n private options: Required<CompassOverlayOptions>\n private currentRotation: number = 0\n\n constructor(camera: THREE.Camera, options: CompassOverlayOptions = {}) {\n super()\n this.camera = camera\n\n // Set default options\n this.options = {\n container: options.container || document.body,\n size: options.size || 100,\n position: options.position || 'bottom-right',\n offset: options.offset || { x: 20, y: 20 },\n colors: {\n background: options.colors?.background || '#1a1a1a',\n border: options.colors?.border || '#333333',\n arrow: options.colors?.arrow || '#ff4444',\n text: options.colors?.text || '#ffffff',\n ticks: options.colors?.ticks || '#666666',\n ...options.colors,\n },\n }\n\n this.container = this.options.container\n this.createCompassElement()\n this.setupStyles()\n }\n\n private createCompassElement(): void {\n // Create main compass container\n this.compassElement = document.createElement('div')\n this.compassElement.className = 'threejs-compass-overlay'\n\n // Create compass background with ticks\n const compassBg = document.createElement('div')\n compassBg.className = 'compass-background'\n\n // Create cardinal direction labels\n const directions = [\n { label: 'N', angle: 0 },\n { label: 'E', angle: 90 },\n { label: 'S', angle: 180 },\n { label: 'W', angle: 270 },\n ]\n\n // Create tick marks\n for (let i = 0; i < 360; i += 11.25) {\n const tick = document.createElement('div')\n tick.className =\n i % 45 === 0 ? 'compass-tick-major' : 'compass-tick-minor'\n const tickLength = i % 30 === 0 ? 8 : 4\n tick.style.transform = `rotate(${i}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n compassBg.appendChild(tick)\n }\n\n directions.forEach(({ label, angle }) => {\n const dirLabel = document.createElement('div')\n dirLabel.className = 'compass-label'\n dirLabel.textContent = label\n dirLabel.style.transform = `rotate(${angle}deg) translateY(-${this.options.size / 2 - 15}px) rotate(-${angle}deg)`\n compassBg.appendChild(dirLabel)\n })\n\n // Create arrow element\n this.arrowElement = document.createElement('div')\n this.arrowElement.className = 'compass-arrow'\n\n // Arrow SVG\n this.arrowElement.innerHTML = `\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <polygon points=\"10,2 14,12 10,10 6,12\" fill=\"${this.options.colors.arrow}\" stroke=\"#000\" stroke-width=\"0.5\"/>\n </svg>\n `\n\n this.compassElement.appendChild(compassBg)\n this.compassElement.appendChild(this.arrowElement)\n this.container.appendChild(this.compassElement)\n\n // Add double-click event to reset camera to north\n this.setupDoubleClickHandler()\n }\n\n private setupStyles(): void {\n // Check if styles already exist\n if (document.getElementById('threejs-compass-styles')) {\n return\n }\n\n const style = document.createElement('style')\n style.id = 'threejs-compass-styles'\n style.textContent = `\n .threejs-compass-overlay {\n position: fixed;\n width: ${this.options.size}px;\n height: ${this.options.size}px;\n background: ${this.options.colors.background};\n border: 2px solid ${this.options.colors.border};\n border-radius: 50%;\n z-index: 1000;\n pointer-events: none;\n user-select: none;\n transition: opacity 0.2s ease;\n ${this.getPositionStyles()};\n }\n\n .compass-background {\n position: relative;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n }\n\n .compass-label {\n position: absolute;\n top: 50%;\n left: 50%;\n transform-origin: center;\n color: ${this.options.colors.text};\n font-family: Arial, sans-serif;\n font-size: 12px;\n font-weight: bold;\n text-align: center;\n width: 12px;\n height: 12px;\n line-height: 12px;\n margin-left: -6px;\n margin-top: -6px;\n }\n\n .compass-tick-major {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 2px;\n height: 8px;\n background: ${this.options.colors.ticks};\n transform-origin: center top;\n margin-left: -1px;\n margin-top: -4px;\n }\n\n .compass-tick-minor {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 1px;\n height: 4px;\n background: ${this.options.colors.ticks};\n transform-origin: center top;\n margin-left: -0.5px;\n margin-top: -2px;\n opacity: 0.6;\n }\n\n .compass-arrow {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: transform 0.1s ease-out;\n z-index: 1;\n }\n\n .compass-arrow svg {\n display: block;\n filter: drop-shadow(0 0 2px rgba(0,0,0,0.5));\n }\n `\n\n document.head.appendChild(style)\n }\n\n private setupDoubleClickHandler(): void {\n // Enable pointer events for interaction\n this.compassElement.style.pointerEvents = 'auto'\n this.compassElement.style.cursor = 'pointer'\n\n // Add double-click event listener\n this.compassElement.addEventListener('dblclick', (event) => {\n event.preventDefault()\n event.stopPropagation()\n\n // Dispatch custom event\n this.dispatchEvent({ type: 'resetToNorth' })\n })\n\n // Add visual feedback on hover\n this.compassElement.addEventListener('mouseenter', () => {\n this.compassElement.style.opacity = '0.8'\n })\n\n this.compassElement.addEventListener('mouseleave', () => {\n this.compassElement.style.opacity = '1'\n })\n }\n\n private updateTicksAndLabels(): void {\n // Update compass labels\n\n // Update tick marks\n const majorTicks = this.compassElement.querySelectorAll(\n '.compass-tick-major'\n )\n const minorTicks = this.compassElement.querySelectorAll(\n '.compass-tick-minor'\n )\n\n majorTicks.forEach((tick, index) => {\n const angle = (index * 30) % 360 // Major ticks every 30 degrees\n const tickLength = 8\n ;(tick as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n })\n\n minorTicks.forEach((tick, index) => {\n // Minor ticks are at angles that aren't multiples of 30\n const allAngles = []\n for (let i = 0; i < 360; i += 10) {\n if (i % 30 !== 0) {\n // Skip major tick positions\n allAngles.push(i)\n }\n }\n const angle = allAngles[index] || 0\n const tickLength = 4\n ;(tick as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - tickLength - 2}px)`\n })\n\n const labels = this.compassElement.querySelectorAll('.compass-label')\n labels.forEach((label, index) => {\n const directions = [\n { label: 'N', angle: 0 },\n { label: 'E', angle: 90 },\n { label: 'S', angle: 180 },\n { label: 'W', angle: 270 },\n ]\n const { angle } = directions[index]\n ;(label as HTMLElement).style.transform =\n `rotate(${angle}deg) translateY(-${this.options.size / 2 - 15}px) rotate(-${angle}deg)`\n })\n }\n\n private getPositionStyles(): string {\n const { position, offset } = this.options\n\n switch (position) {\n case 'top-left':\n return `top: ${offset.y}px; left: ${offset.x}px;`\n case 'top-right':\n return `top: ${offset.y}px; right: ${offset.x}px;`\n case 'bottom-left':\n return `bottom: ${offset.y}px; left: ${offset.x}px;`\n case 'bottom-right':\n default:\n return `bottom: ${offset.y}px; right: ${offset.x}px;`\n }\n }\n\n public start(): void {\n if (this.isActive) return\n this.isActive = true\n this.update()\n }\n\n public stop(): void {\n this.isActive = false\n }\n\n public update(): void {\n if (!this.isActive) return\n\n // Get the camera's world matrix\n const cameraMatrix = new THREE.Matrix4()\n this.camera.updateMatrixWorld()\n cameraMatrix.copy(this.camera.matrixWorld)\n\n // Extract the camera's forward direction (negative Z in camera space)\n const forward = new THREE.Vector3(0, 0, -1)\n forward.transformDirection(cameraMatrix)\n\n // Project to XZ plane (ignore Y component for top-down compass view)\n forward.y = 0\n forward.normalize()\n\n // Calculate angle from world -Z axis (north)\n // World Z- is \"north\", X- is \"east\" (flipped X axis)\n const targetAngle = Math.atan2(-forward.x, -forward.z) * (180 / Math.PI)\n\n // Calculate the shortest rotation path to avoid spinning around\n let angleDiff = targetAngle - this.currentRotation\n\n // Normalize the angle difference to be between -180 and 180\n while (angleDiff > 180) angleDiff -= 360\n while (angleDiff < -180) angleDiff += 360\n\n // Update current rotation with smooth transition\n this.currentRotation += angleDiff\n\n // Update arrow rotation (negative because we want the arrow to point to north, not where camera is facing)\n this.arrowElement.style.transform = `translate(-50%, -50%) rotate(${-this.currentRotation}deg)`\n\n // Continue updating if active\n if (this.isActive) {\n requestAnimationFrame(() => this.update())\n }\n }\n\n public setCamera(camera: THREE.Camera): void {\n this.camera = camera\n }\n\n public setSize(size: number): void {\n this.options.size = size\n this.compassElement.style.width = `${size}px`\n this.compassElement.style.height = `${size}px`\n this.updateTicksAndLabels()\n this.updateStyles()\n }\n\n public setPosition(\n position: CompassOverlayOptions['position'],\n offset?: { x: number; y: number }\n ): void {\n if (position) {\n this.options.position = position\n }\n if (offset) {\n this.options.offset = offset\n }\n\n // Update position styles\n const positionStyles = this.getPositionStyles()\n const styles = positionStyles.split(';').filter((s) => s.trim())\n\n // Clear existing position styles\n this.compassElement.style.top = ''\n this.compassElement.style.right = ''\n this.compassElement.style.bottom = ''\n this.compassElement.style.left = ''\n\n // Apply new position styles\n styles.forEach((style) => {\n const [property, value] = style.split(':').map((s) => s.trim())\n if (property && value) {\n ;(this.compassElement.style as any)[property] = value\n }\n })\n }\n\n public setColors(colors: Partial<CompassOverlayOptions['colors']>): void {\n this.options.colors = { ...this.options.colors, ...colors }\n this.updateStyles()\n }\n\n /**\n * Helper method to reset camera to look north (world Z- direction)\n * This is a convenience method that can be called when handling the 'resetToNorth' event\n */\n public static resetCameraToNorth(\n camera: THREE.Camera,\n smooth: boolean = true\n ): void {\n if (smooth) {\n // Create a smooth rotation animation to north\n const startQuaternion = camera.quaternion.clone()\n const targetQuaternion = new THREE.Quaternion()\n\n // Set target rotation to look at north (Z-)\n const targetMatrix = new THREE.Matrix4()\n targetMatrix.lookAt(\n camera.position,\n camera.position.clone().add(new THREE.Vector3(0, 0, -1)),\n new THREE.Vector3(0, 1, 0)\n )\n targetQuaternion.setFromRotationMatrix(targetMatrix)\n\n // Animate rotation\n const duration = 500 // milliseconds\n const startTime = Date.now()\n\n const animate = () => {\n const elapsed = Date.now() - startTime\n const progress = Math.min(elapsed / duration, 1)\n\n // Use smooth easing\n const easeProgress = 1 - Math.pow(1 - progress, 3)\n\n camera.quaternion.slerpQuaternions(\n startQuaternion,\n targetQuaternion,\n easeProgress\n )\n\n if (progress < 1) {\n requestAnimationFrame(animate)\n }\n }\n animate()\n } else {\n // Instant rotation to north\n camera.lookAt(camera.position.clone().add(new THREE.Vector3(0, 0, -1)))\n }\n }\n\n private updateStyles(): void {\n // Remove existing styles\n const existingStyle = document.getElementById('threejs-compass-styles')\n if (existingStyle) {\n existingStyle.remove()\n }\n\n // Recreate styles with new values\n this.setupStyles()\n }\n\n public dispose(): void {\n this.stop()\n\n if (this.compassElement && this.compassElement.parentNode) {\n this.compassElement.parentNode.removeChild(this.compassElement)\n }\n\n // Remove styles if no other compass instances exist\n const compassElements = document.querySelectorAll(\n '.threejs-compass-overlay'\n )\n if (compassElements.length === 0) {\n const style = document.getElementById('threejs-compass-styles')\n if (style) {\n style.remove()\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../packages/grid/src/index.ts","../../packages/grid/src/InfiniteGrid.ts"],"sourcesContent":["export * from './InfiniteGrid'\n","import * as THREE from 'three'\n\n// Define custom event types\ninterface InfiniteGridEventMap {\n subdivisionsChanged: { subdivisions: number }\n divisionsChanged: { divisions: number }\n colorChanged: {\n color: THREE.Color | number\n colorType: 'color1' | 'color2' | 'fog'\n }\n fogChanged: { property: 'near' | 'far'; value: number }\n}\n\nexport class InfiniteGrid extends THREE.Object3D {\n subdivisions: number\n divisions: number\n gridMaterial: THREE.ShaderMaterial\n private eventDispatcher: THREE.EventDispatcher<InfiniteGridEventMap>\n\n constructor(divisions: number = 1, subdivisions: number = 10) {\n super()\n\n // Create an internal EventDispatcher for custom events\n this.eventDispatcher = new THREE.EventDispatcher<InfiniteGridEventMap>()\n this.divisions = divisions\n this.subdivisions = subdivisions\n\n // Create a custom grid shader material\n const gridSize = 100\n\n // Create a plane for the grid\n const gridGeometry = new THREE.PlaneGeometry(\n gridSize * 2,\n gridSize * 2,\n 1,\n 1\n )\n\n // Custom grid shader material\n this.gridMaterial = new THREE.ShaderMaterial({\n uniforms: {\n uSize1: { value: this.divisions / this.subdivisions },\n uSize2: { value: this.divisions },\n uColor1: { value: new THREE.Color(0x444444) },\n uColor2: { value: new THREE.Color(0x666666) },\n uFogColor: { value: new THREE.Color(0x2a2a2a) },\n uFogNear: { value: 20.0 },\n uFogFar: { value: 60.0 },\n },\n vertexShader: `\n varying vec3 worldPosition;\n \n void main() {\n worldPosition = position.xzy;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform float uSize1;\n uniform float uSize2;\n uniform vec3 uColor1;\n uniform vec3 uColor2;\n uniform vec3 uFogColor;\n uniform float uFogNear;\n uniform float uFogFar;\n \n varying vec3 worldPosition;\n \n float getGrid(float size) {\n vec2 r = worldPosition.xz / size;\n vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);\n float line = min(grid.x, grid.y);\n return 1.0 - min(line, 1.0);\n }\n \n void main() {\n float g1 = getGrid(uSize1);\n float g2 = getGrid(uSize2);\n \n // Calculate fog factor using both near and far\n float dist = length(worldPosition);\n float fogFactor = smoothstep(uFogNear, uFogFar, dist);\n \n // Mix grid colors\n vec3 color = mix(uColor1, uColor2, g2);\n \n // Apply fog to color and use inverse fog factor for alpha (fade out with distance)\n vec3 finalColor = mix(color, uFogColor, fogFactor);\n float alpha = (g1 + g2) * (1.0 - fogFactor);\n \n gl_FragColor = vec4(finalColor, alpha);\n }\n `,\n transparent: true,\n side: THREE.DoubleSide,\n depthWrite: false,\n })\n\n // Create the grid mesh\n const gridMesh = new THREE.Mesh(gridGeometry, this.gridMaterial)\n gridMesh.renderOrder = -1\n gridMesh.rotation.x = -Math.PI / 2\n this.add(gridMesh)\n // return gridMesh;\n }\n\n setSubdivisions(subdivisions: number): void {\n this.subdivisions = subdivisions\n this.gridMaterial.uniforms.uSize1.value = this.divisions / this.subdivisions\n this.eventDispatcher.dispatchEvent({\n type: 'subdivisionsChanged',\n subdivisions,\n })\n }\n\n setDivisions(divisions: number): void {\n this.divisions = divisions\n this.gridMaterial.uniforms.uSize1.value = this.divisions / this.subdivisions\n this.gridMaterial.uniforms.uSize2.value = this.divisions\n this.eventDispatcher.dispatchEvent({ type: 'divisionsChanged', divisions })\n }\n\n setColor1(color: THREE.Color | number): void {\n if (typeof color === 'number') {\n this.gridMaterial.uniforms.uColor1.value.setHex(color)\n } else {\n this.gridMaterial.uniforms.uColor1.value.copy(color)\n }\n this.eventDispatcher.dispatchEvent({\n type: 'colorChanged',\n color,\n colorType: 'color1',\n })\n }\n\n setColor2(color: THREE.Color | number): void {\n if (typeof color === 'number') {\n this.gridMaterial.uniforms.uColor2.value.setHex(color)\n } else {\n this.gridMaterial.uniforms.uColor2.value.copy(color)\n }\n this.eventDispatcher.dispatchEvent({\n type: 'colorChanged',\n color,\n colorType: 'color2',\n })\n }\n\n setFogColor(color: THREE.Color | number): void {\n if (typeof color === 'number') {\n this.gridMaterial.uniforms.uFogColor.value.setHex(color)\n } else {\n this.gridMaterial.uniforms.uFogColor.value.copy(color)\n }\n this.eventDispatcher.dispatchEvent({\n type: 'colorChanged',\n color,\n colorType: 'fog',\n })\n }\n\n setFogNear(near: number): void {\n const fogFar = this.gridMaterial.uniforms.uFogFar.value\n const clampedNear = Math.min(near, fogFar)\n this.gridMaterial.uniforms.uFogNear.value = clampedNear\n this.eventDispatcher.dispatchEvent({\n type: 'fogChanged',\n property: 'near',\n value: clampedNear,\n })\n }\n\n setFogFar(far: number): void {\n const fogNear = this.gridMaterial.uniforms.uFogNear.value\n const clampedFar = Math.max(far, fogNear)\n this.gridMaterial.uniforms.uFogFar.value = clampedFar\n this.eventDispatcher.dispatchEvent({\n type: 'fogChanged',\n property: 'far',\n value: clampedFar,\n })\n }\n\n // Custom event listener methods (prefixed to avoid conflicts with Object3D)\n addGridEventListener<K extends keyof InfiniteGridEventMap>(\n type: K,\n listener: (event: InfiniteGridEventMap[K] & { type: K }) => void\n ): void {\n this.eventDispatcher.addEventListener(type, listener)\n }\n\n removeGridEventListener<K extends keyof InfiniteGridEventMap>(\n type: K,\n listener: (event: InfiniteGridEventMap[K] & { type: K }) => void\n ): void {\n this.eventDispatcher.removeEventListener(type, listener)\n }\n\n hasGridEventListener<K extends keyof InfiniteGridEventMap>(\n type: K,\n listener: (event: InfiniteGridEventMap[K] & { type: K }) => void\n ): boolean {\n return this.eventDispatcher.hasEventListener(type, listener)\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}