diff --git a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts deleted file mode 100644 index 80fdcb9..0000000 --- a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts +++ /dev/null @@ -1,110 +0,0 @@ -// import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -// import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -// import * as THREE from "three"; -// import * as Types from "../../../types/world/worldTypes"; -// import { getWallItems } from "../../../services/factoryBuilder/asset/wallAsset/getWallItemsApi"; -// import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; -// import { getUserData } from "../../../functions/getUserData"; - -// async function loadInitialWallItems( -// setWallItems: Types.setWallItemSetState, -// projectId?: string, -// versionId?: string -// ): Promise { -// if (!projectId || !versionId) return; -// try { -// const { organization, email } = getUserData(); - -// if (!email) { -// console.error("No email found in localStorage"); -// } - -// const items = await getWallItems(organization, projectId, versionId); - -// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - -// if (!items || items.length === 0) { -// localStorage.removeItem("WallItems"); -// return; -// } - -// localStorage.setItem("WallItems", JSON.stringify(items)); - -// const loader = new GLTFLoader(); -// const dracoLoader = new DRACOLoader(); -// dracoLoader.setDecoderPath( -// "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" -// ); -// loader.setDRACOLoader(dracoLoader); - -// const loadedWallItems = await Promise.all( -// items.map(async (item: Types.WallItem) => { -// // Check THREE.js cache first -// const cachedModel = THREE.Cache.get(item.assetId!); -// if (cachedModel) { -// return processModel(cachedModel, item); -// } - -// // Check IndexedDB cache -// const cachedModelBlob = await retrieveGLTF(item.assetId!); -// if (cachedModelBlob) { -// const blobUrl = URL.createObjectURL(cachedModelBlob); -// return new Promise((resolve) => { -// loader.load(blobUrl, (gltf) => { -// URL.revokeObjectURL(blobUrl); -// THREE.Cache.add(item.assetId!, gltf); -// resolve(processModel(gltf, item)); -// }); -// }); -// } - -// // Load from original URL if not cached -// const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.assetId!}`; -// return new Promise((resolve) => { -// loader.load(modelUrl, async (gltf) => { -// try { -// // Cache the model -// const modelBlob = await fetch(modelUrl).then((res) => res.blob()); -// await storeGLTF(item.assetId!, modelBlob); -// THREE.Cache.add(item.assetId!, gltf); -// resolve(processModel(gltf, item)); -// } catch (error) { -// console.error("Failed to cache model:", error); -// resolve(processModel(gltf, item)); -// } -// }); -// }); -// }) -// ); - -// setWallItems(loadedWallItems); -// } catch (error) { -// console.error("Failed to load wall items:", error); -// } -// } - -// function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem { -// const model = gltf.scene.clone(); -// model.uuid = item.modelUuid!; - -// model.children[0]?.children?.forEach((child: THREE.Object3D) => { -// if (child.name !== "CSG_REF") { -// child.castShadow = true; -// child.receiveShadow = true; -// } -// }); - -// return { -// type: item.type, -// model: model, -// modelName: item.modelName, -// assetId: item.assetId, -// scale: item.scale, -// csgscale: item.csgscale, -// csgposition: item.csgposition, -// position: item.position, -// quaternion: item.quaternion, -// }; -// } - -// export default loadInitialWallItems; diff --git a/app/src/modules/builder/asset/assetsGroup.tsx b/app/src/modules/builder/asset/assetsGroup.tsx index 0215a9f..6c06be2 100644 --- a/app/src/modules/builder/asset/assetsGroup.tsx +++ b/app/src/modules/builder/asset/assetsGroup.tsx @@ -44,6 +44,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) { useEffect(() => { if (!projectId || !selectedVersion) return; + clearEvents(); let totalAssets = 0; @@ -304,7 +305,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) { pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - addAssetModel(scene, raycaster, camera, pointer, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, selectedVersion, projectId, userId); + addAssetModel(scene, raycaster, camera, pointer, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, loader, selectedVersion, projectId, userId); } }; @@ -348,7 +349,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) { }, [selectedItem, camera, activeModule, controls, isRenameMode]); return ( - + ) } diff --git a/app/src/modules/builder/asset/functions/addAssetModel.ts b/app/src/modules/builder/asset/functions/addAssetModel.ts index 80d8b0a..bb66c6a 100644 --- a/app/src/modules/builder/asset/functions/addAssetModel.ts +++ b/app/src/modules/builder/asset/functions/addAssetModel.ts @@ -20,6 +20,7 @@ async function addAssetModel( addEvent: (event: EventsSchema) => void, addAsset: (asset: Asset) => void, plane: Types.RefMesh, + loader: GLTFLoader, selectedVersion?: Version | null, projectId?: string, userId?: string @@ -29,12 +30,6 @@ async function addAssetModel( let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; try { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"); - loader.setDRACOLoader(dracoLoader); - raycaster.setFromCamera(pointer, camera); const wallFloorsGroup = scene.getObjectByName("Walls-Floors-Group") as Types.Group | null; const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null; diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 9cc0af4..16f37ee 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -2,7 +2,6 @@ import * as THREE from 'three'; import { useCallback, useEffect, useRef, useState } from 'react'; import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { ThreeEvent, useThree } from '@react-three/fiber'; import { useActiveTool, useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { AssetBoundingBox } from '../../functions/assetBoundingBox'; @@ -21,8 +20,7 @@ import { upsertProductOrEventApi } from '../../../../../services/simulation/prod import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs'; import { ModelAnimator } from './animator/modelAnimator'; - -function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boolean }) { +function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendered: boolean, loader: GLTFLoader }) { const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; const savedTheme: string = localStorage.getItem("theme") || "light"; const { controls, gl } = useThree(); @@ -109,11 +107,6 @@ function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boole }, [isRendered, selectedFloorItem]) useEffect(() => { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - loader.setDRACOLoader(dracoLoader); const loadModel = async () => { try { diff --git a/app/src/modules/builder/asset/models/models.tsx b/app/src/modules/builder/asset/models/models.tsx index cdfa21b..5944b1c 100644 --- a/app/src/modules/builder/asset/models/models.tsx +++ b/app/src/modules/builder/asset/models/models.tsx @@ -7,10 +7,11 @@ import { useSelectedAsset } from '../../../../store/simulation/useSimulationStor import { useSceneContext } from '../../../scene/sceneContext'; import Model from './model/model'; +import { GLTFLoader } from "three/examples/jsm/Addons"; const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url)); -function Models() { +function Models({ loader }: { loader: GLTFLoader }) { const { controls, camera } = useThree(); const { assetStore } = useSceneContext(); const { assets } = assetStore(); @@ -57,7 +58,7 @@ function Models() { }} > {assets.map((asset) => ( - + ))} ); diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index d340a35..ab9434b 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -95,7 +95,7 @@ export default function Builder() { - {/* + @@ -103,7 +103,7 @@ export default function Builder() { - */} + @@ -113,9 +113,9 @@ export default function Builder() { - {/* */} + - {/* */} + diff --git a/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx index 1cf361e..2dfaa13 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx +++ b/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx @@ -44,6 +44,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); loader.setDRACOLoader(dracoLoader); + const loadModel = async () => { try { // Check Cache diff --git a/app/src/modules/collaboration/camera/collabCams.tsx b/app/src/modules/collaboration/camera/collabCams.tsx index 2cf09b1..ec313ec 100644 --- a/app/src/modules/collaboration/camera/collabCams.tsx +++ b/app/src/modules/collaboration/camera/collabCams.tsx @@ -17,295 +17,292 @@ import setCameraView from "../functions/setCameraView"; import { getUserData } from "../../../functions/getUserData"; const CamModelsGroup = () => { - const navigate = useNavigate(); - const groupRef = useRef(null); - const { userId, organization, email } = getUserData(); - const { setActiveUsers } = useActiveUsers(); - const { socket } = useSocketStore(); - const { activeModule } = useModuleStore(); - const { selectedUser, setSelectedUser } = useSelectedUserStore(); - const { isPlaying } = usePlayButtonStore(); - // eslint-disable-next-line react-hooks/exhaustive-deps - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); - loader.setDRACOLoader(dracoLoader); - - - const { camMode } = useCamMode(); - const { camera, controls } = useThree(); // Access R3F camera and controls - - useEffect(() => { - if (camMode !== "FollowPerson") return; - // If a user is selected, set the camera view to their location - // and update the camera and controls accordingly - if (selectedUser?.location) { - const { position, rotation, target } = selectedUser.location; - if (rotation && target) - setCameraView({ - controls, - camera, - position, - rotation, - target, - username: selectedUser.name, - }); - } - }, [selectedUser, camera, controls, camMode]); - - const [cams, setCams] = useState([]); - const [models, setModels] = useState< - Record< - string, - { - targetPosition: THREE.Vector3; - targetRotation: THREE.Euler; - target: THREE.Vector3; - } - > - >({}); - - const dedupeCams = (cams: any[]) => { - const seen = new Set(); - return cams.filter((cam) => { - if (seen.has(cam.uuid)) return false; - seen.add(cam.uuid); - return true; - }); - }; - - const dedupeUsers = (users: any[]) => { - const seen = new Set(); - return users.filter((user) => { - if (seen.has(user._id)) return false; - seen.add(user._id); - return true; - }); - }; - - useEffect(() => { - if (!email) navigate("/"); - - if (!socket) return; - - socket.on("userConnectResponse", (data: any) => { - if (!groupRef.current) return; - if (data.data.userData.email === email) return; - if (socket.id === data.socketId || organization !== data.organization) - return; - - const model = groupRef.current.getObjectByProperty( - "uuid", - data.data.userData._id - ); - if (model) { - groupRef.current.remove(model); - } - - loader.load(camModel, (gltf) => { - const newModel = gltf.scene.clone(); - newModel.uuid = data.data.userData._id; - newModel.position.set( - data.data.position.x, - data.data.position.y, - data.data.position.z - ); - newModel.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - newModel.userData = data.data.userData; - newModel.userData.target = new THREE.Vector3( - data.data.target.x, - data.data.target.y, - data.data.target.z - ); - - setCams((prev) => dedupeCams([...prev, newModel])); - setActiveUsers((prev: any) => - dedupeUsers([...prev, data.data.userData]) - ); - }); - }); - - socket.on("userDisConnectResponse", (data: any) => { - if (!groupRef.current) return; - if (socket.id === data.socketId || organization !== data.organization) - return; - - setCams((prev) => - prev.filter((cam) => cam.uuid !== data.data.userData._id) - ); - setActiveUsers((prev: any) => - prev.filter((user: any) => user._id !== data.data.userData._id) - ); - }); - - socket.on("v1:camera:Response:update", (data: any) => { - // console.log('data: ', data); - if ( - !groupRef.current || - socket.id === data.socketId || - organization !== data.organization - ) - return; - - if (selectedUser && selectedUser?.id === data.data.userId) { - setSelectedUser({ - color: selectedUser.color, - name: selectedUser.name, - id: selectedUser.id, - location: { - position: data.data.position, - rotation: data.data.rotation, - target: data.data.target, - }, - }); - } - setModels((prev) => ({ - ...prev, - [data.data.userId]: { - targetPosition: new THREE.Vector3( - data.data.position.x, - data.data.position.y, - data.data.position.z - ), - targetRotation: new THREE.Euler( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ), - target: new THREE.Vector3( - data.data.target.x, - data.data.target.y, - data.data.target.z - ), - }, - })); - }); - - return () => { - socket.off("userConnectResponse"); - socket.off("userDisConnectResponse"); - socket.off("v1:camera:Response:update"); - }; + const navigate = useNavigate(); + const groupRef = useRef(null); + const { organization, email } = getUserData(); + const { setActiveUsers } = useActiveUsers(); + const { socket } = useSocketStore(); + const { activeModule } = useModuleStore(); + const { selectedUser, setSelectedUser } = useSelectedUserStore(); + const { isPlaying } = usePlayButtonStore(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [email, loader, navigate, setActiveUsers, socket]); + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); + loader.setDRACOLoader(dracoLoader); - useFrame(() => { - if (!groupRef.current) return; - Object.keys(models).forEach((uuid) => { - const model = groupRef.current!.getObjectByProperty("uuid", uuid); - if (!model) return; + const { camMode } = useCamMode(); + const { camera, controls } = useThree(); - const { targetPosition, targetRotation } = models[uuid]; - model.position.lerp(targetPosition, 0.1); - model.rotation.x = THREE.MathUtils.lerp( - model.rotation.x, - targetRotation.x, - 0.1 - ); - model.rotation.y = THREE.MathUtils.lerp( - model.rotation.y, - targetRotation.y, - 0.1 - ); - model.rotation.z = THREE.MathUtils.lerp( - model.rotation.z, - targetRotation.z, - 0.1 - ); - }); - }); + useEffect(() => { + if (camMode !== "FollowPerson") return; + if (selectedUser?.location) { + const { position, rotation, target } = selectedUser.location; + if (rotation && target) + setCameraView({ + controls, + camera, + position, + rotation, + target, + username: selectedUser.name, + }); + } + }, [selectedUser, camera, controls, camMode]); - useEffect(() => { - if (!groupRef.current) return; - - getActiveUsersData(organization).then((data) => { - const filteredData = data.cameraDatas.filter( - (camera: any) => camera.userData.email !== email - ); - - if (filteredData.length > 0) { - loader.load(camModel, (gltf) => { - const newCams = filteredData.map((cam: any) => { - const newModel = gltf.scene.clone(); - newModel.uuid = cam.userData._id; - newModel.position.set( - cam.position.x, - cam.position.y, - cam.position.z - ); - newModel.rotation.set( - cam.rotation.x, - cam.rotation.y, - cam.rotation.z - ); - newModel.userData = cam.userData; - cam.userData.position = newModel.position; - cam.userData.rotation = newModel.rotation; - newModel.userData.target = cam.target; - - return newModel; - }); - - const users = filteredData.map((cam: any) => cam.userData); - setActiveUsers((prev: any) => dedupeUsers([...prev, ...users])); - setCams((prev) => dedupeCams([...prev, ...newCams])); - }); - } - }).catch(() => { - console.log('Error fetching active users data') - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - {cams.map((cam, index) => ( - ([]); + const [models, setModels] = useState< + Record< + string, + { + targetPosition: THREE.Vector3; + targetRotation: THREE.Euler; + target: THREE.Vector3; + } > - - - - - ))} - - ); + >({}); + + const dedupeCams = (cams: any[]) => { + const seen = new Set(); + return cams.filter((cam) => { + if (seen.has(cam.uuid)) return false; + seen.add(cam.uuid); + return true; + }); + }; + + const dedupeUsers = (users: any[]) => { + const seen = new Set(); + return users.filter((user) => { + if (seen.has(user._id)) return false; + seen.add(user._id); + return true; + }); + }; + + useEffect(() => { + if (!email) navigate("/"); + + if (!socket) return; + + socket.on("userConnectResponse", (data: any) => { + if (!groupRef.current) return; + if (data.data.userData.email === email) return; + if (socket.id === data.socketId || organization !== data.organization) + return; + + const model = groupRef.current.getObjectByProperty( + "uuid", + data.data.userData._id + ); + if (model) { + groupRef.current.remove(model); + } + + loader.load(camModel, (gltf) => { + const newModel = gltf.scene.clone(); + newModel.uuid = data.data.userData._id; + newModel.position.set( + data.data.position.x, + data.data.position.y, + data.data.position.z + ); + newModel.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + newModel.userData = data.data.userData; + newModel.userData.target = new THREE.Vector3( + data.data.target.x, + data.data.target.y, + data.data.target.z + ); + + setCams((prev) => dedupeCams([...prev, newModel])); + setActiveUsers((prev: any) => + dedupeUsers([...prev, data.data.userData]) + ); + }); + }); + + socket.on("userDisConnectResponse", (data: any) => { + if (!groupRef.current) return; + if (socket.id === data.socketId || organization !== data.organization) + return; + + setCams((prev) => + prev.filter((cam) => cam.uuid !== data.data.userData._id) + ); + setActiveUsers((prev: any) => + prev.filter((user: any) => user._id !== data.data.userData._id) + ); + }); + + socket.on("v1:camera:Response:update", (data: any) => { + // console.log('data: ', data); + if ( + !groupRef.current || + socket.id === data.socketId || + organization !== data.organization + ) + return; + + if (selectedUser && selectedUser?.id === data.data.userId) { + setSelectedUser({ + color: selectedUser.color, + name: selectedUser.name, + id: selectedUser.id, + location: { + position: data.data.position, + rotation: data.data.rotation, + target: data.data.target, + }, + }); + } + setModels((prev) => ({ + ...prev, + [data.data.userId]: { + targetPosition: new THREE.Vector3( + data.data.position.x, + data.data.position.y, + data.data.position.z + ), + targetRotation: new THREE.Euler( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ), + target: new THREE.Vector3( + data.data.target.x, + data.data.target.y, + data.data.target.z + ), + }, + })); + }); + + return () => { + socket.off("userConnectResponse"); + socket.off("userDisConnectResponse"); + socket.off("v1:camera:Response:update"); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [email, loader, navigate, setActiveUsers, socket]); + + useFrame(() => { + if (!groupRef.current) return; + Object.keys(models).forEach((uuid) => { + const model = groupRef.current!.getObjectByProperty("uuid", uuid); + if (!model) return; + + const { targetPosition, targetRotation } = models[uuid]; + model.position.lerp(targetPosition, 0.1); + model.rotation.x = THREE.MathUtils.lerp( + model.rotation.x, + targetRotation.x, + 0.1 + ); + model.rotation.y = THREE.MathUtils.lerp( + model.rotation.y, + targetRotation.y, + 0.1 + ); + model.rotation.z = THREE.MathUtils.lerp( + model.rotation.z, + targetRotation.z, + 0.1 + ); + }); + }); + + useEffect(() => { + if (!groupRef.current) return; + + getActiveUsersData(organization).then((data) => { + const filteredData = data.cameraDatas.filter( + (camera: any) => camera.userData.email !== email + ); + + if (filteredData.length > 0) { + loader.load(camModel, (gltf) => { + const newCams = filteredData.map((cam: any) => { + const newModel = gltf.scene.clone(); + newModel.uuid = cam.userData._id; + newModel.position.set( + cam.position.x, + cam.position.y, + cam.position.z + ); + newModel.rotation.set( + cam.rotation.x, + cam.rotation.y, + cam.rotation.z + ); + newModel.userData = cam.userData; + cam.userData.position = newModel.position; + cam.userData.rotation = newModel.rotation; + newModel.userData.target = cam.target; + + return newModel; + }); + + const users = filteredData.map((cam: any) => cam.userData); + setActiveUsers((prev: any) => dedupeUsers([...prev, ...users])); + setCams((prev) => dedupeCams([...prev, ...newCams])); + }); + } + }).catch(() => { + console.log('Error fetching active users data') + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + {cams.map((cam, index) => ( + + + + + + ))} + + ); }; export default CamModelsGroup; diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index b53d8b4..79b9c9f 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -122,7 +122,7 @@ export default function Controls() { ref={controlsRef} minDistance={toggleView ? CONSTANTS.twoDimension.minDistance : CONSTANTS.threeDimension.minDistance} maxDistance={CONSTANTS.thirdPersonControls.maxDistance} - minZoom={CONSTANTS.thirdPersonControls.minZoom} + minZoom={CONSTANTS.thirdPersonControls.minZoom} maxZoom={CONSTANTS.thirdPersonControls.maxZoom} maxPolarAngle={CONSTANTS.thirdPersonControls.maxPolarAngle} camera={state.camera} diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx index a147750..1466b3f 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx @@ -31,7 +31,7 @@ const CopyPasteControls3D = ({ const { assetStore, eventStore } = useSceneContext(); const { addEvent } = eventStore(); const { projectId } = useParams(); - const { assets, addAsset, setPosition, updateAsset, removeAsset, getAssetById } = assetStore(); + const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { userId, organization } = getUserData(); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx index 18f4abf..6f65300 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx @@ -339,7 +339,7 @@ function MoveControls3D({ } updateAsset(movedAsset.userData.modelUuid, { - position: asset.position, + position: [position.x, position.y, position.z], rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z], }); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 0e7a277..22e18ac 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -53,7 +53,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co }).catch((err) => { console.error(err); }); - // eslint-disable-next-line + // eslint-disable-next-line }, [activeModule, assets, loadingProgress]) return ( diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index c382230..1f81c2d 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,15 +1,27 @@ import { useEffect, useRef, useState } from 'react'; -import { useFrame } from '@react-three/fiber'; +import { useFrame, useThree } from '@react-three/fiber'; import * as THREE from 'three'; import { Line, Text } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; type PointWithDegree = { position: [number, number, number]; degree: number; }; -function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) { +interface RoboticArmAnimatorProps { + HandleCallback: () => void; + restPosition: THREE.Vector3; + ikSolver: any; + targetBone: string; + armBot: ArmBotStatus; + path: [number, number, number][]; + currentPhase: string; +} + +function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path, currentPhase }: RoboticArmAnimatorProps) { const progressRef = useRef(0); const curveRef = useRef(null); const totalDistanceRef = useRef(0); @@ -19,6 +31,14 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); const [circlePointsWithDegrees, setCirclePointsWithDegrees] = useState([]); const [customCurvePoints, setCustomCurvePoints] = useState(null); + const { armBotStore, productStore, materialStore } = useSceneContext(); + const { getArmBotById } = armBotStore(); + const { getMaterialById } = materialStore(); + const { getEventByModelUuid } = productStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { scene } = useThree(); + let curveHeight = 1.75 const CIRCLE_RADIUS = 1.6 @@ -145,8 +165,38 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone useEffect(() => { if (circlePoints.length > 0 && currentPath.length > 0) { - const start = currentPath[0]; - const end = currentPath[currentPath.length - 1]; + let start = currentPath[0]; + let end = currentPath[currentPath.length - 1]; + + const armbotStatus = getArmBotById(armBot.modelUuid); + const currentMaterial = armbotStatus?.currentAction?.materialId; + if (armbotStatus && currentMaterial && (currentPhase === 'rest-to-start' || currentPhase === 'start-to-end')) { + const materialData = getMaterialById(currentMaterial); + if (materialData) { + const prevModel = getEventByModelUuid(selectedProduct.productUuid, materialData.current.modelUuid); + + if (prevModel && prevModel.type === 'transfer') { + const material = scene.getObjectByProperty("uuid", currentMaterial); + const armbotModel = scene.getObjectByProperty("uuid", armBot.modelUuid); + if (material && armbotModel) { + const materialWorldPos = new THREE.Vector3(); + material.getWorldPosition(materialWorldPos); + + const armbotWorldPos = new THREE.Vector3(); + armbotModel.getWorldPosition(armbotWorldPos); + + const materialLocalPos = materialWorldPos.clone(); + armbotModel.worldToLocal(materialLocalPos); + + if (currentPhase === 'rest-to-start') { + end = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; + } else if (currentPhase === 'start-to-end') { + start = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; + } + } + } + } + } const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index b41bef8..e1595b6 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -334,7 +334,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { }, [currentPhase, armBot, isPlaying, isReset, ikSolver]) - function createCurveBetweenTwoPoints(p1: any, p2: any) { const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5); const points = [p1, mid, p2]; @@ -342,7 +341,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { } const HandleCallback = () => { - if (armBot.isActive && armBot.state == "running" && currentPhase == "init-to-rest") { logStatus(armBot.modelUuid, "Callback triggered: rest"); setArmBotActive(armBot.modelUuid, false) @@ -370,7 +368,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("rest"); setPath([]) - } } @@ -389,7 +386,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { ikSolver={ikSolver} targetBone={targetBone} armBot={armBot} - logStatus={logStatus} path={path} currentPhase={currentPhase} /> diff --git a/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts b/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts index 2f63234..ce2090f 100644 --- a/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts @@ -183,7 +183,11 @@ export default function useDraggableGLTF( targetPosition.z = centerZ + finalLocal.z; // Clamp Y axis using variables - targetPosition.y = Math.min(Math.max(targetPosition.y, minHeight), maxHeight); + + targetPosition.y = Math.min( + Math.max(targetPosition.y, Math.min(minHeight, maxHeight)), + Math.max(minHeight, maxHeight) + ); // Convert to local if parent exists if (parent) { diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 5ea19f9..2edfc58 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useFrame, useThree, ThreeEvent } from '@react-three/fiber'; import * as THREE from 'three'; -import { Line, TransformControls } from '@react-three/drei'; +import { Line } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useSceneContext } from '../../../../scene/sceneContext'; import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store';