Refactor success notifications to use echo instead of toast; update selection controls and duplication logic

- Changed success notifications from `toast.success` to `echo.success` in copy, duplication, move, rotate, and selection controls.
- Updated selection logic in `PostProcessing` to use `flattenChildren` for better performance.
- Enhanced `Simulator` component to handle selected product state and execution order more effectively.
- Modified `useSelectedItem` state to include `category` and `subCategory` properties for better item classification.
- Adjusted `WallItem` interface to standardize type values and added `modelfileID` for improved asset management.
This commit is contained in:
2025-05-13 17:21:52 +05:30
parent 48fc770b51
commit 2c67081173
28 changed files with 1257 additions and 1207 deletions

View File

@@ -140,16 +140,14 @@ const Assets: React.FC = () => {
alt={asset.filename} alt={asset.filename}
className="asset-image" className="asset-image"
onPointerDown={() => { onPointerDown={() => {
if (asset.category !== 'Feneration') { setSelectedItem({
setSelectedItem({ name: asset.filename,
name: asset.filename, id: asset.AssetID,
id: asset.AssetID, type:
type: asset.type === "undefined"
asset.type === "undefined" ? undefined
? undefined : asset.type
: asset.type, });
});
}
}} }}
/> />
@@ -206,6 +204,8 @@ const Assets: React.FC = () => {
asset.type === "undefined" asset.type === "undefined"
? undefined ? undefined
: asset.type, : asset.type,
category: asset.category,
subCategory: asset.subCategory
}); });
}} }}
/> />

View File

@@ -71,7 +71,6 @@ async function loadInitialFloorItems(
// Check Three.js Cache // Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!); const cachedModel = THREE.Cache.get(item.modelfileID!);
if (cachedModel) { if (cachedModel) {
//
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++; modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
@@ -81,7 +80,6 @@ async function loadInitialFloorItems(
// Check IndexedDB // Check IndexedDB
const indexedDBModel = await retrieveGLTF(item.modelfileID!); const indexedDBModel = await retrieveGLTF(item.modelfileID!);
if (indexedDBModel) { if (indexedDBModel) {
//
const blobUrl = URL.createObjectURL(indexedDBModel); const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(blobUrl, (gltf) => { loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
@@ -102,7 +100,6 @@ async function loadInitialFloorItems(
} }
// Fetch from Backend // Fetch from Backend
//
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
loader.load(modelUrl, async (gltf) => { loader.load(modelUrl, async (gltf) => {
const modelBlob = await fetch(modelUrl).then((res) => res.blob()); const modelBlob = await fetch(modelUrl).then((res) => res.blob());
@@ -338,7 +335,7 @@ function checkLoadingCompletion(
resolve: () => void resolve: () => void
) { ) {
if (modelsLoaded === modelsToLoad) { if (modelsLoaded === modelsToLoad) {
toast.success("Models Loaded!"); echo.success("Models Loaded!");
dracoLoader.dispose(); dracoLoader.dispose();
} }
resolve(); resolve();

View File

@@ -1,54 +1,102 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; 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 * as Types from "../../../types/world/worldTypes";
import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi';
import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
////////// Load the Wall Items's intially of there is any //////////
async function loadInitialWallItems( async function loadInitialWallItems(
setWallItems: Types.setWallItemSetState, setWallItems: Types.setWallItemSetState,
AssetConfigurations: Types.AssetConfigurations
): Promise<void> { ): Promise<void> {
try {
const email = localStorage.getItem('email');
if (!email) {
throw new Error('No email found in localStorage');
}
const email = localStorage.getItem('email') const organization = email.split("@")[1].split(".")[0];
const organization = (email!.split("@")[1]).split(".")[0]; const items = await getWallItems(organization);
const items = await getWallItems(organization); let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
localStorage.setItem("WallItems", JSON.stringify(items)); if (!items || items.length === 0) {
if (items.length > 0) { localStorage.removeItem("WallItems");
const storedWallItems: Types.wallItems = items; return;
}
const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { localStorage.setItem("WallItems", JSON.stringify(items));
const loader = new GLTFLoader();
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.modelName!);
if (cachedModel) {
return processModel(cachedModel, item);
}
// Check IndexedDB cache
const cachedModelBlob = await retrieveGLTF(item.modelfileID!);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
return new Promise<Types.WallItem>((resolve) => {
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.add(item.modelName!, gltf);
resolve(processModel(gltf, item));
});
});
}
// Load from original URL if not cached
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
return new Promise<Types.WallItem>((resolve) => { return new Promise<Types.WallItem>((resolve) => {
loader.load(AssetConfigurations[item.modelName!].modelUrl, (gltf) => { loader.load(modelUrl, async (gltf) => {
const model = gltf.scene; try {
model.uuid = item.modelUuid!; // Cache the model
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
model.children[0].children.forEach((child: any) => { await storeGLTF(item.modelName!, modelBlob);
if (child.name !== "CSG_REF") { THREE.Cache.add(item.modelName!, gltf);
child.castShadow = true; resolve(processModel(gltf, item));
child.receiveShadow = true; } catch (error) {
} console.error('Failed to cache model:', error);
resolve(processModel(gltf, item));
}
}); });
resolve({
type: item.type,
model: model,
modelName: item.modelName,
scale: item.scale,
csgscale: item.csgscale,
csgposition: item.csgposition,
position: item.position,
quaternion: item.quaternion,
});
});
}); });
})); }));
setWallItems(loadedWallItems); setWallItems(loadedWallItems);
} catch (error) {
console.error('Failed to load wall items:', error);
} }
} }
export default loadInitialWallItems; 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,
modelfileID: item.modelfileID,
scale: item.scale,
csgscale: item.csgscale,
csgposition: item.csgposition,
position: item.position,
quaternion: item.quaternion,
};
}
export default loadInitialWallItems;

View File

@@ -126,32 +126,6 @@ export default function Builder() {
// dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
// loader.setDRACOLoader(dracoLoader); // loader.setDRACOLoader(dracoLoader);
////////// Assest Configuration Values //////////
const AssetConfigurations: Types.AssetConfigurations = {
arch: {
modelUrl: arch,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
type: "Fixed-Move",
},
door: {
modelUrl: door,
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
type: "Fixed-Move",
},
window: {
modelUrl: Window,
scale: [0.75, 0.75, 0.75],
csgscale: [5, 3, 0.5],
csgposition: [0, 1.5, 0],
type: "Free-Move",
},
};
////////// All Toggle's ////////// ////////// All Toggle's //////////
useEffect(() => { useEffect(() => {
@@ -247,7 +221,6 @@ export default function Builder() {
floorGroupAisle={floorGroupAisle} floorGroupAisle={floorGroupAisle}
scene={scene} scene={scene}
onlyFloorlines={onlyFloorlines} onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
isTempLoader={isTempLoader} isTempLoader={isTempLoader}
tempLoader={tempLoader} tempLoader={tempLoader}
@@ -260,7 +233,6 @@ export default function Builder() {
<WallsAndWallItems <WallsAndWallItems
CSGGroup={CSGGroup} CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex} setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex} selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem} currentWallItem={currentWallItem}

View File

@@ -27,7 +27,16 @@ export const Csg: React.FC<CsgProps> = (props) => {
} }
}); });
} }
props.hoveredDeletableWallItem.current = hovered ? object : null; let currentObject = object;
while (currentObject) {
if (currentObject.name === "Scene") {
break;
}
currentObject = currentObject.parent as THREE.Mesh;
}
if (currentObject) {
props.hoveredDeletableWallItem.current = hovered ? currentObject : null;
}
}; };
return ( return (

View File

@@ -35,29 +35,26 @@ function handleMeshDown(
} }
if (event.intersections.length > 0) { if (event.intersections.length > 0) {
const clickedIndex = wallItems.findIndex((item) => item.model === event.intersections[0]?.object?.parent?.parent); if (event.object) {
if (clickedIndex !== -1) { const wallItemModel = event.object;
setSelectedItemsIndex(clickedIndex); let currentObject = wallItemModel as THREE.Object3D;
const wallItemModel = wallItems[clickedIndex]?.model; while (currentObject) {
if (wallItemModel && wallItemModel.parent && wallItemModel.parent.parent) { if (currentObject.name === "Scene") {
currentWallItem.current = (wallItemModel.parent.parent.children[0]?.children[1]?.children[0] as Types.Mesh) || null; break;
setSelectedWallItem(wallItemModel.parent); }
// currentWallItem.current?.children.forEach((child) => { currentObject = currentObject.parent as THREE.Object3D;
// if ((child as THREE.Mesh).isMesh && child.name !== "CSG_REF") { }
// const material = (child as THREE.Mesh).material; if (!currentObject) return;
// if (Array.isArray(material)) { const clickedIndex = wallItems.findIndex((item) => item.model?.uuid === currentObject.uuid);
// material.forEach(mat => { if (clickedIndex !== -1) {
// if (mat instanceof THREE.MeshStandardMaterial) { setSelectedItemsIndex(clickedIndex);
// mat.emissive = new THREE.Color("green"); const wallItemModel = wallItems[clickedIndex]?.model;
// } if (wallItemModel) {
// }); setSelectedWallItem(wallItemModel);
// } else if (material instanceof THREE.MeshStandardMaterial) { }
// material.emissive = new THREE.Color("green");
// }
// }
// });
} }
} }
} }
} }
} }

View File

@@ -397,7 +397,7 @@ async function handleModelLoad(
socket.emit("v2:model-asset:add", completeData); socket.emit("v2:model-asset:add", completeData);
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } });
} else { } else {
const data = { const data = {
@@ -421,7 +421,7 @@ async function handleModelLoad(
socket.emit("v2:model-asset:add", data); socket.emit("v2:model-asset:add", data);
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } });
} }
} }

View File

@@ -80,7 +80,7 @@ async function DeleteFloorItems(
} }
setFloorItems(updatedItems); setFloorItems(updatedItems);
toast.success("Model Removed!"); echo.success("Model Removed!");
} }
} }
} }

View File

@@ -83,7 +83,7 @@ async function DeleteLayer(
floorGroup.current?.remove(meshToRemove); floorGroup.current?.remove(meshToRemove);
} }
toast.success("Layer Removed!"); echo.success("Layer Removed!");
setRemovedLayer(null); setRemovedLayer(null);
} }
export default DeleteLayer; export default DeleteLayer;

View File

@@ -82,7 +82,7 @@ function deleteLine(
} }
}); });
toast.success("Line Removed!"); echo.success("Line Removed!");
} }
export default deleteLine; export default deleteLine;

View File

@@ -13,7 +13,7 @@ function DeletePillar(
(<any>hoveredDeletablePillar.current.material).dispose(); (<any>hoveredDeletablePillar.current.material).dispose();
(<any>hoveredDeletablePillar.current.geometry).dispose(); (<any>hoveredDeletablePillar.current.geometry).dispose();
floorGroup.current.remove(hoveredDeletablePillar.current); floorGroup.current.remove(hoveredDeletablePillar.current);
toast.success("Pillar Removed!"); echo.success("Pillar Removed!");
hoveredDeletablePillar.current = undefined; hoveredDeletablePillar.current = undefined;
} }
} }

View File

@@ -51,7 +51,7 @@ function deletePoint(
RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines); RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines);
toast.success("Point Removed!"); echo.success("Point Removed!");
} }
export default deletePoint; export default deletePoint;

View File

@@ -1,108 +1,138 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import * as THREE from 'three'; import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from '../../../../types/world/worldConstants'; import * as CONSTANTS from '../../../../types/world/worldConstants';
// import { setWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/setWallItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
async function AddWallItems( async function AddWallItems(
selected: Types.String, selected: any,
raycaster: THREE.Raycaster, raycaster: THREE.Raycaster,
CSGGroup: Types.RefMesh, CSGGroup: Types.RefMesh,
AssetConfigurations: Types.AssetConfigurations,
setWallItems: Types.setWallItemSetState, setWallItems: Types.setWallItemSetState,
socket: Socket<any> socket: Socket<any>
): Promise<void> { ): Promise<void> {
////////// Load Wall GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
let intersects = raycaster?.intersectObject(CSGGroup.current!, true); let intersects = raycaster?.intersectObject(CSGGroup.current!, true);
const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference")); const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference"));
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
if (wallRaycastIntersection) { if (!wallRaycastIntersection) return;
const intersectionPoint = wallRaycastIntersection;
const loader = new GLTFLoader(); const intersectionPoint = wallRaycastIntersection;
loader.load(AssetConfigurations[selected].modelUrl, async (gltf) => { const loader = new GLTFLoader();
const model = gltf.scene; const dracoLoader = new DRACOLoader();
model.userData = { wall: intersectionPoint.object.parent };
model.children[0].children.forEach((child) => { dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
if (child.name !== "CSG_REF") { loader.setDRACOLoader(dracoLoader);
child.castShadow = true;
child.receiveShadow = true; // Check THREE.js cache first
} const cachedModel = THREE.Cache.get(selected.id);
if (cachedModel) {
handleModelLoad(cachedModel);
return;
}
// Check IndexedDB cache
const cachedModelBlob = await retrieveGLTF(selected.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selected.id, gltf);
handleModelLoad(gltf);
});
return;
}
// Load from backend if not in any cache
loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`, async (gltf) => {
try {
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`).then((res) => res.blob());
await storeGLTF(selected.id, modelBlob);
THREE.Cache.add(selected.id, gltf);
await handleModelLoad(gltf);
} catch (error) {
console.error('Failed to cache model:', error);
handleModelLoad(gltf);
}
});
async function handleModelLoad(gltf: GLTF) {
const model = gltf.scene.clone();
model.userData = { wall: intersectionPoint.object.parent };
model.children[0].children.forEach((child) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
});
const boundingBox = new THREE.Box3().setFromObject(model);
const size = new THREE.Vector3();
boundingBox.getSize(size);
const csgscale = [size.x, size.y, size.z] as [number, number, number];
const center = new THREE.Vector3();
boundingBox.getCenter(center);
const csgposition = [center.x, center.y, center.z] as [number, number, number];
let positionY = selected.subCategory === 'fixed-move' ? 0 : intersectionPoint.point.y;
if (positionY === 0) {
positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height;
}
const newWallItem = {
type: selected.subCategory,
model: model,
modelName: selected.name,
modelfileID: selected.id,
scale: [1, 1, 1] as [number, number, number],
csgscale: csgscale,
csgposition: csgposition,
position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number],
quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType
};
const email = localStorage.getItem('email');
const organization = email ? (email.split("@")[1]).split(".")[0] : 'default';
const data = {
organization: organization,
modelUuid: model.uuid,
modelName: newWallItem.modelName,
modelfileID: selected.id,
type: selected.subCategory,
csgposition: newWallItem.csgposition,
csgscale: newWallItem.csgscale,
position: newWallItem.position,
quaternion: newWallItem.quaternion,
scale: newWallItem.scale,
socketId: socket.id
};
socket.emit('v1:wallItems:set', data);
setWallItems((prevItems) => {
const updatedItems = [...prevItems, newWallItem];
const WallItemsForStorage = updatedItems.map(item => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
}); });
const config = AssetConfigurations[selected]; localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y; echo.success("Model Added!");
if (positionY === 0) { return updatedItems;
positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height;
}
const newWallItem = {
type: config.type,
model: model,
modelName: selected,
scale: config.scale,
csgscale: config.csgscale,
csgposition: config.csgposition,
position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number],
quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType
};
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// await setWallItem(
// organization,
// model.uuid,
// newWallItem.modelName,
// newWallItem.type!,
// newWallItem.csgposition!,
// newWallItem.csgscale!,
// newWallItem.position,
// newWallItem.quaternion,
// newWallItem.scale!,
// )
//SOCKET
const data = {
organization: organization,
modelUuid: model.uuid,
modelName: newWallItem.modelName,
type: newWallItem.type!,
csgposition: newWallItem.csgposition!,
csgscale: newWallItem.csgscale!,
position: newWallItem.position,
quaternion: newWallItem.quaternion,
scale: newWallItem.scale!,
socketId: socket.id
}
socket.emit('v1:wallItems:set', data);
setWallItems((prevItems) => {
const updatedItems = [...prevItems, newWallItem];
const WallItemsForStorage = updatedItems.map(item => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
toast.success("Model Added!");
return updatedItems;
});
}); });
} }
} }
export default AddWallItems; export default AddWallItems;

View File

@@ -1,5 +1,3 @@
import { toast } from 'react-toastify';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
// import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi'; // import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
@@ -13,11 +11,11 @@ function DeleteWallItems(
////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage ////////// ////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage //////////
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current.parent) { if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) {
setWallItems([]); setWallItems([]);
let WallItemsRef = wallItems; let WallItemsRef = wallItems;
const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.parent?.uuid); const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid);
const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.parent?.uuid); const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid);
setTimeout(async () => { setTimeout(async () => {
WallItemsRef = Items; WallItemsRef = Items;
@@ -50,7 +48,6 @@ function DeleteWallItems(
}); });
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
toast.success("Model Removed!");
hoveredDeletableWallItem.current = null; hoveredDeletableWallItem.current = null;
}, 50); }, 50);
} }

View File

@@ -362,7 +362,7 @@ const FloorItemsGroup = ({
const onDrop = (event: any) => { const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return; if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
addAssetModel( addAssetModel(
raycaster, raycaster,
state.camera, state.camera,

View File

@@ -7,6 +7,7 @@ import {
useSelectedWallItem, useSelectedWallItem,
useSocketStore, useSocketStore,
useWallItems, useWallItems,
useSelectedItem,
} from "../../../store/store"; } from "../../../store/store";
import { Csg } from "../csg/csg"; import { Csg } from "../csg/csg";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
@@ -21,7 +22,6 @@ import useModuleStore from "../../../store/useModuleStore";
const WallItemsGroup = ({ const WallItemsGroup = ({
currentWallItem, currentWallItem,
AssetConfigurations,
hoveredDeletableWallItem, hoveredDeletableWallItem,
selectedItemsIndex, selectedItemsIndex,
setSelectedItemsIndex, setSelectedItemsIndex,
@@ -37,34 +37,22 @@ const WallItemsGroup = ({
const { deletePointOrLine } = useDeletePointOrLine(); const { deletePointOrLine } = useDeletePointOrLine();
const { setSelectedWallItem } = useSelectedWallItem(); const { setSelectedWallItem } = useSelectedWallItem();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { selectedItem, setSelectedItem } = useSelectedItem();
useEffect(() => { useEffect(() => {
// Load Wall Items from the backend // Load Wall Items from the backend
loadInitialWallItems(setWallItems, AssetConfigurations); loadInitialWallItems(setWallItems);
}, []); }, []);
////////// Update the Scale value changes in thewallItems State //////////
////////// Update the Position value changes in the selected item ////////// ////////// Update the Position value changes in the selected item //////////
////////// Update the Rotation value changes in the selected item //////////
useEffect(() => { useEffect(() => {
const canvasElement = state.gl.domElement; const canvasElement = state.gl.domElement;
function handlePointerMove(e: any) { function handlePointerMove(e: any) {
if ( if (selectedItemsIndex !== null && !deletePointOrLine && e.buttons === 1) {
selectedItemsIndex !== null &&
!deletePointOrLine &&
e.buttons === 1
) {
const Raycaster = state.raycaster; const Raycaster = state.raycaster;
const intersects = Raycaster.intersectObjects( const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true);
CSGGroup.current?.children[0].children!, const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference"));
true
);
const Object = intersects.find((child) =>
child.object.name.includes("WallRaycastReference")
);
if (Object) { if (Object) {
(state.controls as any)!.enabled = false; (state.controls as any)!.enabled = false;
@@ -72,14 +60,14 @@ const WallItemsGroup = ({
const updatedItems = [...prevItems]; const updatedItems = [...prevItems];
let position: [number, number, number] = [0, 0, 0]; let position: [number, number, number] = [0, 0, 0];
if (updatedItems[selectedItemsIndex].type === "Fixed-Move") { if (updatedItems[selectedItemsIndex].type === "fixed-move") {
position = [ position = [
Object!.point.x, Object!.point.x,
Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) * Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) *
CONSTANTS.wallConfig.height, CONSTANTS.wallConfig.height,
Object!.point.z, Object!.point.z,
]; ];
} else if (updatedItems[selectedItemsIndex].type === "Free-Move") { } else if (updatedItems[selectedItemsIndex].type === "free-move") {
position = [Object!.point.x, Object!.point.y, Object!.point.z]; position = [Object!.point.x, Object!.point.y, Object!.point.z];
} }
@@ -95,8 +83,7 @@ const WallItemsGroup = ({
updatedItems[selectedItemsIndex] = { updatedItems[selectedItemsIndex] = {
...updatedItems[selectedItemsIndex], ...updatedItems[selectedItemsIndex],
position: position, position: position,
quaternion: quaternion: Object!.object.quaternion.clone() as Types.QuaternionType,
Object!.object.quaternion.clone() as Types.QuaternionType,
}; };
return updatedItems; return updatedItems;
@@ -128,10 +115,7 @@ const WallItemsGroup = ({
}); });
currentItem = updatedItems[selectedItemsIndex]; currentItem = updatedItems[selectedItemsIndex];
localStorage.setItem( localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
"WallItems",
JSON.stringify(WallItemsForStorage)
);
return updatedItems; return updatedItems;
}); });
@@ -145,6 +129,7 @@ const WallItemsGroup = ({
// organization, // organization,
// currentItem?.model?.uuid, // currentItem?.model?.uuid,
// currentItem.modelName, // currentItem.modelName,
// currentItem.modelfileID,
// currentItem.type!, // currentItem.type!,
// currentItem.csgposition!, // currentItem.csgposition!,
// currentItem.csgscale!, // currentItem.csgscale!,
@@ -158,6 +143,7 @@ const WallItemsGroup = ({
const data = { const data = {
organization: organization, organization: organization,
modelUuid: currentItem.model?.uuid!, modelUuid: currentItem.model?.uuid!,
modelfileID: currentItem.modelfileID,
modelName: currentItem.modelName!, modelName: currentItem.modelName!,
type: currentItem.type!, type: currentItem.type!,
csgposition: currentItem.csgposition!, csgposition: currentItem.csgposition!,
@@ -217,21 +203,19 @@ const WallItemsGroup = ({
}; };
const onDrop = (event: any) => { const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return; if (selectedItem.category !== 'Fenestration') return;
pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
if (AssetConfigurations[event.dataTransfer.files[0].name.split(".")[0]]) { if (selectedItem.id) {
const selected = event.dataTransfer.files[0].name.split(".")[0]; if (selectedItem.subCategory) {
if (AssetConfigurations[selected]?.type) {
AddWallItems( AddWallItems(
selected, selectedItem,
raycaster, raycaster,
CSGGroup, CSGGroup,
AssetConfigurations,
setWallItems, setWallItems,
socket socket
); );
@@ -257,7 +241,7 @@ const WallItemsGroup = ({
canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver); canvasElement.removeEventListener("dragover", onDragOver);
}; };
}, [deleteTool, wallItems]); }, [deleteTool, wallItems, selectedItem, camera]);
useEffect(() => { useEffect(() => {
if (deleteTool && activeModule === "builder") { if (deleteTool && activeModule === "builder") {

View File

@@ -10,11 +10,9 @@ import handleMeshDown from "../eventFunctions/handleMeshDown";
import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import WallsMesh from "./wallsMesh"; import WallsMesh from "./wallsMesh";
import WallItemsGroup from "./wallItemsGroup"; import WallItemsGroup from "./wallItemsGroup";
import { useEffect } from "react";
const WallsAndWallItems = ({ const WallsAndWallItems = ({
CSGGroup, CSGGroup,
AssetConfigurations,
setSelectedItemsIndex, setSelectedItemsIndex,
selectedItemsIndex, selectedItemsIndex,
currentWallItem, currentWallItem,
@@ -63,7 +61,6 @@ const WallsAndWallItems = ({
<WallsMesh lines={lines} /> <WallsMesh lines={lines} />
<WallItemsGroup <WallItemsGroup
currentWallItem={currentWallItem} currentWallItem={currentWallItem}
AssetConfigurations={AssetConfigurations}
hoveredDeletableWallItem={hoveredDeletableWallItem} hoveredDeletableWallItem={hoveredDeletableWallItem}
selectedItemsIndex={selectedItemsIndex} selectedItemsIndex={selectedItemsIndex}
setSelectedItemsIndex={setSelectedItemsIndex} setSelectedItemsIndex={setSelectedItemsIndex}

View File

@@ -4,7 +4,7 @@ import * as CONSTANTS from "../../../types/world/worldConstants";
import { Base } from "@react-three/csg"; import { Base } from "@react-three/csg";
import { MeshDiscardMaterial } from "@react-three/drei"; import { MeshDiscardMaterial } from "@react-three/drei";
import { useUpdateScene, useWalls } from "../../../store/store"; import { useUpdateScene, useWalls } from "../../../store/store";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
import loadWalls from "../geomentries/walls/loadWalls"; import loadWalls from "../geomentries/walls/loadWalls";

File diff suppressed because it is too large Load Diff

View File

@@ -415,7 +415,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
} }
}); });
toast.success("Object added!"); echo.success("Object added!");
clearSelection(); clearSelection();
}; };

View File

@@ -392,7 +392,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
} }
}); });
toast.success("Object duplicated!"); echo.success("Object duplicated!");
clearSelection(); clearSelection();
} }

View File

@@ -326,7 +326,7 @@ function MoveControls({
itemsGroupRef.current.add(obj); itemsGroupRef.current.add(obj);
} }
}); });
toast.success("Object moved!"); echo.success("Object moved!");
itemsData.current = []; itemsData.current = [];
clearSelection(); clearSelection();

View File

@@ -265,7 +265,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
itemsGroupRef.current.add(obj); itemsGroupRef.current.add(obj);
} }
}); });
toast.success("Object rotated!"); echo.success("Object rotated!");
itemsData.current = []; itemsData.current = [];
clearSelection(); clearSelection();

View File

@@ -251,7 +251,7 @@ const SelectionControls: React.FC = () => {
const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid));
setFloorItems(updatedItems); setFloorItems(updatedItems);
} }
toast.success("Selected models removed!"); echo.success("Selected models removed!");
clearSelection(); clearSelection();
}; };

View File

@@ -57,9 +57,7 @@ export default function PostProcessing() {
)} )}
{selectedWallItem && ( {selectedWallItem && (
<Outline <Outline
selection={selectedWallItem.children[1].children[0].children.filter( selection={flattenChildren(selectedWallItem.children)}
(child: Types.Mesh) => child.name !== "CSG_REF"
)}
selectionLayer={10} selectionLayer={10}
width={3000} width={3000}
blendFunction={BlendFunction.ALPHA} blendFunction={BlendFunction.ALPHA}

View File

@@ -3,22 +3,27 @@ import { useProductStore } from '../../../store/simulation/useProductStore';
import { useActionHandler } from '../actions/useActionHandler'; import { useActionHandler } from '../actions/useActionHandler';
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
import { determineExecutionOrder } from './functions/determineExecutionOrder'; import { determineExecutionOrder } from './functions/determineExecutionOrder';
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
function Simulator() { function Simulator() {
const { products } = useProductStore(); const { products, getProductById } = useProductStore();
const { handleAction } = useActionHandler(); const { handleAction } = useActionHandler();
const { selectedProduct } = useSelectedProduct();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
useEffect(() => { useEffect(() => {
if (!isPlaying || isReset) return; if (!isPlaying || isReset || !selectedProduct.productId) return;
const executionOrder = determineExecutionOrder(products); const product = getProductById(selectedProduct.productId);
if (!product) return;
const executionOrder = determineExecutionOrder([product]);
executionOrder.forEach(point => { executionOrder.forEach(point => {
const action = 'actions' in point ? point.actions[0] : point.action; const action = 'actions' in point ? point.actions[0] : point.action;
handleAction(action); handleAction(action);
}); });
}, [products, isPlaying, isReset]); }, [products, isPlaying, isReset, selectedProduct]);
return ( return (

View File

@@ -90,7 +90,7 @@ export const useZonePoints = create<ZonePointsState>((set) => ({
})); }));
export const useSelectedItem = create<any>((set: any) => ({ export const useSelectedItem = create<any>((set: any) => ({
selectedItem: { name: "", id: "", type: undefined }, selectedItem: { name: "", id: "", type: undefined, category: '', subCatergory: '' },
setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), setSelectedItem: (x: any) => set(() => ({ selectedItem: x })),
})); }));

View File

@@ -217,27 +217,14 @@ export type FloorItems = Array<FloorItemType>;
// Dispatch type for setting floor item state in React // Dispatch type for setting floor item state in React
export type setFloorItemSetState = React.Dispatch<React.SetStateAction<FloorItems | null | undefined>>; export type setFloorItemSetState = React.Dispatch<React.SetStateAction<FloorItems | null | undefined>>;
/** Asset Configuration for Loading and Positioning **/
// Configuration for assets, allowing model URLs, scaling, positioning, and types
interface AssetConfiguration {
modelUrl: string;
scale?: [number, number, number];
csgscale?: [number, number, number];
csgposition?: [number, number, number];
type?: "Fixed-Move" | "Free-Move";
}
// Collection of asset configurations, keyed by unique identifiers
export type AssetConfigurations = { [key: string]: AssetConfiguration; };
/** Wall Item Configuration **/ /** Wall Item Configuration **/
// Configuration for wall items, including model, scale, position, and rotation // Configuration for wall items, including model, scale, position, and rotation
interface WallItem { interface WallItem {
type: "Fixed-Move" | "Free-Move" | undefined; type: "fixed-move" | "free-move" | undefined;
model?: THREE.Group; model?: THREE.Group;
modelUuid?: string; modelUuid?: string;
modelfileID: string;
modelName?: string; modelName?: string;
scale?: [number, number, number]; scale?: [number, number, number];
csgscale?: [number, number, number]; csgscale?: [number, number, number];