@tonybfox/threejs-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +321 -0
  2. package/dist/asset-loader/index.cjs +376 -0
  3. package/dist/asset-loader/index.cjs.map +1 -0
  4. package/dist/asset-loader/index.d.mts +101 -0
  5. package/dist/asset-loader/index.d.ts +101 -0
  6. package/dist/asset-loader/index.mjs +7 -0
  7. package/dist/asset-loader/index.mjs.map +1 -0
  8. package/dist/camera/index.cjs +313 -0
  9. package/dist/camera/index.cjs.map +1 -0
  10. package/dist/camera/index.d.mts +82 -0
  11. package/dist/camera/index.d.ts +82 -0
  12. package/dist/camera/index.mjs +7 -0
  13. package/dist/camera/index.mjs.map +1 -0
  14. package/dist/chunk-5DP6WDB3.mjs +1161 -0
  15. package/dist/chunk-5DP6WDB3.mjs.map +1 -0
  16. package/dist/chunk-BJKSICFA.mjs +1579 -0
  17. package/dist/chunk-BJKSICFA.mjs.map +1 -0
  18. package/dist/chunk-BYRZCHE7.mjs +277 -0
  19. package/dist/chunk-BYRZCHE7.mjs.map +1 -0
  20. package/dist/chunk-EIROAPF7.mjs +387 -0
  21. package/dist/chunk-EIROAPF7.mjs.map +1 -0
  22. package/dist/chunk-EQDOX34V.mjs +164 -0
  23. package/dist/chunk-EQDOX34V.mjs.map +1 -0
  24. package/dist/chunk-IIAZ2WJJ.mjs +405 -0
  25. package/dist/chunk-IIAZ2WJJ.mjs.map +1 -0
  26. package/dist/chunk-L4VIIJZD.mjs +340 -0
  27. package/dist/chunk-L4VIIJZD.mjs.map +1 -0
  28. package/dist/chunk-P35QJCOG.mjs +339 -0
  29. package/dist/chunk-P35QJCOG.mjs.map +1 -0
  30. package/dist/chunk-R64RVBRM.mjs +394 -0
  31. package/dist/chunk-R64RVBRM.mjs.map +1 -0
  32. package/dist/compass/index.cjs +375 -0
  33. package/dist/compass/index.cjs.map +1 -0
  34. package/dist/compass/index.d.mts +58 -0
  35. package/dist/compass/index.d.ts +58 -0
  36. package/dist/compass/index.mjs +7 -0
  37. package/dist/compass/index.mjs.map +1 -0
  38. package/dist/grid/index.cjs +200 -0
  39. package/dist/grid/index.cjs.map +1 -0
  40. package/dist/grid/index.d.mts +43 -0
  41. package/dist/grid/index.d.ts +43 -0
  42. package/dist/grid/index.mjs +7 -0
  43. package/dist/grid/index.mjs.map +1 -0
  44. package/dist/index.cjs +5049 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.mts +13 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.mjs +47 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/dist/measurements/index.cjs +1198 -0
  51. package/dist/measurements/index.cjs.map +1 -0
  52. package/dist/measurements/index.d.mts +449 -0
  53. package/dist/measurements/index.d.ts +449 -0
  54. package/dist/measurements/index.mjs +9 -0
  55. package/dist/measurements/index.mjs.map +1 -0
  56. package/dist/sunlight/index.cjs +441 -0
  57. package/dist/sunlight/index.cjs.map +1 -0
  58. package/dist/sunlight/index.d.mts +92 -0
  59. package/dist/sunlight/index.d.ts +92 -0
  60. package/dist/sunlight/index.mjs +7 -0
  61. package/dist/sunlight/index.mjs.map +1 -0
  62. package/dist/terrain/index.cjs +423 -0
  63. package/dist/terrain/index.cjs.map +1 -0
  64. package/dist/terrain/index.d.mts +219 -0
  65. package/dist/terrain/index.d.ts +219 -0
  66. package/dist/terrain/index.mjs +7 -0
  67. package/dist/terrain/index.mjs.map +1 -0
  68. package/dist/transform-controls/index.cjs +1587 -0
  69. package/dist/transform-controls/index.cjs.map +1 -0
  70. package/dist/transform-controls/index.d.mts +162 -0
  71. package/dist/transform-controls/index.d.ts +162 -0
  72. package/dist/transform-controls/index.mjs +13 -0
  73. package/dist/transform-controls/index.mjs.map +1 -0
  74. package/dist/view-helper/index.cjs +430 -0
  75. package/dist/view-helper/index.cjs.map +1 -0
  76. package/dist/view-helper/index.d.mts +75 -0
  77. package/dist/view-helper/index.d.ts +75 -0
  78. package/dist/view-helper/index.mjs +7 -0
  79. package/dist/view-helper/index.mjs.map +1 -0
  80. package/package.json +124 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../packages/asset-loader/src/index.ts","../../packages/asset-loader/src/AssetLoader.ts"],"sourcesContent":["export * from './AssetLoader'\n","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'\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'\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 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 }\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 },\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 varying vec3 vPosition;\n \n void main() {\n float normalizedY = (vPosition.y + 1.0) / 2.0; // Normalize from -1,1 to 0,1\n float alpha = opacity;\n \n // Create fill-up effect\n if (normalizedY > fillProgress) {\n alpha *= 0.3; // 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.8,\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 const percentage = (event.loaded / event.total) * 100\n this.dispatchEvent({\n type: 'progress',\n loaded: event.loaded,\n total: event.total,\n percentage,\n })\n\n // Update placeholder fill\n if (!isLowRes) {\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 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":[]}
@@ -0,0 +1,101 @@
1
+ import * as THREE from 'three';
2
+
3
+ interface AssetLoaderEventMap {
4
+ progress: {
5
+ loaded: number;
6
+ total: number;
7
+ percentage: number;
8
+ };
9
+ loaded: {
10
+ asset: THREE.Object3D;
11
+ };
12
+ error: {
13
+ error: Error;
14
+ };
15
+ placeholderCreated: {
16
+ placeholder: THREE.Object3D;
17
+ };
18
+ lowResLoaded: {
19
+ lowRes: THREE.Object3D;
20
+ };
21
+ }
22
+ type AssetType = 'gltf' | 'fbx' | 'obj';
23
+ interface AssetLoaderOptions {
24
+ type: AssetType;
25
+ url: string;
26
+ size?: [number, number, number];
27
+ lowResUrl?: string;
28
+ enableCaching?: boolean;
29
+ placeholderColor?: number;
30
+ placeholderOpacity?: number;
31
+ errorColor?: number;
32
+ errorOpacity?: number;
33
+ }
34
+ declare class AssetLoader extends THREE.EventDispatcher<AssetLoaderEventMap> {
35
+ private cache;
36
+ private gltfLoader;
37
+ private fbxLoader;
38
+ private objLoader;
39
+ private placeholder;
40
+ private loadedAsset;
41
+ private lowResAsset;
42
+ private errorColor;
43
+ private errorOpacity;
44
+ constructor();
45
+ /**
46
+ * Create a placeholder cube with shader effect
47
+ */
48
+ private createPlaceholder;
49
+ /**
50
+ * Update placeholder fill progress based on loading progress
51
+ */
52
+ private updatePlaceholder;
53
+ /**
54
+ * Set placeholder to error state with configurable color and opacity
55
+ */
56
+ private setPlaceholderError;
57
+ /**
58
+ * Update placeholder animation time (call this in your render loop)
59
+ */
60
+ updatePlaceholderAnimation(deltaTime: number): void;
61
+ /**
62
+ * Reposition an asset so that its bottom-center sits at the local origin.
63
+ */
64
+ private positionAssetAtBottomCenter;
65
+ private ensureBottomCenterOffset;
66
+ private normalizeBottomCenterData;
67
+ /**
68
+ * Load an asset with the specified options
69
+ */
70
+ load(options: AssetLoaderOptions): Promise<THREE.Object3D>;
71
+ /**
72
+ * Load a model based on type
73
+ */
74
+ private loadModel;
75
+ /**
76
+ * Get the placeholder object
77
+ */
78
+ getPlaceholder(): THREE.Object3D | null;
79
+ /**
80
+ * Get the loaded asset
81
+ */
82
+ getAsset(): THREE.Object3D | null;
83
+ /**
84
+ * Get the low-res asset
85
+ */
86
+ getLowResAsset(): THREE.Object3D | null;
87
+ /**
88
+ * Clear the cache
89
+ */
90
+ clearCache(): void;
91
+ /**
92
+ * Remove an item from cache
93
+ */
94
+ removeFromCache(url: string): void;
95
+ /**
96
+ * Get cache size
97
+ */
98
+ getCacheSize(): number;
99
+ }
100
+
101
+ export { AssetLoader, type AssetLoaderOptions, type AssetType };
@@ -0,0 +1,101 @@
1
+ import * as THREE from 'three';
2
+
3
+ interface AssetLoaderEventMap {
4
+ progress: {
5
+ loaded: number;
6
+ total: number;
7
+ percentage: number;
8
+ };
9
+ loaded: {
10
+ asset: THREE.Object3D;
11
+ };
12
+ error: {
13
+ error: Error;
14
+ };
15
+ placeholderCreated: {
16
+ placeholder: THREE.Object3D;
17
+ };
18
+ lowResLoaded: {
19
+ lowRes: THREE.Object3D;
20
+ };
21
+ }
22
+ type AssetType = 'gltf' | 'fbx' | 'obj';
23
+ interface AssetLoaderOptions {
24
+ type: AssetType;
25
+ url: string;
26
+ size?: [number, number, number];
27
+ lowResUrl?: string;
28
+ enableCaching?: boolean;
29
+ placeholderColor?: number;
30
+ placeholderOpacity?: number;
31
+ errorColor?: number;
32
+ errorOpacity?: number;
33
+ }
34
+ declare class AssetLoader extends THREE.EventDispatcher<AssetLoaderEventMap> {
35
+ private cache;
36
+ private gltfLoader;
37
+ private fbxLoader;
38
+ private objLoader;
39
+ private placeholder;
40
+ private loadedAsset;
41
+ private lowResAsset;
42
+ private errorColor;
43
+ private errorOpacity;
44
+ constructor();
45
+ /**
46
+ * Create a placeholder cube with shader effect
47
+ */
48
+ private createPlaceholder;
49
+ /**
50
+ * Update placeholder fill progress based on loading progress
51
+ */
52
+ private updatePlaceholder;
53
+ /**
54
+ * Set placeholder to error state with configurable color and opacity
55
+ */
56
+ private setPlaceholderError;
57
+ /**
58
+ * Update placeholder animation time (call this in your render loop)
59
+ */
60
+ updatePlaceholderAnimation(deltaTime: number): void;
61
+ /**
62
+ * Reposition an asset so that its bottom-center sits at the local origin.
63
+ */
64
+ private positionAssetAtBottomCenter;
65
+ private ensureBottomCenterOffset;
66
+ private normalizeBottomCenterData;
67
+ /**
68
+ * Load an asset with the specified options
69
+ */
70
+ load(options: AssetLoaderOptions): Promise<THREE.Object3D>;
71
+ /**
72
+ * Load a model based on type
73
+ */
74
+ private loadModel;
75
+ /**
76
+ * Get the placeholder object
77
+ */
78
+ getPlaceholder(): THREE.Object3D | null;
79
+ /**
80
+ * Get the loaded asset
81
+ */
82
+ getAsset(): THREE.Object3D | null;
83
+ /**
84
+ * Get the low-res asset
85
+ */
86
+ getLowResAsset(): THREE.Object3D | null;
87
+ /**
88
+ * Clear the cache
89
+ */
90
+ clearCache(): void;
91
+ /**
92
+ * Remove an item from cache
93
+ */
94
+ removeFromCache(url: string): void;
95
+ /**
96
+ * Get cache size
97
+ */
98
+ getCacheSize(): number;
99
+ }
100
+
101
+ export { AssetLoader, type AssetLoaderOptions, type AssetType };
@@ -0,0 +1,7 @@
1
+ import {
2
+ AssetLoader
3
+ } from "../chunk-L4VIIJZD.mjs";
4
+ export {
5
+ AssetLoader
6
+ };
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // packages/camera/src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ DualCameraControls: () => DualCameraControls
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+
37
+ // packages/camera/src/DualCameraControls.ts
38
+ var THREE = __toESM(require("three"));
39
+ var import_camera_controls = __toESM(require("camera-controls"));
40
+ var controlsInstalled = false;
41
+ var tempVec3A = new THREE.Vector3();
42
+ var tempVec3B = new THREE.Vector3();
43
+ var tempVec2 = new THREE.Vector2();
44
+ function ensureCameraControlsInstalled() {
45
+ if (!controlsInstalled) {
46
+ import_camera_controls.default.install({ THREE });
47
+ controlsInstalled = true;
48
+ }
49
+ }
50
+ function toVector3(value, fallback, target) {
51
+ if (!value) {
52
+ target.set(fallback[0], fallback[1], fallback[2]);
53
+ return target;
54
+ }
55
+ if (Array.isArray(value)) {
56
+ target.set(value[0], value[1], value[2]);
57
+ return target;
58
+ }
59
+ target.copy(value);
60
+ return target;
61
+ }
62
+ var DualCameraControls = class extends import_camera_controls.default {
63
+ constructor(renderer, options = {}) {
64
+ ensureCameraControlsInstalled();
65
+ const { domElement = renderer.domElement } = options;
66
+ const aspect = resolveAspect(renderer, domElement);
67
+ const perspectiveConfig = options.perspective ?? {};
68
+ const orthographicConfig = options.orthographic ?? {};
69
+ const perspectiveCamera = new THREE.PerspectiveCamera(
70
+ perspectiveConfig.fov ?? 60,
71
+ aspect,
72
+ perspectiveConfig.near ?? 0.1,
73
+ perspectiveConfig.far ?? 2e3
74
+ );
75
+ perspectiveCamera.position.copy(
76
+ toVector3(
77
+ perspectiveConfig.position,
78
+ [12, 12, 12],
79
+ new THREE.Vector3()
80
+ )
81
+ );
82
+ if (perspectiveConfig.zoom !== void 0) {
83
+ perspectiveCamera.zoom = perspectiveConfig.zoom;
84
+ perspectiveCamera.updateProjectionMatrix();
85
+ }
86
+ const orthoHalfHeight = Math.max(orthographicConfig.size ?? 20, 1e-3) * 0.5;
87
+ const orthoHalfWidth = orthoHalfHeight * aspect;
88
+ const orthographicCamera = new THREE.OrthographicCamera(
89
+ -orthoHalfWidth,
90
+ orthoHalfWidth,
91
+ orthoHalfHeight,
92
+ -orthoHalfHeight,
93
+ orthographicConfig.near ?? 0.1,
94
+ orthographicConfig.far ?? 2e3
95
+ );
96
+ orthographicCamera.position.copy(
97
+ toVector3(
98
+ orthographicConfig.position,
99
+ [12, 12, 12],
100
+ new THREE.Vector3()
101
+ )
102
+ );
103
+ if (orthographicConfig.zoom !== void 0) {
104
+ orthographicCamera.zoom = orthographicConfig.zoom;
105
+ orthographicCamera.updateProjectionMatrix();
106
+ }
107
+ const initialMode = options.initialMode ?? "perspective";
108
+ const initialCamera = initialMode === "orthographic" ? orthographicCamera : perspectiveCamera;
109
+ super(initialCamera, domElement);
110
+ this.updateClock = new THREE.Clock();
111
+ const initialTarget = toVector3(
112
+ options.initialTarget,
113
+ [0, 0, 0],
114
+ new THREE.Vector3()
115
+ );
116
+ void this.setLookAt(
117
+ initialCamera.position.x,
118
+ initialCamera.position.y,
119
+ initialCamera.position.z,
120
+ initialTarget.x,
121
+ initialTarget.y,
122
+ initialTarget.z,
123
+ false
124
+ );
125
+ this.renderer = renderer;
126
+ this.domElementRef = domElement;
127
+ this.perspectiveCamera = perspectiveCamera;
128
+ this.orthographicCamera = orthographicCamera;
129
+ this.activeMode = initialMode;
130
+ this.minOrthoHalfHeight = orthoHalfHeight;
131
+ this.updateInputBindingsForMode(initialMode);
132
+ }
133
+ get mode() {
134
+ return this.activeMode;
135
+ }
136
+ /**
137
+ * Returns the currently active camera instance.
138
+ */
139
+ get activeCamera() {
140
+ return this.camera;
141
+ }
142
+ /**
143
+ * Switch to the perspective camera while keeping the current framing.
144
+ */
145
+ switchToPerspective(enableTransition = false) {
146
+ if (this.activeMode === "perspective") {
147
+ return;
148
+ }
149
+ const target = this.getTarget(tempVec3A);
150
+ const position = this.getPosition(tempVec3B);
151
+ const aspect = resolveAspect(this.renderer, this.domElementRef);
152
+ this.perspectiveCamera.aspect = aspect;
153
+ this.perspectiveCamera.position.copy(position);
154
+ this.perspectiveCamera.quaternion.copy(this.camera.quaternion);
155
+ this.perspectiveCamera.up.copy(this.camera.up);
156
+ this.perspectiveCamera.updateProjectionMatrix();
157
+ this.camera = this.perspectiveCamera;
158
+ this.activeMode = "perspective";
159
+ this.updateInputBindingsForMode("perspective");
160
+ void this.setLookAt(
161
+ position.x,
162
+ position.y,
163
+ position.z,
164
+ target.x,
165
+ target.y,
166
+ target.z,
167
+ enableTransition
168
+ );
169
+ this.dispatchEvent({
170
+ type: "modechange",
171
+ mode: this.activeMode,
172
+ previousMode: "orthographic",
173
+ camera: this.perspectiveCamera
174
+ });
175
+ }
176
+ /**
177
+ * Switch to the orthographic camera while keeping the current framing.
178
+ */
179
+ switchToOrthographic(enableTransition = false) {
180
+ if (this.activeMode === "orthographic") {
181
+ return;
182
+ }
183
+ const target = this.getTarget(tempVec3A);
184
+ const position = this.getPosition(tempVec3B);
185
+ const aspect = resolveAspect(this.renderer, this.domElementRef);
186
+ this.updateOrthographicFrustum(position, target, aspect);
187
+ this.orthographicCamera.position.copy(position);
188
+ this.orthographicCamera.quaternion.copy(this.camera.quaternion);
189
+ this.orthographicCamera.up.copy(this.camera.up);
190
+ this.orthographicCamera.updateProjectionMatrix();
191
+ this.camera = this.orthographicCamera;
192
+ this.activeMode = "orthographic";
193
+ this.updateInputBindingsForMode("orthographic");
194
+ void this.setLookAt(
195
+ position.x,
196
+ position.y,
197
+ position.z,
198
+ target.x,
199
+ target.y,
200
+ target.z,
201
+ enableTransition
202
+ );
203
+ this.dispatchEvent({
204
+ type: "modechange",
205
+ mode: this.activeMode,
206
+ previousMode: "perspective",
207
+ camera: this.orthographicCamera
208
+ });
209
+ }
210
+ /**
211
+ * Toggles between perspective and orthographic camera modes.
212
+ */
213
+ toggleCameraMode(enableTransition = false) {
214
+ if (this.activeMode === "perspective") {
215
+ this.switchToOrthographic(enableTransition);
216
+ } else {
217
+ this.switchToPerspective(enableTransition);
218
+ }
219
+ }
220
+ /**
221
+ * Update camera projection parameters when the viewport size changes.
222
+ */
223
+ handleResize(width, height) {
224
+ const aspect = height === 0 ? 1 : width / height;
225
+ this.perspectiveCamera.aspect = aspect;
226
+ this.perspectiveCamera.updateProjectionMatrix();
227
+ const target = this.getTarget(tempVec3A);
228
+ const position = this.getPosition(tempVec3B);
229
+ this.updateOrthographicFrustum(position, target, aspect);
230
+ if (this.activeMode === "orthographic") {
231
+ this.camera = this.orthographicCamera;
232
+ void this.setLookAt(
233
+ position.x,
234
+ position.y,
235
+ position.z,
236
+ target.x,
237
+ target.y,
238
+ target.z,
239
+ false
240
+ );
241
+ }
242
+ }
243
+ /**
244
+ * Moves the camera to a new position and target.
245
+ */
246
+ moveCamera(position, target, enableTransition = true) {
247
+ toVector3(position, [0, 0, 0], tempVec3A);
248
+ toVector3(target, [0, 0, 0], tempVec3B);
249
+ return this.setLookAt(
250
+ tempVec3A.x,
251
+ tempVec3A.y,
252
+ tempVec3A.z,
253
+ tempVec3B.x,
254
+ tempVec3B.y,
255
+ tempVec3B.z,
256
+ enableTransition
257
+ );
258
+ }
259
+ /**
260
+ * Updates the controls using an internally managed clock.
261
+ * Useful when you don't want to pass delta time each frame.
262
+ */
263
+ updateDelta() {
264
+ const delta = this.updateClock.getDelta();
265
+ return super.update(delta);
266
+ }
267
+ updateInputBindingsForMode(mode) {
268
+ const { ACTION } = import_camera_controls.default;
269
+ if (mode === "orthographic") {
270
+ this.mouseButtons.left = ACTION.TRUCK;
271
+ this.mouseButtons.right = ACTION.ROTATE;
272
+ this.mouseButtons.wheel = ACTION.ZOOM;
273
+ this.touches.one = ACTION.TOUCH_TRUCK;
274
+ this.touches.two = ACTION.TOUCH_ZOOM_TRUCK;
275
+ } else {
276
+ this.mouseButtons.left = ACTION.ROTATE;
277
+ this.mouseButtons.right = ACTION.TRUCK;
278
+ this.mouseButtons.wheel = ACTION.DOLLY;
279
+ this.touches.one = ACTION.TOUCH_ROTATE;
280
+ this.touches.two = ACTION.TOUCH_DOLLY_TRUCK;
281
+ }
282
+ }
283
+ updateOrthographicFrustum(position, target, aspect) {
284
+ const distance = Math.max(position.distanceTo(target), 1e-3);
285
+ const fov = this.perspectiveCamera.fov;
286
+ const halfHeight = Math.max(
287
+ distance * Math.tan(THREE.MathUtils.degToRad(fov * 0.5)),
288
+ this.minOrthoHalfHeight
289
+ );
290
+ const halfWidth = halfHeight * aspect;
291
+ this.orthographicCamera.left = -halfWidth;
292
+ this.orthographicCamera.right = halfWidth;
293
+ this.orthographicCamera.top = halfHeight;
294
+ this.orthographicCamera.bottom = -halfHeight;
295
+ this.orthographicCamera.updateProjectionMatrix();
296
+ }
297
+ };
298
+ function resolveAspect(renderer, domElement) {
299
+ const size = renderer.getSize(tempVec2);
300
+ if (size.y > 0) {
301
+ return size.x / size.y;
302
+ }
303
+ const { clientWidth, clientHeight } = domElement;
304
+ if (clientHeight > 0) {
305
+ return clientWidth / clientHeight;
306
+ }
307
+ return 1;
308
+ }
309
+ // Annotate the CommonJS export names for ESM import in node:
310
+ 0 && (module.exports = {
311
+ DualCameraControls
312
+ });
313
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../packages/camera/src/index.ts","../../packages/camera/src/DualCameraControls.ts"],"sourcesContent":["export * from './DualCameraControls'\n","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\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\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(\n perspectiveConfig.position,\n [12, 12, 12],\n new THREE.Vector3()\n )\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(\n orthographicConfig.position,\n [12, 12, 12],\n new THREE.Vector3()\n )\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') {\n return\n }\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') {\n return\n }\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 * 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":["CameraControls"]}