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

View File

@@ -71,7 +71,6 @@ async function loadInitialFloorItems(
// Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!);
if (cachedModel) {
//
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
@@ -81,7 +80,6 @@ async function loadInitialFloorItems(
// Check IndexedDB
const indexedDBModel = await retrieveGLTF(item.modelfileID!);
if (indexedDBModel) {
//
const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
@@ -102,7 +100,6 @@ async function loadInitialFloorItems(
}
// Fetch from Backend
//
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
loader.load(modelUrl, async (gltf) => {
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
@@ -338,7 +335,7 @@ function checkLoadingCompletion(
resolve: () => void
) {
if (modelsLoaded === modelsToLoad) {
toast.success("Models Loaded!");
echo.success("Models Loaded!");
dracoLoader.dispose();
}
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 { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi';
////////// Load the Wall Items's intially of there is any //////////
import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
async function loadInitialWallItems(
setWallItems: Types.setWallItemSetState,
AssetConfigurations: Types.AssetConfigurations
): 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.length > 0) {
const storedWallItems: Types.wallItems = items;
if (!items || items.length === 0) {
localStorage.removeItem("WallItems");
return;
}
const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => {
const loader = new GLTFLoader();
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.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) => {
loader.load(AssetConfigurations[item.modelName!].modelUrl, (gltf) => {
const model = gltf.scene;
model.uuid = item.modelUuid!;
model.children[0].children.forEach((child: any) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
loader.load(modelUrl, async (gltf) => {
try {
// Cache the model
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
await storeGLTF(item.modelName!, modelBlob);
THREE.Cache.add(item.modelName!, gltf);
resolve(processModel(gltf, item));
} 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);
} 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/');
// 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 //////////
useEffect(() => {
@@ -247,7 +221,6 @@ export default function Builder() {
floorGroupAisle={floorGroupAisle}
scene={scene}
onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup}
isTempLoader={isTempLoader}
tempLoader={tempLoader}
@@ -260,7 +233,6 @@ export default function Builder() {
<WallsAndWallItems
CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex}
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 (

View File

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

View File

@@ -397,7 +397,7 @@ async function handleModelLoad(
socket.emit("v2:model-asset:add", completeData);
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 {
const data = {
@@ -421,7 +421,7 @@ async function handleModelLoad(
socket.emit("v2:model-asset:add", data);
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);
toast.success("Model Removed!");
echo.success("Model Removed!");
}
}
}

View File

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

View File

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

View File

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

View File

@@ -51,7 +51,7 @@ function deletePoint(
RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines);
toast.success("Point Removed!");
echo.success("Point Removed!");
}
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 * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from '../../../../types/world/worldConstants';
// import { setWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/setWallItemApi';
import { Socket } from 'socket.io-client';
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
async function AddWallItems(
selected: Types.String,
selected: any,
raycaster: THREE.Raycaster,
CSGGroup: Types.RefMesh,
AssetConfigurations: Types.AssetConfigurations,
setWallItems: Types.setWallItemSetState,
socket: Socket<any>
): 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);
const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference"));
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
if (wallRaycastIntersection) {
const intersectionPoint = wallRaycastIntersection;
const loader = new GLTFLoader();
loader.load(AssetConfigurations[selected].modelUrl, async (gltf) => {
const model = gltf.scene;
model.userData = { wall: intersectionPoint.object.parent };
model.children[0].children.forEach((child) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
if (!wallRaycastIntersection) return;
const intersectionPoint = wallRaycastIntersection;
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);
// 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];
let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y;
if (positionY === 0) {
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;
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
echo.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 { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi';
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 //////////
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current.parent) {
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) {
setWallItems([]);
let WallItemsRef = wallItems;
const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.parent?.uuid);
const Items = WallItemsRef.filter((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?.uuid);
setTimeout(async () => {
WallItemsRef = Items;
@@ -50,7 +48,6 @@ function DeleteWallItems(
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
toast.success("Model Removed!");
hoveredDeletableWallItem.current = null;
}, 50);
}

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import * as CONSTANTS from "../../../types/world/worldConstants";
import { Base } from "@react-three/csg";
import { MeshDiscardMaterial } from "@react-three/drei";
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 objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
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();
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -90,7 +90,7 @@ export const useZonePoints = create<ZonePointsState>((set) => ({
}));
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 })),
}));

View File

@@ -217,27 +217,14 @@ export type FloorItems = Array<FloorItemType>;
// Dispatch type for setting floor item state in React
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 **/
// Configuration for wall items, including model, scale, position, and rotation
interface WallItem {
type: "Fixed-Move" | "Free-Move" | undefined;
type: "fixed-move" | "free-move" | undefined;
model?: THREE.Group;
modelUuid?: string;
modelfileID: string;
modelName?: string;
scale?: [number, number, number];
csgscale?: [number, number, number];