Merge remote-tracking branch 'origin/main-dev' into main-demo

This commit is contained in:
2025-08-01 18:12:12 +05:30
16 changed files with 359 additions and 431 deletions

View File

@@ -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<void> {
// 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<Types.WallItem>((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<Types.WallItem>((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;

View File

@@ -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 (
<Models />
<Models loader={loader} />
)
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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) => (
<Model key={asset.modelUuid} asset={asset} isRendered={renderMap[asset.modelUuid] ?? false} />
<Model key={asset.modelUuid} asset={asset} isRendered={renderMap[asset.modelUuid] ?? false} loader={loader} />
))}
</group>
);

View File

@@ -95,7 +95,7 @@ export default function Builder() {
<AssetsGroup plane={plane} />
{/* <mesh name='Walls-And-WallAssets-Group'>
<mesh name='Walls-And-WallAssets-Group'>
<Geometry ref={csgRef} useGroups>
<WallGroup />
@@ -103,7 +103,7 @@ export default function Builder() {
<WallAssetGroup />
</Geometry>
</mesh> */}
</mesh>
<AislesGroup />
@@ -113,9 +113,9 @@ export default function Builder() {
<MeasurementTool />
{/* <CalculateAreaGroup /> */}
<CalculateAreaGroup />
{/* <NavMesh /> */}
<NavMesh />
<DxfFile />

View File

@@ -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

View File

@@ -17,295 +17,292 @@ import setCameraView from "../functions/setCameraView";
import { getUserData } from "../../../functions/getUserData";
const CamModelsGroup = () => {
const navigate = useNavigate();
const groupRef = useRef<THREE.Group>(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<any[]>([]);
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<THREE.Group>(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 (
<group ref={groupRef} name="Cam-Model-Group">
{cams.map((cam, index) => (
<primitive
key={cam.uuid}
//eslint-disable-next-line
object={cam}
visible={
selectedUser?.name !== cam.userData.userName &&
activeModule !== "visualization" &&
!isPlaying
}
const [cams, setCams] = useState<any[]>([]);
const [models, setModels] = useState<
Record<
string,
{
targetPosition: THREE.Vector3;
targetRotation: THREE.Euler;
target: THREE.Vector3;
}
>
<Html
as="div"
center
zIndexRange={[1, 0]}
sprite
style={{
color: "white",
textAlign: "center",
fontFamily: "Arial, sans-serif",
display: `${activeModule !== "visualization" ? "" : "none"}`,
opacity: `${selectedUser?.name !== cam.userData.userName && !isPlaying
? 1
: 0
}`,
transition: "opacity .2s ease",
}}
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={cam.userData.userImage ?? ""}
userName={cam.userData.userName}
id={cam.userData._id}
color={getAvatarColor(index, cam.userData.userName)}
position={cam.position}
rotation={cam.rotation}
target={cam.userData.target}
/>
</Html>
</primitive>
))}
</group>
);
>({});
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 (
<group ref={groupRef} name="Cam-Model-Group">
{cams.map((cam, index) => (
<primitive
key={cam.uuid}
//eslint-disable-next-line
object={cam}
visible={
selectedUser?.name !== cam.userData.userName &&
activeModule !== "visualization" &&
!isPlaying
}
>
<Html
as="div"
center
zIndexRange={[1, 0]}
sprite
style={{
color: "white",
textAlign: "center",
fontFamily: "Arial, sans-serif",
display: `${activeModule !== "visualization" ? "" : "none"}`,
opacity: `${selectedUser?.name !== cam.userData.userName && !isPlaying
? 1
: 0
}`,
transition: "opacity .2s ease",
}}
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={cam.userData.userImage ?? ""}
userName={cam.userData.userName}
id={cam.userData._id}
color={getAvatarColor(index, cam.userData.userName)}
position={cam.position}
rotation={cam.rotation}
target={cam.userData.target}
/>
</Html>
</primitive>
))}
</group>
);
};
export default CamModelsGroup;

View File

@@ -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}

View File

@@ -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();

View File

@@ -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],
});

View File

@@ -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 (

View File

@@ -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<THREE.Vector3[] | null>(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<PointWithDegree[]>([]);
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(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];

View File

@@ -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}
/>

View File

@@ -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) {

View File

@@ -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';