@mapcomponents/three 1.7.2 → 1.7.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 (73) hide show
  1. package/dist/README.md +54 -0
  2. package/dist/assets/3D/godzilla_simple.glb +0 -0
  3. package/dist/assets/splats/output.splat +0 -0
  4. package/dist/components/MlThreeGizmo.d.ts +12 -0
  5. package/dist/components/MlThreeGizmo.d.ts.map +1 -0
  6. package/dist/components/MlThreeModelLayer/MlThreeModelLayer.cy.d.ts +2 -0
  7. package/dist/components/MlThreeModelLayer/MlThreeModelLayer.cy.d.ts.map +1 -0
  8. package/dist/components/MlThreeModelLayer/MlThreeModelLayer.d.ts +12 -0
  9. package/dist/components/MlThreeModelLayer/MlThreeModelLayer.d.ts.map +1 -0
  10. package/dist/components/MlThreeObjectControls.d.ts +41 -0
  11. package/dist/components/MlThreeObjectControls.d.ts.map +1 -0
  12. package/dist/components/MlThreeSplatLayer/MlThreeSplatLayer.cy.d.ts +2 -0
  13. package/dist/components/MlThreeSplatLayer/MlThreeSplatLayer.cy.d.ts.map +1 -0
  14. package/dist/components/MlThreeSplatLayer/MlThreeSplatLayer.d.ts +12 -0
  15. package/dist/components/MlThreeSplatLayer/MlThreeSplatLayer.d.ts.map +1 -0
  16. package/dist/contexts/ThreeContext.d.ts +15 -0
  17. package/dist/contexts/ThreeContext.d.ts.map +1 -0
  18. package/dist/contexts/ThreeProvider.d.ts +18 -0
  19. package/dist/contexts/ThreeProvider.d.ts.map +1 -0
  20. package/dist/cypress/support/commands.d.ts +1 -0
  21. package/dist/cypress/support/commands.d.ts.map +1 -0
  22. package/dist/cypress/support/component.d.ts +9 -0
  23. package/dist/cypress/support/component.d.ts.map +1 -0
  24. package/dist/decorators/ThreejsContextDecorator.d.ts +3 -0
  25. package/dist/decorators/ThreejsContextDecorator.d.ts.map +1 -0
  26. package/dist/hooks/useThreeModel.d.ts +34 -0
  27. package/dist/hooks/useThreeModel.d.ts.map +1 -0
  28. package/dist/html2canvas.esm-CUkZERmf.js +22 -0
  29. package/dist/html2canvas.esm-Dmi1NfiH.mjs +4871 -0
  30. package/dist/index-CnnmRv4J.mjs +90875 -0
  31. package/dist/index-DbqgNSpy.js +5442 -0
  32. package/dist/index.css +1 -0
  33. package/dist/index.d.ts +10 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.es-BPSzkkDP.mjs +6674 -0
  36. package/dist/index.es-ajYPKLNS.js +18 -0
  37. package/dist/index.js +1 -0
  38. package/dist/index.mjs +10 -0
  39. package/dist/lib/ThreejsSceneHelper.d.ts +16 -0
  40. package/dist/lib/ThreejsSceneHelper.d.ts.map +1 -0
  41. package/dist/lib/ThreejsSceneRenderer.d.ts +18 -0
  42. package/dist/lib/ThreejsSceneRenderer.d.ts.map +1 -0
  43. package/dist/lib/ThreejsUtils.d.ts +14 -0
  44. package/dist/lib/ThreejsUtils.d.ts.map +1 -0
  45. package/dist/lib/splats/GaussianSplattingMesh.d.ts +104 -0
  46. package/dist/lib/splats/GaussianSplattingMesh.d.ts.map +1 -0
  47. package/dist/lib/splats/GaussianSplattingShaders.d.ts +10 -0
  48. package/dist/lib/splats/GaussianSplattingShaders.d.ts.map +1 -0
  49. package/dist/lib/splats/loaders/PlySplatLoader.d.ts +81 -0
  50. package/dist/lib/splats/loaders/PlySplatLoader.d.ts.map +1 -0
  51. package/dist/lib/splats/loaders/SplatLoader.d.ts +10 -0
  52. package/dist/lib/splats/loaders/SplatLoader.d.ts.map +1 -0
  53. package/dist/lib/utils/coroutine.d.ts +17 -0
  54. package/dist/lib/utils/coroutine.d.ts.map +1 -0
  55. package/dist/package.json +30 -0
  56. package/dist/purify.es-D1I7B1hP.js +2 -0
  57. package/dist/purify.es-DHbHSKL1.mjs +528 -0
  58. package/package.json +13 -7
  59. package/src/components/{MlTransformControls.tsx → MlThreeGizmo.tsx} +4 -4
  60. package/src/components/MlThreeModelLayer/MlThreeModelLayer.cy.tsx +2 -3
  61. package/src/components/MlThreeModelLayer/MlThreeModelLayer.stories.tsx +14 -55
  62. package/src/components/MlThreeModelLayer/MlThreeModelLayer.tsx +30 -133
  63. package/src/components/MlThreeObjectControls.tsx +289 -0
  64. package/src/components/MlThreeSplatLayer/MlThreeSplatLayer.cy.tsx +2 -3
  65. package/src/components/MlThreeSplatLayer/MlThreeSplatLayer.stories.tsx +14 -58
  66. package/src/components/MlThreeSplatLayer/MlThreeSplatLayer.tsx +26 -138
  67. package/src/decorators/ThreejsContextDecorator.tsx +1 -1
  68. package/src/hooks/useThreeModel.tsx +179 -0
  69. package/src/index.ts +4 -2
  70. package/vite.config.ts +2 -2
  71. package/src/components/ThreeObjectControls.tsx +0 -197
  72. /package/src/{components → contexts}/ThreeContext.tsx +0 -0
  73. /package/src/{components → contexts}/ThreeProvider.tsx +0 -0
@@ -3,9 +3,8 @@ import Button from '@mui/material/Button';
3
3
  import MlThreeModelLayer from './MlThreeModelLayer';
4
4
  import { useMap, TopToolbar, Sidebar } from '@mapcomponents/react-maplibre';
5
5
  import ThreejsContextDecorator from '../../decorators/ThreejsContextDecorator';
6
- import { useThree } from '../ThreeContext';
7
- import { ThreeObjectControls } from '../ThreeObjectControls';
8
- import ThreejsUtils from '../../lib/ThreejsUtils';
6
+ import { useThree } from '../../contexts/ThreeContext';
7
+ import { MlThreeObjectControls } from '../MlThreeObjectControls';
9
8
  import * as THREE from 'three';
10
9
 
11
10
  const storyoptions = {
@@ -48,17 +47,14 @@ const Lights = () => {
48
47
  };
49
48
 
50
49
  const Template: any = () => {
51
- const { worldMatrix } = useThree();
52
50
  const [showLayer, setShowLayer] = useState(true);
53
51
  const [scale, setScale] = useState(1);
54
52
  const [rotation, setRotation] = useState({ x: 90, y: 90, z: 0 });
55
- const [useMapCoords, setUseMapCoords] = useState(true);
56
53
  const [mapPosition, setMapPosition] = useState({ lng: 7.097, lat: 50.7355 });
57
- const [altitude, setAltitude] = useState(0);
58
54
  const [position, setPosition] = useState({ x: 0, y: 0, z: 0 });
55
+ const [sidebarOpen, setSidebarOpen] = useState(true);
59
56
  const [enableTransformControls, setEnableTransformControls] = useState(false);
60
57
  const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | 'scale'>('translate');
61
- const [sidebarOpen, setSidebarOpen] = useState(true);
62
58
 
63
59
  const mapHook = useMap({ mapId: 'map_1' });
64
60
  useEffect(() => {
@@ -68,55 +64,22 @@ const Template: any = () => {
68
64
  mapHook.map?.setCenter([7.097, 50.7355]);
69
65
  }, [mapHook.map]);
70
66
 
71
- // Center map on position when switching coordinate modes
72
- useEffect(() => {
73
- if (!mapHook.map) return;
74
- if (useMapCoords) {
75
- mapHook.map.setCenter([mapPosition.lng, mapPosition.lat]);
76
- }
77
- // eslint-disable-next-line react-hooks/exhaustive-deps
78
- }, [useMapCoords, mapHook.map]);
79
-
80
- const handleTransformChange = (object: THREE.Object3D) => {
81
- setRotation({
82
- x: (object.rotation.x * 180) / Math.PI,
83
- y: (object.rotation.y * 180) / Math.PI,
84
- z: (object.rotation.z * 180) / Math.PI,
85
- });
86
- setScale(object.scale.x);
87
-
88
- if (useMapCoords && worldMatrix) {
89
- const [lng, lat, alt] = ThreejsUtils.toMapPosition(worldMatrix, object.position);
90
- setMapPosition({ lng, lat });
91
- setAltitude(alt);
92
- } else {
93
- setPosition({ x: object.position.x, y: object.position.y, z: object.position.z });
94
- }
95
- };
96
-
97
67
  return (
98
68
  <>
99
69
  <Lights />
100
70
  {showLayer && (
101
71
  <MlThreeModelLayer
102
72
  url="assets/3D/godzilla_simple.glb"
103
- rotation={{
104
- x: (rotation.x * Math.PI) / 180,
105
- y: (rotation.y * Math.PI) / 180,
106
- z: (rotation.z * Math.PI) / 180,
73
+ position={[mapPosition.lng, mapPosition.lat]}
74
+ transform={{
75
+ rotation: {
76
+ x: (rotation.x * Math.PI) / 180,
77
+ y: (rotation.y * Math.PI) / 180,
78
+ z: (rotation.z * Math.PI) / 180,
79
+ },
80
+ scale: scale,
81
+ position: position,
107
82
  }}
108
- scale={scale}
109
- enableTransformControls={enableTransformControls}
110
- transformMode={transformMode}
111
- onTransformChange={handleTransformChange}
112
- {...(useMapCoords
113
- ? {
114
- mapPosition: [mapPosition.lng, mapPosition.lat],
115
- altitude: altitude,
116
- }
117
- : {
118
- position: position,
119
- })}
120
83
  />
121
84
  )}
122
85
 
@@ -131,26 +94,22 @@ const Template: any = () => {
131
94
  }
132
95
  />
133
96
  <Sidebar open={sidebarOpen} setOpen={setSidebarOpen} name="3D Model Config">
134
- <ThreeObjectControls
97
+ <MlThreeObjectControls
135
98
  showLayer={showLayer}
136
99
  setShowLayer={setShowLayer}
137
100
  scale={scale}
138
101
  setScale={setScale}
139
102
  rotation={rotation}
140
103
  setRotation={setRotation}
141
- useMapCoords={useMapCoords}
142
- setUseMapCoords={setUseMapCoords}
143
104
  mapPosition={mapPosition}
144
105
  setMapPosition={setMapPosition}
145
- altitude={altitude}
146
- setAltitude={setAltitude}
147
106
  position={position}
148
107
  setPosition={setPosition}
108
+ layerName="Model"
149
109
  enableTransformControls={enableTransformControls}
150
110
  setEnableTransformControls={setEnableTransformControls}
151
111
  transformMode={transformMode}
152
112
  setTransformMode={setTransformMode}
153
- layerName="Model"
154
113
  />
155
114
  </Sidebar>
156
115
  </>
@@ -1,152 +1,49 @@
1
- import { useEffect, useRef, useState } from 'react';
2
- import * as THREE from 'three';
1
+ import { useMemo } from 'react';
3
2
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
4
3
  import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
5
- import { LngLatLike } from 'maplibre-gl';
6
- import { useThree } from '../ThreeContext';
7
- import ThreejsUtils from '../../lib/ThreejsUtils';
8
- import MlTransformControls from '../MlTransformControls';
4
+ import { useThreeModel, UseThreeModelProps, ModelLoader } from '../../hooks/useThreeModel';
9
5
 
10
6
  /**
11
- * Renders obj or gltf 3D Models on the MapLibreMap referenced by props.mapId
7
+ * Renders obj or gltf 3D Models on the MapLibreMap
12
8
  *
13
9
  * @component
14
10
  */
15
11
 
16
- export interface MlThreeModelLayerProps {
12
+ export type MlThreeModelLayerProps = Omit<UseThreeModelProps, 'loaders'> & {
17
13
  mapId?: string;
18
- url: string;
19
- position?: { x: number; y: number; z: number };
20
- mapPosition?: LngLatLike;
21
- altitude?: number;
22
- rotation?: { x: number; y: number; z: number };
23
- scale?: { x: number; y: number; z: number } | number;
24
- enableTransformControls?: boolean;
25
- transformMode?: 'translate' | 'rotate' | 'scale';
26
- onTransformChange?: (object: THREE.Object3D) => void;
27
- init?: () => void;
28
- onDone?: () => void;
29
- }
14
+ };
30
15
 
31
16
  const MlThreeModelLayer = (props: MlThreeModelLayerProps) => {
32
- const {
17
+ const { url, position, transform, init, onDone, customLoaders } = props;
18
+
19
+ const loaders = useMemo<Record<string, ModelLoader>>(
20
+ () => ({
21
+ gltf: (url, onLoad) => {
22
+ const loader = new GLTFLoader();
23
+ loader.load(url, (gltf) => onLoad(gltf.scene));
24
+ },
25
+ glb: (url, onLoad) => {
26
+ const loader = new GLTFLoader();
27
+ loader.load(url, (gltf) => onLoad(gltf.scene));
28
+ },
29
+ obj: (url, onLoad) => {
30
+ const loader = new OBJLoader();
31
+ loader.load(url, (obj) => onLoad(obj));
32
+ },
33
+ }),
34
+ []
35
+ );
36
+
37
+ useThreeModel({
33
38
  url,
34
39
  position,
35
- mapPosition,
36
- altitude,
37
- rotation,
38
- scale,
39
- enableTransformControls,
40
- transformMode,
41
- onTransformChange,
40
+ transform,
42
41
  init,
43
42
  onDone,
44
- } = props;
45
- const { scene, worldMatrixInv } = useThree();
46
- const modelRef = useRef<THREE.Object3D | undefined>(undefined);
47
- const [model, setModel] = useState<THREE.Object3D | undefined>(undefined);
48
-
49
- // Use refs for callbacks to avoid re-triggering the effect when they change
50
- const initRef = useRef(init);
51
- const onDoneRef = useRef(onDone);
52
- initRef.current = init;
53
- onDoneRef.current = onDone;
54
-
55
- const transformRef = useRef({ position, mapPosition, altitude, rotation, scale });
56
- transformRef.current = { position, mapPosition, altitude, rotation, scale };
57
- const worldMatrixInvRef = useRef(worldMatrixInv);
58
- worldMatrixInvRef.current = worldMatrixInv;
59
-
60
- useEffect(() => {
61
- if (!scene) return;
62
-
63
- if (typeof initRef.current === 'function') {
64
- initRef.current();
65
- }
66
-
67
- const extension = url.split('.').pop()?.toLowerCase();
68
-
69
- const onLoad = (object: THREE.Object3D) => {
70
- const { position, mapPosition, altitude, rotation, scale } = transformRef.current;
71
- const worldMatrixInv = worldMatrixInvRef.current;
72
-
73
- if (mapPosition && worldMatrixInv) {
74
- const scenePos = ThreejsUtils.toScenePosition(worldMatrixInv, mapPosition, altitude);
75
- object.position.set(scenePos.x, scenePos.y, scenePos.z);
76
- } else if (position) {
77
- object.position.set(position.x, position.y, position.z);
78
- }
79
-
80
- if (rotation) {
81
- object.rotation.set(rotation.x, rotation.y, rotation.z);
82
- }
83
- if (scale) {
84
- if (typeof scale === 'number') {
85
- object.scale.set(scale, scale, scale);
86
- } else {
87
- object.scale.set(scale.x, scale.y, scale.z);
88
- }
89
- }
90
-
91
- modelRef.current = object;
92
- scene.add(object);
93
- setModel(object);
94
- if (typeof onDoneRef.current === 'function') {
95
- onDoneRef.current();
96
- }
97
- };
98
-
99
- if (extension === 'glb' || extension === 'gltf') {
100
- const loader = new GLTFLoader();
101
- loader.load(url, (gltf) => {
102
- onLoad(gltf.scene);
103
- });
104
- } else if (extension === 'obj') {
105
- const loader = new OBJLoader();
106
- loader.load(url, (obj) => {
107
- onLoad(obj);
108
- });
109
- } else {
110
- console.warn('MlThreeModelLayer: Unsupported file extension', extension);
111
- }
112
-
113
- return () => {
114
- if (modelRef.current) {
115
- scene.remove(modelRef.current);
116
- modelRef.current = undefined;
117
- setModel(undefined);
118
- }
119
- };
120
- }, [scene, url]);
121
-
122
- useEffect(() => {
123
- if (!model) return;
124
-
125
- // Handle position: mapPosition takes precedence over position
126
- if (mapPosition && worldMatrixInv) {
127
- const scenePos = ThreejsUtils.toScenePosition(worldMatrixInv, mapPosition, altitude);
128
- model.position.set(scenePos.x, scenePos.y, scenePos.z);
129
- } else if (position) {
130
- model.position.set(position.x, position.y, position.z);
131
- }
132
-
133
- if (rotation) {
134
- model.rotation.set(rotation.x, rotation.y, rotation.z);
135
- }
136
- if (scale) {
137
- if (typeof scale === 'number') {
138
- model.scale.set(scale, scale, scale);
139
- } else {
140
- model.scale.set(scale.x, scale.y, scale.z);
141
- }
142
- }
143
- }, [model, position, mapPosition, altitude, rotation, scale, worldMatrixInv]);
43
+ loaders,
44
+ customLoaders,
45
+ });
144
46
 
145
- if (enableTransformControls && model) {
146
- return (
147
- <MlTransformControls target={model} mode={transformMode} onObjectChange={onTransformChange} />
148
- );
149
- }
150
47
  return null;
151
48
  };
152
49
 
@@ -0,0 +1,289 @@
1
+ import { useRef, useLayoutEffect, useState } from 'react';
2
+ import Button from '@mui/material/Button';
3
+ import ButtonGroup from '@mui/material/ButtonGroup';
4
+ import Slider from '@mui/material/Slider';
5
+ import Typography from '@mui/material/Typography';
6
+ import Box from '@mui/material/Box';
7
+ import * as THREE from 'three';
8
+ import { LngLatLike } from 'maplibre-gl';
9
+ import MlThreeGizmo from './MlThreeGizmo';
10
+ import { useThree } from '../contexts/ThreeContext';
11
+ import ThreejsUtils from '../lib/ThreejsUtils';
12
+
13
+ export interface ThreeObjectControlsProps {
14
+ showLayer: boolean;
15
+ setShowLayer: (show: boolean) => void;
16
+ scale: number;
17
+ setScale: (scale: number) => void;
18
+ rotation: { x: number; y: number; z: number };
19
+ setRotation: (rotation: { x: number; y: number; z: number }) => void;
20
+ mapPosition: { lng: number; lat: number };
21
+ setMapPosition: (position: { lng: number; lat: number }) => void;
22
+ position: { x: number; y: number; z: number };
23
+ setPosition: (position: { x: number; y: number; z: number }) => void;
24
+ enableTransformControls?: boolean;
25
+ setEnableTransformControls?: (enable: boolean) => void;
26
+ transformMode?: 'translate' | 'rotate' | 'scale';
27
+ setTransformMode?: (mode: 'translate' | 'rotate' | 'scale') => void;
28
+ layerName?: string;
29
+ }
30
+
31
+ export const MlThreeObjectControls = ({
32
+ showLayer,
33
+ setShowLayer,
34
+ scale,
35
+ setScale,
36
+ rotation,
37
+ setRotation,
38
+ mapPosition,
39
+ setMapPosition,
40
+ position,
41
+ setPosition,
42
+ enableTransformControls,
43
+ setEnableTransformControls,
44
+ transformMode,
45
+ setTransformMode,
46
+ layerName = 'Layer',
47
+ }: ThreeObjectControlsProps) => {
48
+ const { scene, worldMatrixInv } = useThree();
49
+ const dummyMeshRef = useRef<THREE.Mesh | undefined>(undefined);
50
+ const [dummyMeshReady, setDummyMeshReady] = useState(false);
51
+
52
+ // Create and manage dummy mesh for transform controls
53
+ useLayoutEffect(() => {
54
+ if (!scene || !worldMatrixInv || !enableTransformControls) {
55
+ // Clean up dummy mesh when controls are disabled
56
+ if (dummyMeshRef.current) {
57
+ scene?.remove(dummyMeshRef.current);
58
+ dummyMeshRef.current.geometry.dispose();
59
+ (dummyMeshRef.current.material as THREE.Material).dispose();
60
+ dummyMeshRef.current = undefined;
61
+ setDummyMeshReady(false);
62
+ }
63
+ return;
64
+ }
65
+
66
+ // Create invisible dummy mesh at the model position
67
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
68
+ const material = new THREE.MeshBasicMaterial({ visible: false });
69
+ const dummyMesh = new THREE.Mesh(geometry, material);
70
+
71
+ // Position the dummy mesh
72
+ const scenePos = ThreejsUtils.toScenePosition(
73
+ worldMatrixInv,
74
+ [mapPosition.lng, mapPosition.lat] as LngLatLike,
75
+ 0
76
+ );
77
+ dummyMesh.position.set(
78
+ scenePos.x + position.x,
79
+ scenePos.y + position.y,
80
+ scenePos.z + position.z
81
+ );
82
+ dummyMesh.rotation.set(
83
+ (rotation.x * Math.PI) / 180,
84
+ (rotation.y * Math.PI) / 180,
85
+ (rotation.z * Math.PI) / 180
86
+ );
87
+ dummyMesh.scale.set(scale, scale, scale);
88
+
89
+ scene.add(dummyMesh);
90
+ dummyMeshRef.current = dummyMesh;
91
+ setDummyMeshReady(true);
92
+
93
+ return () => {
94
+ if (dummyMeshRef.current) {
95
+ scene.remove(dummyMeshRef.current);
96
+ dummyMeshRef.current.geometry.dispose();
97
+ (dummyMeshRef.current.material as THREE.Material).dispose();
98
+ dummyMeshRef.current = undefined;
99
+ setDummyMeshReady(false);
100
+ }
101
+ };
102
+ }, [scene, worldMatrixInv, enableTransformControls]);
103
+
104
+ // Update dummy mesh position when props change (but only when controls are enabled)
105
+ useLayoutEffect(() => {
106
+ if (!dummyMeshRef.current || !worldMatrixInv) return;
107
+
108
+ const scenePos = ThreejsUtils.toScenePosition(
109
+ worldMatrixInv,
110
+ [mapPosition.lng, mapPosition.lat] as LngLatLike,
111
+ 0
112
+ );
113
+ dummyMeshRef.current.position.set(
114
+ scenePos.x + position.x,
115
+ scenePos.y + position.y,
116
+ scenePos.z + position.z
117
+ );
118
+ dummyMeshRef.current.rotation.set(
119
+ (rotation.x * Math.PI) / 180,
120
+ (rotation.y * Math.PI) / 180,
121
+ (rotation.z * Math.PI) / 180
122
+ );
123
+ dummyMeshRef.current.scale.set(scale, scale, scale);
124
+ dummyMeshRef.current.updateMatrixWorld(true);
125
+ }, [position, rotation, scale, mapPosition, worldMatrixInv]);
126
+
127
+ const handleObjectChange = (object: THREE.Object3D) => {
128
+ if (!worldMatrixInv) return;
129
+
130
+ // Get the base scene position from map coordinates
131
+ const scenePos = ThreejsUtils.toScenePosition(
132
+ worldMatrixInv,
133
+ [mapPosition.lng, mapPosition.lat] as LngLatLike,
134
+ 0
135
+ );
136
+
137
+ // Calculate the offset position (object position - base scene position)
138
+ setPosition({
139
+ x: object.position.x - scenePos.x,
140
+ y: object.position.y - scenePos.y,
141
+ z: object.position.z - scenePos.z,
142
+ });
143
+
144
+ // Update rotation (convert from radians to degrees)
145
+ setRotation({
146
+ x: (object.rotation.x * 180) / Math.PI,
147
+ y: (object.rotation.y * 180) / Math.PI,
148
+ z: (object.rotation.z * 180) / Math.PI,
149
+ });
150
+
151
+ // Update scale (assuming uniform scale)
152
+ setScale(object.scale.x);
153
+ };
154
+
155
+ return (
156
+ <>
157
+ {dummyMeshReady && dummyMeshRef.current && enableTransformControls && (
158
+ <MlThreeGizmo
159
+ target={dummyMeshRef.current}
160
+ mode={transformMode || 'translate'}
161
+ enabled={enableTransformControls}
162
+ onObjectChange={handleObjectChange}
163
+ />
164
+ )}
165
+ <Box sx={{ padding: '10px' }}>
166
+ <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', marginBottom: 2 }}>
167
+ <Button
168
+ color="primary"
169
+ variant={showLayer ? 'contained' : 'outlined'}
170
+ onClick={() => setShowLayer(!showLayer)}
171
+ size="small"
172
+ >
173
+ {showLayer ? 'Hide' : 'Show'} {layerName}
174
+ </Button>
175
+ {setEnableTransformControls && (
176
+ <Button
177
+ color="info"
178
+ variant={enableTransformControls ? 'contained' : 'outlined'}
179
+ onClick={() => setEnableTransformControls(!enableTransformControls)}
180
+ size="small"
181
+ >
182
+ 3D Gizmo
183
+ </Button>
184
+ )}
185
+ </Box>
186
+
187
+ {setTransformMode && enableTransformControls && (
188
+ <Box sx={{ marginBottom: 2 }}>
189
+ <ButtonGroup variant="outlined" size="small" fullWidth aria-label="transform mode">
190
+ <Button
191
+ variant={transformMode === 'translate' ? 'contained' : 'outlined'}
192
+ onClick={() => setTransformMode('translate')}
193
+ >
194
+ Move
195
+ </Button>
196
+ <Button
197
+ variant={transformMode === 'rotate' ? 'contained' : 'outlined'}
198
+ onClick={() => setTransformMode('rotate')}
199
+ >
200
+ Rotate
201
+ </Button>
202
+ <Button
203
+ variant={transformMode === 'scale' ? 'contained' : 'outlined'}
204
+ onClick={() => setTransformMode('scale')}
205
+ >
206
+ Scale
207
+ </Button>
208
+ </ButtonGroup>
209
+ </Box>
210
+ )}
211
+ <Typography gutterBottom>Scale: {scale.toFixed(2)}</Typography>
212
+ <Slider
213
+ value={scale}
214
+ onChange={(e, newValue) => setScale(newValue as number)}
215
+ min={0.01}
216
+ max={150}
217
+ step={0.01}
218
+ valueLabelDisplay="auto"
219
+ />
220
+ <Typography gutterBottom>Rotation X: {rotation.x}°</Typography>
221
+ <Slider
222
+ value={rotation.x}
223
+ onChange={(e, newValue) => setRotation({ ...rotation, x: newValue as number })}
224
+ min={0}
225
+ max={360}
226
+ valueLabelDisplay="auto"
227
+ />
228
+ <Typography gutterBottom>Rotation Y: {rotation.y}°</Typography>
229
+ <Slider
230
+ value={rotation.y}
231
+ onChange={(e, newValue) => setRotation({ ...rotation, y: newValue as number })}
232
+ min={0}
233
+ max={360}
234
+ valueLabelDisplay="auto"
235
+ />
236
+ <Typography gutterBottom>Rotation Z: {rotation.z}°</Typography>
237
+ <Slider
238
+ value={rotation.z}
239
+ onChange={(e, newValue) => setRotation({ ...rotation, z: newValue as number })}
240
+ min={0}
241
+ max={360}
242
+ valueLabelDisplay="auto"
243
+ />
244
+ <Typography gutterBottom>Longitude: {mapPosition.lng.toFixed(6)}</Typography>
245
+ <Slider
246
+ value={mapPosition.lng}
247
+ onChange={(e, newValue) => setMapPosition({ ...mapPosition, lng: newValue as number })}
248
+ min={7.09}
249
+ max={7.11}
250
+ step={0.0001}
251
+ valueLabelDisplay="auto"
252
+ />
253
+ <Typography gutterBottom>Latitude: {mapPosition.lat.toFixed(6)}</Typography>
254
+ <Slider
255
+ value={mapPosition.lat}
256
+ onChange={(e, newValue) => setMapPosition({ ...mapPosition, lat: newValue as number })}
257
+ min={50.73}
258
+ max={50.74}
259
+ step={0.0001}
260
+ valueLabelDisplay="auto"
261
+ />
262
+ <Typography gutterBottom>Position X: {position.x}</Typography>
263
+ <Slider
264
+ value={position.x}
265
+ onChange={(e, newValue) => setPosition({ ...position, x: newValue as number })}
266
+ min={-100}
267
+ max={100}
268
+ valueLabelDisplay="auto"
269
+ />
270
+ <Typography gutterBottom>Position Y: {position.y}</Typography>
271
+ <Slider
272
+ value={position.y}
273
+ onChange={(e, newValue) => setPosition({ ...position, y: newValue as number })}
274
+ min={-100}
275
+ max={100}
276
+ valueLabelDisplay="auto"
277
+ />
278
+ <Typography gutterBottom>Position Z: {position.z}</Typography>
279
+ <Slider
280
+ value={position.z}
281
+ onChange={(e, newValue) => setPosition({ ...position, z: newValue as number })}
282
+ min={-500}
283
+ max={100}
284
+ valueLabelDisplay="auto"
285
+ />
286
+ </Box>
287
+ </>
288
+ );
289
+ };
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import { MapComponentsProvider, MapLibreMap, useMap } from '@mapcomponents/react-maplibre';
3
- import { ThreeProvider } from '../ThreeProvider';
3
+ import { ThreeProvider } from '../../contexts/ThreeProvider';
4
4
  import MlThreeSplatLayer from './MlThreeSplatLayer';
5
5
 
6
6
  const MapExposer = () => {
@@ -20,8 +20,7 @@ const TestComponent = ({ onDone }: { onDone: () => void }) => {
20
20
  <ThreeProvider id="three-provider" mapId="map_1">
21
21
  <MlThreeSplatLayer
22
22
  url="assets/splats/output.splat"
23
- mapPosition={[13.404954, 52.520008]}
24
- scale={1}
23
+ position={[13.404954, 52.520008]}
25
24
  onDone={onDone}
26
25
  />
27
26
  </ThreeProvider>