This commit is contained in:
2025-06-23 09:37:53 +05:30
parent 2fbdf8ab61
commit 54b02541c1
278 changed files with 10134 additions and 7904 deletions

View File

@@ -1,103 +1,110 @@
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as THREE from 'three';
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';
import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getWallItems } from "../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi";
import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils";
import { getUserData } from "../../../functions/getUserData";
async function loadInitialWallItems(
setWallItems: Types.setWallItemSetState,
projectId?: string
setWallItems: Types.setWallItemSetState,
projectId?: string,
versionId?: string
): Promise<void> {
try {
const email = localStorage.getItem('email');
if (!email) {
throw new Error('No email found in localStorage');
}
if (!projectId || !versionId) return;
try {
const { organization, email } = getUserData();
const organization = email.split("@")[1].split(".")[0];
const items = await getWallItems(organization, projectId);
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);
if (!email) {
throw new 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!;
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;
}
});
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,
};
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;
export default loadInitialWallItems;

View File

@@ -1,13 +1,14 @@
import React, { useEffect, useMemo } from 'react';
import { useAisleStore } from '../../../../store/builder/useAisleStore';
import { useToggleView } from '../../../../store/builder/store';
import AisleInstance from './instance/aisleInstance';
import Point from '../../point/point';
import { Html } from '@react-three/drei';
import { Vector3 } from 'three';
import { useSceneContext } from '../../../scene/sceneContext';
function AisleInstances() {
const { aisles } = useAisleStore();
const { aisleStore } = useSceneContext();
const { aisles } = aisleStore();
const { toggleView } = useToggleView();
const allPoints = useMemo(() => {
@@ -26,8 +27,6 @@ function AisleInstances() {
return points;
}, [aisles]);
return (
<>
{toggleView &&

View File

@@ -2,12 +2,13 @@ import * as THREE from 'three'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useAisleStore } from '../../../../store/builder/useAisleStore';
import ReferenceAisle from './referenceAisle';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import ReferencePoint from '../../point/reference/referencePoint';
import { createAisleApi } from '../../../../services/factoryBuilder/aisle/createAisleApi';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext';
import { useSceneContext } from '../../../scene/sceneContext';
function AisleCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree();
@@ -16,14 +17,17 @@ function AisleCreator() {
const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { addAisle, getAislePointById } = useAisleStore();
const { aisleStore } = useSceneContext();
const { addAisle, getAislePointById } = aisleStore();
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const [tempPoints, setTempPoints] = useState<Point[]>([]);
const [isCreating, setIsCreating] = useState(false);
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, snappedPosition, snappedPoint } = useBuilderStore();
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, snappedPosition, snappedPoint, setSnappedPosition, setSnappedPoint } = useBuilderStore();
useEffect(() => {
const canvasElement = gl.domElement;
@@ -100,7 +104,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -123,7 +127,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -145,7 +149,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -166,7 +170,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -189,7 +193,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -211,7 +215,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -232,7 +236,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -254,7 +258,7 @@ function AisleCreator() {
};
addAisle(aisle);
if (projectId) {
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId)
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
}
setTempPoints([newPoint]);
}
@@ -270,6 +274,10 @@ function AisleCreator() {
};
if (toolMode === "Aisle" && toggleView) {
if (tempPoints.length === 0) {
setSnappedPosition(null);
setSnappedPoint(null);
}
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
@@ -294,7 +302,7 @@ function AisleCreator() {
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext);
};
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint, selectedVersion?.versionId]);
return (
<>

View File

@@ -25,7 +25,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
const { checkSnapForAisle } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] });
const { snapAislePoint } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] });
useFrame(() => {
if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) {
@@ -36,7 +36,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (intersectionPoint) {
const snapped = checkSnapForAisle([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
const snapped = snapAislePoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (snapped.isSnapped && snapped.snappedPoint) {
finalPosition.current = snapped.position;

View File

@@ -3,20 +3,29 @@ import AisleCreator from './aisleCreator/aisleCreator'
import AisleInstances from './Instances/aisleInstances'
import { useParams } from 'react-router-dom';
import { getAisleApi } from '../../../services/factoryBuilder/aisle/getAisleApi';
import { useAisleStore } from '../../../store/builder/useAisleStore';
import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext';
function AislesGroup() {
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { aisleStore } = useSceneContext();
const { setAisles } = aisleStore();
const { projectId } = useParams();
const { setAisles } = useAisleStore();
useEffect(() => {
const fetchAisle = async () => {
if (projectId) {
const aisles = await getAisleApi(projectId);
setAisles(aisles);
}
if (projectId) {
getAisleApi(projectId, selectedVersion?.versionId || '').then((aisles) => {
if (aisles && aisles.length > 0) {
setAisles(aisles);
} else {
setAisles([]);
}
}).catch((err) => {
console.log(err);
})
}
fetchAisle()
}, [])
}, [projectId, selectedVersion?.versionId])
return (

View File

@@ -5,8 +5,6 @@ import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelect
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes";
import { useAssetsStore } from "../../../store/builder/useAssetStore";
import { useEventsStore } from "../../../store/simulation/useEventsStore";
import Models from "./models/models";
import useModuleStore from "../../../store/useModuleStore";
import { useThree } from "@react-three/fiber";
@@ -14,6 +12,9 @@ import { CameraControls } from "@react-three/drei";
import addAssetModel from "./functions/addAssetModel";
import { useParams } from "react-router-dom";
import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore";
import { getUserData } from "../../../functions/getUserData";
import { useSceneContext } from "../../scene/sceneContext";
import { useVersionContext } from "../version/versionContext";
const gltfLoaderWorker = new Worker(
new URL(
@@ -27,15 +28,16 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
const { socket } = useSocketStore();
const { controls, gl, pointer, camera, raycaster } = useThree();
const { setLoadingProgress } = useLoadingProgress();
const { setAssets, addAsset } = useAssetsStore();
const { addEvent } = useEventsStore();
const { assetStore, eventStore } = useSceneContext();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { setAssets, addAsset, clearAssets } = assetStore();
const { addEvent, clearEvents } = eventStore();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { projectId } = useParams();
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const userId = localStorage.getItem("userId")!;
const { userId, organization } = getUserData();
const { setTop } = useTopData();
const { setLeft } = useLeftData();
@@ -48,6 +50,8 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
if (!projectId || !selectedVersion) return;
clearEvents();
let totalAssets = 0;
let loadedAssets = 0;
@@ -65,8 +69,8 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
}
};
getFloorAssets(organization, projectId).then((data) => {
if (data.length > 0) {
getFloorAssets(organization, projectId, selectedVersion?.versionId || '').then((data) => {
if (data && data.length > 0) {
const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.assetId === item.assetId));
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
@@ -77,8 +81,11 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
} else {
gltfLoaderWorker.postMessage({ floorItems: [] });
updateLoadingProgress(100);
clearAssets();
}
});
}).catch((err) => {
console.error(err);
})
gltfLoaderWorker.onmessage = async (event) => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
@@ -94,7 +101,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
if (loadedAssets === totalAssets) {
const assets: Asset[] = [];
getFloorAssets(organization, projectId).then((data: FloorItems) => {
getFloorAssets(organization, projectId, selectedVersion.versionId || '').then((data: FloorItems) => {
data.forEach((item) => {
if (item.eventData) {
assets.push({
@@ -257,7 +264,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
});
}
};
}, []);
}, [selectedVersion?.versionId]);
useEffect(() => {
const canvasElement = gl.domElement;
@@ -270,13 +277,14 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, projectId, userId);
addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, selectedVersion, projectId, userId);
}
};
const onDragOver = (event: any) => {
event.preventDefault();
};
const onMouseMove = (evt: any) => {
if (!canvasElement) return;
const canvasRect = canvasElement.getBoundingClientRect();
@@ -286,14 +294,11 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea
setTop(relativeY);
setLeft(relativeX);
}
};
const onMouseUp = (evt: any) => {
setIsRenameMode(false);
}
if (activeModule === "builder") {
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);

View File

@@ -3,425 +3,496 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import * as Types from "../../../../types/world/worldTypes";
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
// import { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi';
import { Socket } from "socket.io-client";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator";
import { getUserData } from "../../../../functions/getUserData";
async function addAssetModel(
raycaster: THREE.Raycaster,
camera: THREE.Camera,
pointer: THREE.Vector2,
floorGroup: Types.RefGroup,
socket: Socket<any>,
selectedItem: any,
setSelectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
plane: Types.RefMesh,
projectId?: string,
userId?: string
raycaster: THREE.Raycaster,
camera: THREE.Camera,
pointer: THREE.Vector2,
floorGroup: Types.RefGroup,
socket: Socket<any>,
selectedItem: any,
setSelectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
plane: Types.RefMesh,
selectedVersion?: Version | null,
projectId?: string,
userId?: string
): Promise<void> {
////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage //////////
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
try {
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
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);
dracoLoader.setDecoderPath(
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader);
raycaster.setFromCamera(pointer, camera);
const floorIntersections = raycaster.intersectObjects(floorGroup.current.children, true);
const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor"));
raycaster.setFromCamera(pointer, camera);
const floorIntersections = raycaster.intersectObjects(
floorGroup.current.children,
true
);
const intersectedFloor = floorIntersections.find((intersect) =>
intersect.object.name.includes("Floor")
);
const planeIntersections = raycaster.intersectObject(plane.current!, true);
const intersectedPlane = planeIntersections[0];
const planeIntersections = raycaster.intersectObject(plane.current!, true);
const intersectedPlane = planeIntersections[0];
let intersectPoint: THREE.Vector3 | null = null;
let intersectPoint: THREE.Vector3 | null = null;
if (intersectedFloor && intersectedPlane) {
intersectPoint =
intersectedFloor.distance < intersectedPlane.distance
? new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)
: new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
} else if (intersectedFloor) {
intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z);
} else if (intersectedPlane) {
intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z);
}
if (intersectPoint) {
if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z);
}
const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) {
handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket, projectId, userId);
return;
} else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, projectId, userId);
});
} else {
loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`, async (gltf) => {
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket, projectId, userId);
}
);
}
}
}
} catch (error) {
echo.error("Failed to add asset");
} finally {
setSelectedItem({});
if (intersectedFloor && intersectedPlane) {
intersectPoint =
intersectedFloor.distance < intersectedPlane.distance
? new THREE.Vector3(
intersectedFloor.point.x,
Math.round(intersectedFloor.point.y),
intersectedFloor.point.z
)
: new THREE.Vector3(
intersectedPlane.point.x,
0,
intersectedPlane.point.z
);
} else if (intersectedFloor) {
intersectPoint = new THREE.Vector3(
intersectedFloor.point.x,
Math.round(intersectedFloor.point.y),
intersectedFloor.point.z
);
} else if (intersectedPlane) {
intersectPoint = new THREE.Vector3(
intersectedPlane.point.x,
0,
intersectedPlane.point.z
);
}
if (intersectPoint) {
if (intersectPoint.y < 0) {
intersectPoint = new THREE.Vector3(
intersectPoint.x,
0,
intersectPoint.z
);
}
const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) {
handleModelLoad(
cachedModel,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
return;
} else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id);
if (cachedModelBlob) {
const blobUrl = URL.createObjectURL(cachedModelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad(
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
});
} else {
loader.load(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`,
async (gltf) => {
const modelBlob = await fetch(
`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`
).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad(
gltf,
intersectPoint!,
selectedItem,
addEvent,
addAsset,
socket,
selectedVersion?.versionId || '',
projectId,
userId
);
}
);
}
}
}
} catch (error) {
echo.error("Failed to add asset");
} finally {
setSelectedItem({});
}
}
async function handleModelLoad(
gltf: any,
intersectPoint: THREE.Vector3,
selectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
socket: Socket<any>,
projectId?: string,
userId?: string
gltf: any,
intersectPoint: THREE.Vector3,
selectedItem: any,
addEvent: (event: EventsSchema) => void,
addAsset: (asset: Asset) => void,
socket: Socket<any>,
versionId: string,
projectId?: string,
userId?: string
) {
const model = gltf.scene.clone();
model.userData = {
name: selectedItem.name,
modelId: selectedItem.id,
modelUuid: model.uuid,
};
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
const { organization } = getUserData();
const model = gltf.scene.clone();
model.userData = {
name: selectedItem.name,
modelId: selectedItem.id,
modelUuid: model.uuid,
};
model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z);
model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap);
model.traverse((child: any) => {
if (child) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const newFloorItem: Asset = {
modelUuid: model.uuid,
modelName: selectedItem.name,
assetId: selectedItem.id,
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
rotation: [0, 0, 0],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 1,
};
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modelUuid,
// newFloorItem.modelName,
// newFloorItem.assetId,
// newFloorItem.position,
// { x: 0, y: 0, z: 0 },
// false,
// true,
// );
// SOCKET
if (selectedItem.type) {
const data = PointsCalculator(
selectedItem.type,
gltf.scene.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const eventData: any = {
type: selectedItem.type,
};
if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = [];
if (data.points && index < data.points.length - 1) {
triggers.push({
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `Trigger 1`,
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: newFloorItem.modelName,
modelUuid: newFloorItem.modelUuid,
},
triggeredPoint: {
pointName: `Point`,
pointUuid: "",
},
triggeredAction: {
actionName: `Action 1`,
actionUuid: "",
},
},
});
}
return {
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: "default",
material: "Default Material",
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: triggers,
},
};
}),
};
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
const currentPoint = ConveyorEvent.points[i];
const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid;
}
}
addEvent(ConveyorEvent);
eventData.points = ConveyorEvent.points.map((point) => ({
uuid: point.uuid,
position: point.position,
rotation: point.rotation,
}));
} else if (selectedItem.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 1,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: [],
},
},
};
addEvent(vehicleEvent);
eventData.point = {
uuid: vehicleEvent.point.uuid,
position: vehicleEvent.point.position,
rotation: vehicleEvent.point.rotation,
};
} else if (selectedItem.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: null,
endPoint: null,
},
triggers: [],
},
],
},
};
addEvent(roboticArmEvent);
eventData.point = {
uuid: roboticArmEvent.point.uuid,
position: roboticArmEvent.point.position,
rotation: roboticArmEvent.point.rotation,
};
} else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "machine",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "Default Material",
triggers: [],
},
},
};
addEvent(machineEvent);
eventData.point = {
uuid: machineEvent.point.uuid,
position: machineEvent.point.position,
rotation: machineEvent.point.rotation,
};
} else if (selectedItem.type === "Storage") {
const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "storageUnit",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "store",
storageCapacity: 10,
triggers: [],
},
},
};
addEvent(storageEvent);
eventData.point = {
uuid: storageEvent.point.uuid,
position: storageEvent.point.position,
rotation: storageEvent.point.rotation,
};
}
const completeData = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
eventData: eventData,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", completeData);
const asset: Asset = {
modelUuid: completeData.modelUuid,
modelName: completeData.modelName,
assetId: completeData.assetId,
position: completeData.position,
rotation: [
completeData.rotation.x,
completeData.rotation.y,
completeData.rotation.z,
] as [number, number, number],
isLocked: completeData.isLocked,
isCollidable: false,
isVisible: completeData.isVisible,
opacity: 1,
eventData: completeData.eventData,
};
addAsset(asset);
} else {
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", data);
const asset = {
modelUuid: data.modelUuid,
modelName: data.modelName,
assetId: data.assetId,
position: data.position,
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [number, number, number],
isLocked: data.isLocked,
isCollidable: false,
isVisible: data.isVisible,
opacity: 1,
};
addAsset(asset);
model.traverse((child: any) => {
if (child) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const newFloorItem: Asset = {
modelUuid: model.uuid,
modelName: selectedItem.name,
assetId: selectedItem.id,
position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z],
rotation: [0, 0, 0],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 1,
};
// API
// await setAssetsApi(
// organization,
// newFloorItem.modelUuid,
// newFloorItem.modelName,
// newFloorItem.assetId,
// newFloorItem.position,
// { x: 0, y: 0, z: 0 },
// false,
// true,
// );
// SOCKET
if (selectedItem.type) {
const data = PointsCalculator(
selectedItem.type,
gltf.scene.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const eventData: any = {
type: selectedItem.type,
};
if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = [];
if (data.points && index < data.points.length - 1) {
triggers.push({
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `Trigger 1`,
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: newFloorItem.modelName,
modelUuid: newFloorItem.modelUuid,
},
triggeredPoint: {
pointName: `Point`,
pointUuid: "",
},
triggeredAction: {
actionName: `Action 1`,
actionUuid: "",
},
},
});
}
return {
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: "default",
material: "Default Material",
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: triggers,
},
};
}),
};
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
const currentPoint = ConveyorEvent.points[i];
const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid =
nextPoint.uuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid =
nextPoint.action.actionUuid;
}
}
addEvent(ConveyorEvent);
eventData.points = ConveyorEvent.points.map((point) => ({
uuid: point.uuid,
position: point.position,
rotation: point.rotation,
}));
} else if (selectedItem.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 1,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: [],
},
},
};
addEvent(vehicleEvent);
eventData.point = {
uuid: vehicleEvent.point.uuid,
position: vehicleEvent.point.position,
rotation: vehicleEvent.point.rotation,
};
} else if (selectedItem.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: null,
endPoint: null,
},
triggers: [],
},
],
},
};
addEvent(roboticArmEvent);
eventData.point = {
uuid: roboticArmEvent.point.uuid,
position: roboticArmEvent.point.position,
rotation: roboticArmEvent.point.rotation,
};
} else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "machine",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "Default Material",
triggers: [],
},
},
};
addEvent(machineEvent);
eventData.point = {
uuid: machineEvent.point.uuid,
position: machineEvent.point.position,
rotation: machineEvent.point.rotation,
};
} else if (selectedItem.type === "Storage") {
const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "storageUnit",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "store",
storageCapacity: 10,
triggers: [],
},
},
};
addEvent(storageEvent);
eventData.point = {
uuid: storageEvent.point.uuid,
position: storageEvent.point.position,
rotation: storageEvent.point.rotation,
};
}
const completeData = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
eventData: eventData,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", completeData);
const asset: Asset = {
modelUuid: completeData.modelUuid,
modelName: completeData.modelName,
assetId: completeData.assetId,
position: completeData.position,
rotation: [
completeData.rotation.x,
completeData.rotation.y,
completeData.rotation.z,
] as [number, number, number],
isLocked: completeData.isLocked,
isCollidable: false,
isVisible: completeData.isVisible,
opacity: 1,
eventData: completeData.eventData,
};
addAsset(asset);
} else {
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId,
position: newFloorItem.position,
rotation: {
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false,
isVisible: true,
socketId: socket.id,
versionId: versionId,
projectId: projectId,
userId: userId,
};
socket.emit("v1:model-asset:add", data);
const asset = {
modelUuid: data.modelUuid,
modelName: data.modelName,
assetId: data.assetId,
position: data.position,
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [
number,
number,
number
],
isLocked: data.isLocked,
isCollidable: false,
isVisible: data.isVisible,
opacity: 1,
};
addAsset(asset);
}
}
export default addAssetModel;

View File

@@ -7,14 +7,14 @@ import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { CameraControls } from '@react-three/drei';
import { useAssetsStore } from '../../../../../store/builder/useAssetStore';
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../../simulation/products/productContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../functions/getUserData';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useVersionContext } from '../../../version/versionContext';
function Model({ asset }: { readonly asset: Asset }) {
const { camera, controls, gl } = useThree();
@@ -22,11 +22,12 @@ function Model({ asset }: { readonly asset: Asset }) {
const { toggleView } = useToggleView();
const { subModule } = useSubModuleStore();
const { activeModule } = useModuleStore();
const { removeAsset } = useAssetsStore();
const { assetStore, eventStore, productStore } = useSceneContext();
const { removeAsset } = assetStore();
const { setTop } = useTopData();
const { setLeft } = useLeftData();
const { getIsEventInProduct } = useProductStore();
const { getEventByModelUuid } = useEventsStore();
const { getIsEventInProduct } = productStore();
const { getEventByModelUuid } = eventStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
@@ -40,7 +41,10 @@ function Model({ asset }: { readonly asset: Asset }) {
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const groupRef = useRef<THREE.Group>(null);
const { toolMode } = useToolMode();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization } = getUserData();
useEffect(() => {
setDeletableFloorItem(null);
@@ -162,9 +166,7 @@ function Model({ asset }: { readonly asset: Asset }) {
const handleClick = (asset: Asset) => {
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
@@ -173,18 +175,19 @@ function Model({ asset }: { readonly asset: Asset }) {
//SOCKET
const data = {
organization: organization,
organization,
modelUuid: asset.modelUuid,
modelName: asset.modelName,
socketId: socket.id,
userId,
versionId: selectedVersion?.versionId || '',
projectId
}
const response = socket.emit('v1:model-asset:delete', data)
useEventsStore.getState().removeEvent(asset.modelUuid);
useProductStore.getState().deleteEvent(asset.modelUuid);
eventStore.getState().removeEvent(asset.modelUuid);
productStore.getState().deleteEvent(asset.modelUuid);
if (response) {

View File

@@ -1,14 +1,15 @@
import { useAssetsStore } from '../../../../store/builder/useAssetStore';
import Model from './model/model';
import { useThree } from '@react-three/fiber';
import { CameraControls } from '@react-three/drei';
import { Vector3 } from 'three';
import { useSelectedFloorItem } from '../../../../store/builder/store';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
import { useSceneContext } from '../../../scene/sceneContext';
function Models() {
const { controls } = useThree();
const { assets } = useAssetsStore();
const { assetStore } = useSceneContext();
const { assets } = assetStore();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();

View File

@@ -51,6 +51,7 @@ import { useParams } from "react-router-dom";
import AislesGroup from "./aisle/aislesGroup";
import WallGroup from "./wall/wallGroup";
import { useBuilderStore } from "../../store/builder/useBuilderStore";
import { getUserData } from "../../functions/getUserData";
export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
@@ -104,6 +105,7 @@ export default function Builder() {
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { projectId } = useParams();
const { setHoveredPoint } = useBuilderStore();
const { userId, organization } = getUserData();
// const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader();
@@ -134,11 +136,9 @@ export default function Builder() {
}, [toggleView]);
useEffect(() => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
async function fetchVisibility() {
const data = await findEnvironment(organization, localStorage.getItem("userId")!, projectId);
const data = await findEnvironment(organization, userId, projectId);
if (data) {
setRoofVisibility(data.roofVisibility);
setWallVisibility(data.wallVisibility);
@@ -255,8 +255,6 @@ export default function Builder() {
plane={plane}
/>
{/* <WallGroup /> */}
<AislesGroup />
<MeasurementTool />
@@ -275,6 +273,8 @@ export default function Builder() {
<LayoutImage />
</Bvh>
{/* <WallGroup /> */}
</>
);
}

View File

@@ -8,6 +8,8 @@ import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBluepri
import * as Types from '../../../types/world/worldTypes';
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { useVersionContext } from '../version/versionContext';
// Interface defining the props for the DxfFile component
interface DxfFileProps {
@@ -35,7 +37,10 @@ const DxfFile = ({
const { setUpdateScene } = useUpdateScene();
const { toggleView } = useToggleView();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization } = getUserData();
// Refs for storing line objects
const lineRefs = useRef<Line[]>([]);
@@ -56,9 +61,6 @@ const DxfFile = ({
lines.current.push(...dfxWallGenerate);
dfxWallGenerate.map((line: any) => {
const lineData = arrayLineToObject(line as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
@@ -67,11 +69,12 @@ const DxfFile = ({
//SOCKET
const input = {
organization: organization,
organization,
layer: lineData.layer,
line: lineData.line,
type: lineData.type,
socketId: socket.id,
versionId: selectedVersion?.versionId || '',
projectId,
userId
}

View File

@@ -1,84 +1,100 @@
import * as THREE from 'three';
import { DragControls } from 'three/examples/jsm/controls/DragControls';
import * as CONSTANTS from '../../../types/world/worldConstants';
import DragPoint from '../geomentries/points/dragPoint';
import * as THREE from "three";
import { DragControls } from "three/examples/jsm/controls/DragControls";
import * as CONSTANTS from "../../../types/world/worldConstants";
import DragPoint from "../geomentries/points/dragPoint";
import * as Types from "../../../types/world/worldTypes";
// import { updatePoint } from '../../../services/factoryBuilder/lines/updatePointApi';
import { Socket } from 'socket.io-client';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../functions/getUserData";
export default async function addDragControl(
dragPointControls: Types.RefDragControl,
currentLayerPoint: Types.RefMeshArray,
state: Types.ThreeState,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
onlyFloorlines: Types.RefOnlyFloorLines,
socket: Socket<any>,
projectId?:string
dragPointControls: Types.RefDragControl,
currentLayerPoint: Types.RefMeshArray,
state: Types.ThreeState,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
onlyFloorlines: Types.RefOnlyFloorLines,
socket: Socket<any>,
projectId?: string,
versionId?: string
) {
////////// Dragging Point and also change the size to indicate during hover //////////
////////// Dragging Point and also change the size to indicate during hover //////////
dragPointControls.current = new DragControls(
currentLayerPoint.current,
state.camera,
state.gl.domElement
);
dragPointControls.current.enabled = false;
const { userId, organization, email } = getUserData();
dragPointControls.current.addEventListener("drag", function (event) {
const object = event.object;
if (object.visible) {
(state.controls as any).enabled = false;
DragPoint(
event as any,
floorPlanGroupPoint,
floorPlanGroupLine,
state.scene,
lines,
onlyFloorlines
);
} else {
(state.controls as any).enabled = true;
}
});
dragPointControls.current = new DragControls(currentLayerPoint.current, state.camera, state.gl.domElement);
dragPointControls.current.enabled = false;
dragPointControls.current.addEventListener("dragstart", function (event) { });
dragPointControls.current.addEventListener('drag', function (event) {
const object = event.object;
if (object.visible) {
(state.controls as any).enabled = false;
DragPoint(event as any, floorPlanGroupPoint, floorPlanGroupLine, state.scene, lines, onlyFloorlines)
} else {
(state.controls as any).enabled = true;
}
});
dragPointControls.current.addEventListener("dragend", async function (event) {
if (!dragPointControls.current) return;
dragPointControls.current.addEventListener('dragstart', function (event) {
});
//REST
dragPointControls.current.addEventListener('dragend', async function (event) {
if (!dragPointControls.current) return;
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
// await updatePoint(
// organization,
// { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z },
// event.object.uuid,
// )
//REST
//SOCKET
// await updatePoint(
// organization,
// { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z },
// event.object.uuid,
// )
const data = {
organization,
position: {
x: event.object.position.x,
y: 0.01,
z: event.object.position.z,
},
uuid: event.object.uuid,
socketId: socket.id,
versionId,
projectId,
userId,
};
//SOCKET
socket.emit("v1:Line:update", data);
const data = {
organization: organization,
position: { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z },
uuid: event.object.uuid,
socketId: socket.id,
projectId,
userId
}
if (state.controls) {
(state.controls as any).enabled = true;
}
});
socket.emit('v1:Line:update', data);
dragPointControls.current.addEventListener("hoveron", function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(
event.object.userData.color
);
}
});
if (state.controls) {
(state.controls as any).enabled = true;
}
});
dragPointControls.current.addEventListener('hoveron', function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(event.object.userData.color)
}
});
dragPointControls.current.addEventListener('hoveroff', function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor))
}
});
}
dragPointControls.current.addEventListener("hoveroff", function (event: any) {
if ((event.object as Types.Mesh).name === "point") {
event.object.material.uniforms.uInnerColor.value.set(
new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor)
);
}
});
}

View File

@@ -1,157 +1,146 @@
import * as THREE from 'three';
import * as THREE from "three";
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as CONSTANTS from "../../../../types/world/worldConstants";
import addPointToScene from '../points/addPointToScene';
import addLineToScene from '../lines/addLineToScene';
import splitLine from '../lines/splitLine';
import removeReferenceLine from '../lines/removeReferenceLine';
import getClosestIntersection from '../lines/getClosestIntersection';
import arrayLineToObject from '../lines/lineConvertions/arrayLineToObject';
import addPointToScene from "../points/addPointToScene";
import addLineToScene from "../lines/addLineToScene";
import splitLine from "../lines/splitLine";
import removeReferenceLine from "../lines/removeReferenceLine";
import getClosestIntersection from "../lines/getClosestIntersection";
import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject";
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
import { Socket } from 'socket.io-client';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
async function drawOnlyFloor(
raycaster: THREE.Raycaster,
state: Types.ThreeState,
camera: THREE.Camera,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
onlyFloorline: Types.RefOnlyFloorLine,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?:string
raycaster: THREE.Raycaster,
state: Types.ThreeState,
camera: THREE.Camera,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
onlyFloorline: Types.RefOnlyFloorLine,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
////////// Creating lines Based on the positions clicked //////////
////////// Creating lines Based on the positions clicked //////////
if (!plane.current) return;
const { userId, organization, email } = getUserData();
const intersects = raycaster.intersectObject(plane.current, true);
const intersectsLines = raycaster.intersectObjects(
floorPlanGroupLine.current.children,
true
);
const intersectsPoint = raycaster.intersectObjects(
floorPlanGroupPoint.current.children,
true
);
const VisibleintersectsPoint = intersectsPoint.find(
(intersect) => intersect.object.visible
);
const visibleIntersect = intersectsLines.find(
(intersect) =>
intersect.object.visible &&
intersect.object.name !== CONSTANTS.lineConfig.referenceName
);
if (
(intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) &&
intersectsLines.length > 0 &&
!isSnapped.current &&
!ispreSnapped.current
) {
////////// Clicked on a preexisting Line //////////
if (!plane.current) return
const intersects = raycaster.intersectObject(plane.current, true);
const intersectsLines = raycaster.intersectObjects(floorPlanGroupLine.current.children, true);
const intersectsPoint = raycaster.intersectObjects(floorPlanGroupPoint.current.children, true);
const VisibleintersectsPoint = intersectsPoint.find(intersect => intersect.object.visible);
const visibleIntersect = intersectsLines.find(intersect => intersect.object.visible && intersect.object.name !== CONSTANTS.lineConfig.referenceName);
if (
visibleIntersect &&
(intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.floorName ||
intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName)
) {
let pointColor, lineColor;
if (
intersectsLines[0].object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName
) {
pointColor = CONSTANTS.pointConfig.wallOuterColor;
lineColor = CONSTANTS.lineConfig.wallColor;
} else {
pointColor = CONSTANTS.pointConfig.floorOuterColor;
lineColor = CONSTANTS.lineConfig.floorColor;
}
let IntersectsPoint = new THREE.Vector3(
intersects[0].point.x,
0.01,
intersects[0].point.z
);
if (
isAngleSnapped.current &&
line.current.length > 0 &&
anglesnappedPoint.current
) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint =
visibleIntersect.object.geometry.parameters.path.getPoints(
CONSTANTS.lineConfig.lineIntersectionPoints
);
let intersectionPoint = getClosestIntersection(
ThroughPoint,
IntersectsPoint
);
if ((intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) && intersectsLines.length > 0 && !isSnapped.current && !ispreSnapped.current) {
if (intersectionPoint) {
const newLines = splitLine(
visibleIntersect,
intersectionPoint,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lines,
setDeletedLines,
floorPlanGroupLine,
socket,
pointColor,
lineColor,
intersectsLines[0].object.userData.linePoints[0][3],
projectId
);
setNewLines([newLines[0], newLines[1]]);
////////// Clicked on a preexisting Line //////////
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.floorName,
]);
if (visibleIntersect && (intersectsLines[0].object.userData.linePoints[0][3] === CONSTANTS.lineConfig.floorName || intersectsLines[0].object.userData.linePoints[0][3] === CONSTANTS.lineConfig.wallName)) {
let pointColor, lineColor;
if (intersectsLines[0].object.userData.linePoints[0][3] === CONSTANTS.lineConfig.wallName) {
pointColor = CONSTANTS.pointConfig.wallOuterColor;
lineColor = CONSTANTS.lineConfig.wallColor;
} else {
pointColor = CONSTANTS.pointConfig.floorOuterColor;
lineColor = CONSTANTS.lineConfig.floorColor;
}
let IntersectsPoint = new THREE.Vector3(intersects[0].point.x, 0.01, intersects[0].point.z);
if (isAngleSnapped.current && line.current.length > 0 && anglesnappedPoint.current) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint = (visibleIntersect.object.geometry.parameters.path).getPoints(CONSTANTS.lineConfig.lineIntersectionPoints);
let intersectionPoint = getClosestIntersection(ThroughPoint, IntersectsPoint);
if (intersectionPoint) {
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, pointColor, lineColor, intersectsLines[0].object.userData.linePoints[0][3],projectId);
setNewLines([newLines[0], newLines[1]]);
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.floorName]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
userId
}
socket.emit('v1:Line:create', input);
setNewLines([newLines[0], newLines[1], line.current]);
onlyFloorline.current.push(line.current as Types.Line);
onlyFloorlines.current.push(onlyFloorline.current);
onlyFloorline.current = [];
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.floorColor, line.current, floorPlanGroupLine);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
return;
}
}
}
}
if (intersects.length > 0 && intersectsLines.length === 0) {
////////// Clicked on an empty place or a point //////////
let intersectionPoint = intersects[0].point;
if (isAngleSnapped.current && line.current.length > 0 && anglesnappedPoint.current) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(intersectionPoint, CONSTANTS.pointConfig.floorOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, CONSTANTS.lineConfig.floorName);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.floorName]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
onlyFloorline.current.push(line.current as Types.Line);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
@@ -159,28 +148,126 @@ async function drawOnlyFloor(
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
userId
}
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
versionId,
userId,
};
socket.emit('v1:Line:create', input);
socket.emit("v1:Line:create", input);
setNewLines([line.current]);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.floorColor, line.current, floorPlanGroupLine);
const lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
if (isSnapped.current) { ////////// Add this to stop the drawing mode after snapping //////////
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
setNewLines([newLines[0], newLines[1], line.current]);
onlyFloorline.current.push(line.current as Types.Line);
onlyFloorlines.current.push(onlyFloorline.current);
onlyFloorline.current = [];
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.floorColor,
line.current,
floorPlanGroupLine
);
removeReferenceLine(
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
line
);
}
return;
}
}
}
}
if (intersects.length > 0 && intersectsLines.length === 0) {
////////// Clicked on an empty place or a point //////////
let intersectionPoint = intersects[0].point;
if (
isAngleSnapped.current &&
line.current.length > 0 &&
anglesnappedPoint.current
) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(
intersectionPoint,
CONSTANTS.pointConfig.floorOuterColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
CONSTANTS.lineConfig.floorName
);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.floorName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
onlyFloorline.current.push(line.current as Types.Line);
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:create", input);
setNewLines([line.current]);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.floorColor,
line.current,
floorPlanGroupLine
);
const lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
if (isSnapped.current) {
////////// Add this to stop the drawing mode after snapping //////////
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
onlyFloorlines.current.push(onlyFloorline.current);
onlyFloorline.current = [];
}
}
}
export default drawOnlyFloor;
export default drawOnlyFloor;

View File

@@ -1,93 +1,104 @@
import { toast } from 'react-toastify';
import RemoveConnectedLines from '../lines/removeConnectedLines';
import { toast } from "react-toastify";
import RemoveConnectedLines from "../lines/removeConnectedLines";
import * as Types from '../../../../types/world/worldTypes';
import { Socket } from 'socket.io-client';
import * as Types from "../../../../types/world/worldTypes";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
// import { deleteLayer } from '../../../../services/factoryBuilder/lines/deleteLayerApi';
async function DeleteLayer(
removedLayer: Types.Number,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
onlyFloorlines: Types.RefOnlyFloorLines,
floorGroup: Types.RefGroup,
setDeletedLines: any,
setRemovedLayer: Types.setRemoveLayerSetState,
socket: Socket<any>,
projectId?:string
removedLayer: Types.Number,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
onlyFloorlines: Types.RefOnlyFloorLines,
floorGroup: Types.RefGroup,
setDeletedLines: any,
setRemovedLayer: Types.setRemoveLayerSetState,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
////////// Remove the Lines from the lines.current based on the removed layer and rearrange the layer number that are higher than the removed layer //////////
////////// Remove the Lines from the lines.current based on the removed layer and rearrange the layer number that are higher than the removed layer //////////
const removedLines: Types.Lines = lines.current.filter(
(line) => line[0][2] === removedLayer
);
const { userId, organization } = getUserData();
const removedLines: Types.Lines = lines.current.filter(line => line[0][2] === removedLayer);
//REST
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
// await deleteLayer(organization, removedLayer);
//REST
//SOCKET
// await deleteLayer(organization, removedLayer);
const data = {
organization,
layer: removedLayer,
socketId: socket.id,
versionId,
projectId,
userId,
};
//SOCKET
socket.emit("v1:Line:delete:layer", data);
const data = {
organization: organization,
layer: removedLayer,
socketId: socket.id,
projectId,
userId
////////// Remove Points and lines from the removed layer //////////
removedLines.forEach((line) => {
line.forEach((removedPoint) => {
RemoveConnectedLines(
removedPoint[1],
floorPlanGroupLine,
floorPlanGroupPoint,
setDeletedLines,
lines
);
});
});
////////// Update the remaining lines layer values in the userData and in lines.current //////////
let remaining = lines.current.filter((line) => line[0][2] !== removedLayer);
let updatedLines: Types.Lines = [];
remaining.forEach((line) => {
let newLines: Types.Line = [...line];
if (newLines[0][2] > removedLayer) {
newLines[0][2] -= 1;
newLines[1][2] -= 1;
}
socket.emit('v1:Line:delete:layer', data);
////////// Remove Points and lines from the removed layer //////////
removedLines.forEach((line) => {
line.forEach((removedPoint) => {
RemoveConnectedLines(removedPoint[1], floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines);
});
});
////////// Update the remaining lines layer values in the userData and in lines.current //////////
let remaining = lines.current.filter(line => line[0][2] !== removedLayer);
let updatedLines: Types.Lines = [];
remaining.forEach(line => {
let newLines: Types.Line = [...line];
if (newLines[0][2] > removedLayer) {
newLines[0][2] -= 1;
newLines[1][2] -= 1;
}
const matchingLine = floorPlanGroupLine.current.children.find(l => l.userData.linePoints[0][1] === line[0][1] && l.userData.linePoints[1][1] === line[1][1]);
if (matchingLine) {
const updatedUserData = matchingLine.userData;
updatedUserData.linePoints[0][2] = newLines[0][2];
updatedUserData.linePoints[1][2] = newLines[1][2];
}
updatedLines.push(newLines);
});
lines.current = updatedLines;
localStorage.setItem("Lines", JSON.stringify(lines.current));
////////// Also remove OnlyFloorLines and update it in localstorage //////////
onlyFloorlines.current = onlyFloorlines.current.filter((floor) => {
return floor[0][0][2] !== removedLayer;
});
const meshToRemove: any = floorGroup.current?.children.find((mesh) =>
mesh.name === `Only_Floor_Line_${removedLayer}`
const matchingLine = floorPlanGroupLine.current.children.find(
(l) =>
l.userData.linePoints[0][1] === line[0][1] &&
l.userData.linePoints[1][1] === line[1][1]
);
if (meshToRemove) {
(<any>meshToRemove.material).dispose();
(<any>meshToRemove.geometry).dispose();
floorGroup.current?.remove(meshToRemove);
if (matchingLine) {
const updatedUserData = matchingLine.userData;
updatedUserData.linePoints[0][2] = newLines[0][2];
updatedUserData.linePoints[1][2] = newLines[1][2];
}
updatedLines.push(newLines);
});
echo.success("Layer Removed!");
setRemovedLayer(null);
lines.current = updatedLines;
localStorage.setItem("Lines", JSON.stringify(lines.current));
////////// Also remove OnlyFloorLines and update it in localstorage //////////
onlyFloorlines.current = onlyFloorlines.current.filter((floor) => {
return floor[0][0][2] !== removedLayer;
});
const meshToRemove: any = floorGroup.current?.children.find(
(mesh) => mesh.name === `Only_Floor_Line_${removedLayer}`
);
if (meshToRemove) {
(<any>meshToRemove.material).dispose();
(<any>meshToRemove.geometry).dispose();
floorGroup.current?.remove(meshToRemove);
}
echo.success("Layer Removed!");
setRemovedLayer(null);
}
export default DeleteLayer;

View File

@@ -2,91 +2,91 @@ import { Socket } from "socket.io-client";
// import { deleteLineApi } from "../../../../services/factoryBuilder/lines/deleteLineApi";
import * as Types from "../../../../types/world/worldTypes";
import { toast } from 'react-toastify';
import { toast } from "react-toastify";
import { getUserData } from "../../../../functions/getUserData";
function deleteLine(
hoveredDeletableLine: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
setDeletedLines: any,
socket: Socket<any>,
projectId?: string
hoveredDeletableLine: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroupPoint: Types.RefGroup,
setDeletedLines: any,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): void {
const { userId, organization, email } = getUserData();
////////// Deleting a line and the points if they are not connected to any other line //////////
////////// Deleting a line and the points if they are not connected to any other line //////////
if (!hoveredDeletableLine.current) {
return;
}
if (!hoveredDeletableLine.current) {
return;
}
const linePoints = hoveredDeletableLine.current.userData.linePoints;
const connectedpoints = [linePoints[0][1], linePoints[1][1]];
const linePoints = hoveredDeletableLine.current.userData.linePoints;
const connectedpoints = [linePoints[0][1], linePoints[1][1]];
//REST
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
// deleteLineApi(
// organization,
// [
// { "uuid": linePoints[0][1] },
// { "uuid": linePoints[1][1] }
// ]
// )
//REST
//SOCKET
// deleteLineApi(
// organization,
// [
// { "uuid": linePoints[0][1] },
// { "uuid": linePoints[1][1] }
// ]
// )
const data = {
organization,
line: [{ uuid: linePoints[0][1] }, { uuid: linePoints[1][1] }],
socketId: socket.id,
versionId,
projectId,
userId,
};
//SOCKET
socket.emit("v1:Line:delete", data);
const data = {
organization: organization,
line: [
{ "uuid": linePoints[0][1] },
{ "uuid": linePoints[1][1] }
],
socketId: socket.id,
projectId,
userId
}
onlyFloorlines.current = onlyFloorlines.current
.map((floorline) =>
floorline.filter(
(line) =>
line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1]
)
)
.filter((floorline) => floorline.length > 0);
socket.emit('v1:Line:delete', data);
lines.current = lines.current.filter((item) => item !== linePoints);
(<any>hoveredDeletableLine.current.material).dispose();
(<any>hoveredDeletableLine.current.geometry).dispose();
floorPlanGroupLine.current.remove(hoveredDeletableLine.current);
setDeletedLines([linePoints]);
onlyFloorlines.current = onlyFloorlines.current.map(floorline =>
floorline.filter(line => line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1])
).filter(floorline => floorline.length > 0);
lines.current = lines.current.filter(item => item !== linePoints);
(<any>hoveredDeletableLine.current.material).dispose();
(<any>hoveredDeletableLine.current.geometry).dispose();
floorPlanGroupLine.current.remove(hoveredDeletableLine.current);
setDeletedLines([linePoints]);
connectedpoints.forEach((pointUUID) => {
let isConnected = false;
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints;
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === pointUUID || uuid2 === pointUUID) {
isConnected = true;
}
});
if (!isConnected) {
floorPlanGroupPoint.current.children.forEach((point: any) => {
if (point.uuid === pointUUID) {
(<any>point.material).dispose();
(<any>point.geometry).dispose();
floorPlanGroupPoint.current.remove(point);
}
});
}
connectedpoints.forEach((pointUUID) => {
let isConnected = false;
floorPlanGroupLine.current.children.forEach((line) => {
const linePoints = line.userData.linePoints;
const uuid1 = linePoints[0][1];
const uuid2 = linePoints[1][1];
if (uuid1 === pointUUID || uuid2 === pointUUID) {
isConnected = true;
}
});
echo.success("Line Removed!");
if (!isConnected) {
floorPlanGroupPoint.current.children.forEach((point: any) => {
if (point.uuid === pointUUID) {
(<any>point.material).dispose();
(<any>point.geometry).dispose();
floorPlanGroupPoint.current.remove(point);
}
});
}
});
echo.success("Line Removed!");
}
export default deleteLine;

View File

@@ -15,6 +15,8 @@ import * as Types from "../../../../../types/world/worldTypes";
import getRoomsFromLines from "../getRoomsFromLines";
import * as turf from '@turf/turf';
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useVersionContext } from "../../../version/versionContext";
const DistanceText = () => {
const [lines, setLines] = useState<
@@ -31,7 +33,10 @@ const DistanceText = () => {
const { deletedLines, setDeletedLines } = useDeletedLines();
const [linesState, setLinesState] = useState<Types.Lines>([]);
const { roomsState, setRoomsState } = useRoomsState();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { organization, email } = getUserData();
useEffect(() => {
@@ -93,11 +98,9 @@ const DistanceText = () => {
useEffect(() => {
const email = localStorage.getItem("email");
if (!email) return;
const organization = email.split("@")[1].split(".")[0];
getLines(organization,projectId).then((data) => {
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
data = objectLinesToArray(data);
setLinesState(data);
@@ -127,7 +130,7 @@ const DistanceText = () => {
});
setLines(lines);
});
}, [activeLayer]);
}, [activeLayer, selectedVersion?.versionId]);
useEffect(() => {
if (newLines.length > 0) {

View File

@@ -1,146 +1,129 @@
import * as THREE from 'three';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as THREE from "three";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import addPointToScene from '../points/addPointToScene';
import addLineToScene from './addLineToScene';
import splitLine from './splitLine';
import removeReferenceLine from './removeReferenceLine';
import getClosestIntersection from './getClosestIntersection';
import addPointToScene from "../points/addPointToScene";
import addLineToScene from "./addLineToScene";
import splitLine from "./splitLine";
import removeReferenceLine from "./removeReferenceLine";
import getClosestIntersection from "./getClosestIntersection";
import * as Types from "../../../../types/world/worldTypes";
import arrayLineToObject from './lineConvertions/arrayLineToObject';
import arrayLineToObject from "./lineConvertions/arrayLineToObject";
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
import { Socket } from 'socket.io-client';
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
async function drawWall(
raycaster: THREE.Raycaster,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?: string
raycaster: THREE.Raycaster,
plane: Types.RefMesh,
floorPlanGroupPoint: Types.RefGroup,
snappedPoint: Types.RefVector3,
isSnapped: Types.RefBoolean,
isSnappedUUID: Types.RefString,
line: Types.RefLine,
ispreSnapped: Types.RefBoolean,
anglesnappedPoint: Types.RefVector3,
isAngleSnapped: Types.RefBoolean,
lines: Types.RefLines,
floorPlanGroupLine: Types.RefGroup,
floorPlanGroup: Types.RefGroup,
ReferenceLineMesh: Types.RefMesh,
LineCreated: Types.RefBoolean,
currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl,
setNewLines: any,
setDeletedLines: any,
activeLayer: Types.Number,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): Promise<void> {
const { userId, organization, email } = getUserData();
////////// Creating lines Based on the positions clicked //////////
////////// Creating lines Based on the positions clicked //////////
////////// Allows the user lines that represents walls and roof, floor if forms a polygon //////////
////////// Allows the user lines that represents walls and roof, floor if forms a polygon //////////
if (!plane.current) return;
let intersects = raycaster.intersectObject(plane.current, true);
let intersectsLines = raycaster.intersectObjects(
floorPlanGroupLine.current.children,
true
);
let intersectsPoint = raycaster.intersectObjects(
floorPlanGroupPoint.current.children,
true
);
if (!plane.current) return
let intersects = raycaster.intersectObject(plane.current, true);
const VisibleintersectsPoint = intersectsPoint.find(
(intersect) => intersect.object.visible
);
const visibleIntersect = intersectsLines.find(
(intersect) =>
intersect.object.visible &&
intersect.object.name !== CONSTANTS.lineConfig.referenceName &&
intersect.object.userData.linePoints[0][3] ===
CONSTANTS.lineConfig.wallName
);
let intersectsLines = raycaster.intersectObjects(floorPlanGroupLine.current.children, true);
let intersectsPoint = raycaster.intersectObjects(floorPlanGroupPoint.current.children, true);
if (
(intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) &&
intersectsLines.length > 0 &&
!isSnapped.current &&
!ispreSnapped.current
) {
////////// Clicked on a preexisting Line //////////
const VisibleintersectsPoint = intersectsPoint.find(intersect => intersect.object.visible);
const visibleIntersect = intersectsLines.find(intersect => intersect.object.visible && intersect.object.name !== CONSTANTS.lineConfig.referenceName && intersect.object.userData.linePoints[0][3] === CONSTANTS.lineConfig.wallName);
if (visibleIntersect && intersects) {
let IntersectsPoint = new THREE.Vector3(
intersects[0].point.x,
0.01,
intersects[0].point.z
);
if ((intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) && intersectsLines.length > 0 && !isSnapped.current && !ispreSnapped.current) {
if (isAngleSnapped.current && anglesnappedPoint.current) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint =
visibleIntersect.object.geometry.parameters.path.getPoints(
CONSTANTS.lineConfig.lineIntersectionPoints
);
let intersectionPoint = getClosestIntersection(
ThroughPoint,
IntersectsPoint
);
////////// Clicked on a preexisting Line //////////
if (intersectionPoint) {
const newLines = splitLine(
visibleIntersect,
intersectionPoint,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lines,
setDeletedLines,
floorPlanGroupLine,
socket,
CONSTANTS.pointConfig.wallOuterColor,
CONSTANTS.lineConfig.wallColor,
CONSTANTS.lineConfig.wallName,
projectId,
versionId
);
setNewLines([newLines[0], newLines[1]]);
if (visibleIntersect && intersects) {
let IntersectsPoint = new THREE.Vector3(intersects[0].point.x, 0.01, intersects[0].point.z);
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.wallName,
]);
if (isAngleSnapped.current && anglesnappedPoint.current) {
IntersectsPoint = anglesnappedPoint.current;
}
if (visibleIntersect.object instanceof THREE.Mesh) {
const ThroughPoint = (visibleIntersect.object.geometry.parameters.path).getPoints(CONSTANTS.lineConfig.lineIntersectionPoints);
let intersectionPoint = getClosestIntersection(ThroughPoint, IntersectsPoint);
if (intersectionPoint) {
const newLines = splitLine(visibleIntersect, intersectionPoint, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lines, setDeletedLines, floorPlanGroupLine, socket, CONSTANTS.pointConfig.wallOuterColor, CONSTANTS.lineConfig.wallColor, CONSTANTS.lineConfig.wallName,projectId);
setNewLines([newLines[0], newLines[1]]);
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.wallName,]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
userId
}
socket.emit('v1:Line:create', input);
setNewLines([newLines[0], newLines[1], line.current]);
lines.current.push(line.current as Types.Line);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.wallColor, line.current, floorPlanGroupLine);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
return;
}
}
}
}
if (intersects && intersects.length > 0) {
////////// Clicked on a emply place or a point //////////
let intersectionPoint = intersects[0].point;
if (isAngleSnapped.current && line.current.length > 0 && anglesnappedPoint.current) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(intersectionPoint, CONSTANTS.pointConfig.wallOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, CONSTANTS.lineConfig.wallName);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), isSnappedUUID.current!, activeLayer, CONSTANTS.lineConfig.wallName,]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
@@ -148,27 +131,115 @@ async function drawWall(
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
projectId,
userId
}
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit('v1:Line:create', input);
socket.emit("v1:Line:create", input);
setNewLines([line.current])
setNewLines([newLines[0], newLines[1], line.current]);
lines.current.push(line.current as Types.Line);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.wallColor, line.current, floorPlanGroupLine);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.wallColor,
line.current,
floorPlanGroupLine
);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
return;
}
if (isSnapped.current) {
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
}
}
}
if (intersects && intersects.length > 0) {
////////// Clicked on a emply place or a point //////////
let intersectionPoint = intersects[0].point;
if (
isAngleSnapped.current &&
line.current.length > 0 &&
anglesnappedPoint.current
) {
intersectionPoint = anglesnappedPoint.current;
}
if (isSnapped.current && line.current.length > 0 && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
}
if (!isSnapped.current && !ispreSnapped.current) {
addPointToScene(
intersectionPoint,
CONSTANTS.pointConfig.wallOuterColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
CONSTANTS.lineConfig.wallName
);
} else {
ispreSnapped.current = false;
isSnapped.current = false;
}
(line.current as Types.Line).push([
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
isSnappedUUID.current!,
activeLayer,
CONSTANTS.lineConfig.wallName,
]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
const data = arrayLineToObject(line.current as Types.Line);
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
socket.emit("v1:Line:create", input);
setNewLines([line.current]);
lines.current.push(line.current as Types.Line);
addLineToScene(
line.current[0][0],
line.current[1][0],
CONSTANTS.lineConfig.wallColor,
line.current,
floorPlanGroupLine
);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
if (isSnapped.current) {
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
}
}
export default drawWall;

View File

@@ -5,11 +5,11 @@ import * as Types from "../../../../types/world/worldTypes";
function getClosestIntersection(
intersects: Types.Vector3Array,
point: Types.Vector3
): Types.Vector3 | null {
): Types.Vector3 {
////////// A function that finds which point is closest from the intersects points that is given, Used in finding which point in a line is closest when clicked on a line during drawing //////////
let closestNewPoint: THREE.Vector3 | null = null;
let closestNewPoint: THREE.Vector3 = point;
let minDistance = Infinity;
for (const intersect of intersects) {

View File

@@ -1,132 +1,151 @@
import * as THREE from 'three';
import * as THREE from "three";
import addLineToScene from './addLineToScene';
import addPointToScene from '../points/addPointToScene';
import addLineToScene from "./addLineToScene";
import addPointToScene from "../points/addPointToScene";
import * as Types from "../../../../types/world/worldTypes";
import arrayLineToObject from '../lines/lineConvertions/arrayLineToObject';
import { Socket } from 'socket.io-client';
import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
// import { deleteLineApi } from '../../../../services/factoryBuilder/lines/deleteLineApi';
// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi';
function splitLine(
visibleIntersect: Types.IntersectionEvent,
intersectionPoint: Types.Vector3,
currentLayerPoint: Types.RefMeshArray,
floorPlanGroupPoint: Types.RefGroup,
dragPointControls: Types.RefDragControl,
isSnappedUUID: Types.RefString,
lines: Types.RefLines,
setDeletedLines: any,
floorPlanGroupLine: { current: THREE.Group },
socket: Socket<any>,
pointColor: Types.String,
lineColor: Types.String,
lineType: Types.String,
projectId?: string
visibleIntersect: Types.IntersectionEvent,
intersectionPoint: Types.Vector3,
currentLayerPoint: Types.RefMeshArray,
floorPlanGroupPoint: Types.RefGroup,
dragPointControls: Types.RefDragControl,
isSnappedUUID: Types.RefString,
lines: Types.RefLines,
setDeletedLines: any,
floorPlanGroupLine: { current: THREE.Group },
socket: Socket<any>,
pointColor: Types.String,
lineColor: Types.String,
lineType: Types.String,
projectId?: string,
versionId?: string,
): [Types.Line, Types.Line] {
////////// Removing the clicked line and splitting it with the clicked position adding a new point and two new lines //////////
////////// Removing the clicked line and splitting it with the clicked position adding a new point and two new lines //////////
const { userId, organization, email } = getUserData();
(visibleIntersect.object as any).material.dispose();
(visibleIntersect.object as any).geometry.dispose();
floorPlanGroupLine.current.remove(visibleIntersect.object);
setDeletedLines([visibleIntersect.object.userData.linePoints]);
//REST
((visibleIntersect.object as any).material).dispose();
((visibleIntersect.object as any).geometry).dispose();
floorPlanGroupLine.current.remove(visibleIntersect.object);
setDeletedLines([visibleIntersect.object.userData.linePoints]);
// deleteLineApi(
// organization,
// [
// { "uuid": visibleIntersect.object.userData.linePoints[0][1] },
// { "uuid": visibleIntersect.object.userData.linePoints[1][1] }
// ]
// )
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//SOCKET
//REST
const data = {
organization,
line: [
{ uuid: visibleIntersect.object.userData.linePoints[0][1] },
{ uuid: visibleIntersect.object.userData.linePoints[1][1] },
],
socketId: socket.id,
versionId,
projectId,
userId,
};
// deleteLineApi(
// organization,
// [
// { "uuid": visibleIntersect.object.userData.linePoints[0][1] },
// { "uuid": visibleIntersect.object.userData.linePoints[1][1] }
// ]
// )
socket.emit("v1:Line:delete", data);
//SOCKET
const point = addPointToScene(
intersectionPoint,
pointColor,
currentLayerPoint,
floorPlanGroupPoint,
dragPointControls,
isSnappedUUID,
lineType
);
const oldLinePoints = visibleIntersect.object.userData.linePoints;
lines.current = lines.current.filter((item) => item !== oldLinePoints);
const data = {
organization: organization,
line: [
{ "uuid": visibleIntersect.object.userData.linePoints[0][1] },
{ "uuid": visibleIntersect.object.userData.linePoints[1][1] }
],
socketId: socket.id,
projectId,
userId
}
const clickedPoint: Types.Point = [
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
point.uuid,
oldLinePoints[0][2],
lineType,
];
socket.emit('v1:Line:delete', data);
const start = oldLinePoints[0];
const end = oldLinePoints[1];
const point = addPointToScene(intersectionPoint, pointColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, isSnappedUUID, lineType);
const newLine1: Types.Line = [start, clickedPoint];
const newLine2: Types.Line = [clickedPoint, end];
const oldLinePoints = visibleIntersect.object.userData.linePoints;
lines.current = lines.current.filter(item => item !== oldLinePoints);
const line1 = arrayLineToObject(newLine1);
const line2 = arrayLineToObject(newLine2);
const clickedPoint: Types.Point = [
new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z),
point.uuid,
oldLinePoints[0][2],
lineType
];
//REST
const start = oldLinePoints[0];
const end = oldLinePoints[1];
// setLine(organization, line1.layer!, line1.line!, line1.type!);
const newLine1: Types.Line = [start, clickedPoint];
const newLine2: Types.Line = [clickedPoint, end];
//SOCKET
const line1 = arrayLineToObject(newLine1);
const line2 = arrayLineToObject(newLine2);
const input1 = {
organization,
layer: line1.layer,
line: line1.line,
type: line1.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
//REST
socket.emit("v1:Line:create", input1);
// setLine(organization, line1.layer!, line1.line!, line1.type!);
//REST
//SOCKET
// setLine(organization, line2.layer!, line2.line!, line2.type!);
const input1 = {
organization: organization,
layer: line1.layer,
line: line1.line,
type: line1.type,
socketId: socket.id,
projectId,
userId
}
//SOCKET
socket.emit('v1:Line:create', input1);
const input2 = {
organization,
layer: line2.layer,
line: line2.line,
type: line2.type,
socketId: socket.id,
versionId,
projectId,
userId,
};
//REST
socket.emit("v1:Line:create", input2);
// setLine(organization, line2.layer!, line2.line!, line2.type!);
lines.current.push(newLine1, newLine2);
//SOCKET
addLineToScene(
newLine1[0][0],
newLine1[1][0],
lineColor,
newLine1,
floorPlanGroupLine
);
addLineToScene(
newLine2[0][0],
newLine2[1][0],
lineColor,
newLine2,
floorPlanGroupLine
);
const input2 = {
organization: organization,
layer: line2.layer,
line: line2.line,
type: line2.type,
socketId: socket.id,
projectId,
userId
}
socket.emit('v1:Line:create', input2);
lines.current.push(newLine1, newLine2);
addLineToScene(newLine1[0][0], newLine1[1][0], lineColor, newLine1, floorPlanGroupLine);
addLineToScene(newLine2[0][0], newLine2[1][0], lineColor, newLine2, floorPlanGroupLine);
return [newLine1, newLine2];
return [newLine1, newLine2];
}
export default splitLine;

View File

@@ -1,62 +1,72 @@
import * as Types from "../../../../types/world/worldTypes";
import { toast } from 'react-toastify';
import { toast } from "react-toastify";
import RemoveConnectedLines from "../lines/removeConnectedLines";
// import { deletePointApi } from "../../../../services/factoryBuilder/lines/deletePointApi";
import { Socket } from "socket.io-client";
import { getUserData } from "../../../../functions/getUserData";
function deletePoint(
hoveredDeletablePoint: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
setDeletedLines: any,
socket: Socket<any>,
projectId?:string
hoveredDeletablePoint: Types.RefMesh,
onlyFloorlines: Types.RefOnlyFloorLines,
floorPlanGroupPoint: Types.RefGroup,
floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines,
setDeletedLines: any,
socket: Socket<any>,
projectId?: string,
versionId?: string,
): void {
////////// Deleting a Point and the lines that are connected to it //////////
////////// Deleting a Point and the lines that are connected to it //////////
if (!hoveredDeletablePoint.current) {
return;
}
if (!hoveredDeletablePoint.current) {
return;
}
const { userId, organization, email } = getUserData();
(<any>hoveredDeletablePoint.current.material).dispose();
(<any>hoveredDeletablePoint.current.geometry).dispose();
floorPlanGroupPoint.current.remove(hoveredDeletablePoint.current);
const DeletedPointUUID = hoveredDeletablePoint.current.uuid;
(<any>hoveredDeletablePoint.current.material).dispose();
(<any>hoveredDeletablePoint.current.geometry).dispose();
floorPlanGroupPoint.current.remove(hoveredDeletablePoint.current);
const DeletedPointUUID = hoveredDeletablePoint.current.uuid;
//REST
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
// deletePointApi(organization, DeletedPointUUID);
//REST
//SOCKET
// deletePointApi(organization, DeletedPointUUID);
const data = {
organization,
uuid: DeletedPointUUID,
socketId: socket.id,
versionId,
projectId,
userId,
};
//SOCKET
// console.log('data: ', data);
socket.emit("v1:Line:delete:point", data);
const data = {
organization: organization,
uuid: DeletedPointUUID,
socketId: socket.id,
projectId,
userId
}
////////// Update onlyFloorlines.current to remove references to the deleted point //////////
// console.log('data: ', data);
socket.emit('v1:Line:delete:point', data);
onlyFloorlines.current = onlyFloorlines.current
.map((floorline) =>
floorline.filter(
(line) =>
line[0][1] !== DeletedPointUUID && line[1][1] !== DeletedPointUUID
)
)
.filter((floorline) => floorline.length > 0);
////////// Update onlyFloorlines.current to remove references to the deleted point //////////
RemoveConnectedLines(
DeletedPointUUID,
floorPlanGroupLine,
floorPlanGroupPoint,
setDeletedLines,
lines
);
onlyFloorlines.current = onlyFloorlines.current.map(floorline =>
floorline.filter(line => line[0][1] !== DeletedPointUUID && line[1][1] !== DeletedPointUUID)
).filter(floorline => floorline.length > 0);
RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines);
echo.success("Point Removed!");
echo.success("Point Removed!");
}
export default deletePoint;

View File

@@ -1,142 +1,164 @@
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { toast } from 'react-toastify';
import * as THREE from 'three';
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 { Socket } from 'socket.io-client';
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { Socket } from "socket.io-client";
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { getUserData } from "../../../../functions/getUserData";
async function AddWallItems(
selected: any,
raycaster: THREE.Raycaster,
CSGGroup: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
socket: Socket<any>,
projectId?: string
selected: any,
raycaster: THREE.Raycaster,
CSGGroup: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
socket: Socket<any>,
projectId?: string,
versionId?: string
): Promise<void> {
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) return;
const { userId, organization } = getUserData();
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}`;
const intersectionPoint = wallRaycastIntersection;
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
if (!wallRaycastIntersection) return;
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
loader.setDRACOLoader(dracoLoader);
const intersectionPoint = wallRaycastIntersection;
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
// Check THREE.js cache first
const cachedModel = THREE.Cache.get(selected.id);
if (cachedModel) {
handleModelLoad(cachedModel);
return;
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);
}
}
);
// 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;
}
async function handleModelLoad(gltf: GLTF) {
const model = gltf.scene.clone();
model.userData = { wall: intersectionPoint.object.parent };
// 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);
}
model.children[0].children.forEach((child) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
});
async function handleModelLoad(gltf: GLTF) {
const model = gltf.scene.clone();
model.userData = { wall: intersectionPoint.object.parent };
const boundingBox = new THREE.Box3().setFromObject(model);
const size = new THREE.Vector3();
boundingBox.getSize(size);
model.children[0].children.forEach((child) => {
if (child.name !== "CSG_REF") {
child.castShadow = true;
child.receiveShadow = true;
}
});
const csgscale = [size.x, size.y, size.z] as [number, number, number];
const boundingBox = new THREE.Box3().setFromObject(model);
const size = new THREE.Vector3();
boundingBox.getSize(size);
const center = new THREE.Vector3();
boundingBox.getCenter(center);
const csgposition = [center.x, center.y, center.z] as [
number,
number,
number
];
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,
assetId: 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 userId = localStorage.getItem("userId");
const data = {
organization: organization,
modelUuid: model.uuid,
modelName: newWallItem.modelName,
assetId: selected.id,
type: selected.subCategory,
csgposition: newWallItem.csgposition,
csgscale: newWallItem.csgscale,
position: newWallItem.position,
quaternion: newWallItem.quaternion,
scale: newWallItem.scale,
socketId: socket.id,
projectId,
userId
};
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));
echo.success("Model Added!");
return updatedItems;
});
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,
assetId: 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 data = {
organization,
modelUuid: model.uuid,
modelName: newWallItem.modelName,
assetId: selected.id,
type: selected.subCategory,
csgposition: newWallItem.csgposition,
csgscale: newWallItem.csgscale,
position: newWallItem.position,
quaternion: newWallItem.quaternion,
scale: newWallItem.scale,
socketId: socket.id,
versionId,
projectId,
userId,
};
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));
echo.success("Model Added!");
return updatedItems;
});
}
}
export default AddWallItems;
export default AddWallItems;

View File

@@ -1,60 +1,62 @@
import { getUserData } from "../../../../functions/getUserData";
import * as Types from "../../../../types/world/worldTypes";
// import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi';
import { Socket } from 'socket.io-client';
import { Socket } from "socket.io-client";
function DeleteWallItems(
hoveredDeletableWallItem: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
wallItems: Types.wallItems,
socket: Socket<any>,
projectId?: string
hoveredDeletableWallItem: Types.RefMesh,
setWallItems: Types.setWallItemSetState,
wallItems: Types.wallItems,
socket: Socket<any>,
projectId?: string,
versionId? : string,
): void {
////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage //////////
const { userId, organization, email } = getUserData();
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) {
setWallItems([]);
let WallItemsRef = wallItems;
const removedItem = WallItemsRef.find(
(item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid
);
const Items = WallItemsRef.filter(
(item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid
);
////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage //////////
setTimeout(async () => {
WallItemsRef = Items;
setWallItems(WallItemsRef);
if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) {
setWallItems([]);
let WallItemsRef = wallItems;
const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid);
const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid);
//REST
setTimeout(async () => {
WallItemsRef = Items;
setWallItems(WallItemsRef);
// await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelName!)
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const userId = localStorage.getItem("userId");
//SOCKET
//REST
const data = {
organization,
modelUuid: removedItem?.model?.uuid!,
modelName: removedItem?.modelName!,
socketId: socket.id,
projectId,
versionId,
userId,
};
// await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelName!)
socket.emit("v1:wallItems:delete", data);
//SOCKET
const WallItemsForStorage = WallItemsRef.map((item) => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
const data = {
organization: organization,
modelUuid: removedItem?.model?.uuid!,
modelName: removedItem?.modelName!,
socketId: socket.id,
projectId,
userId
}
socket.emit('v1:wallItems:delete', data);
const WallItemsForStorage = WallItemsRef.map(item => {
const { model, ...rest } = item;
return {
...rest,
modelUuid: model?.uuid,
};
});
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
hoveredDeletableWallItem.current = null;
}, 50);
}
localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
hoveredDeletableWallItem.current = null;
}, 50);
}
}
export default DeleteWallItems;

View File

@@ -17,6 +17,8 @@ import drawWall from "../geomentries/lines/drawWall";
import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor";
import addDragControl from "../eventDeclaration/dragControlDeclaration";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
const state = useThree();
@@ -29,11 +31,14 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
const { setNewLines } = useNewLines();
const { setDeletedLines } = useDeletedLines();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { organization } = getUserData();
useEffect(() => {
if (toolMode === 'move') {
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId);
addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId, selectedVersion?.versionId || '',);
}
return () => {
@@ -44,17 +49,11 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
}, [toolMode, state]);
useEffect(() => {
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
// Load data from localStorage if available
getLines(organization, projectId).then((data) => {
// console.log('data: ', data);
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
// const data = localStorage.getItem("Lines");
if (Lines) {
lines.current = Lines;
loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
@@ -62,7 +61,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
setUpdateScene(true);
}
})
}, []);
}, [selectedVersion?.versionId]);
useEffect(() => {
if (!toggleView) {
@@ -94,7 +93,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
useEffect(() => {
if (removedLayer !== null) {
DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket, projectId);
DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket, projectId, selectedVersion?.versionId || '',);
}
}, [removedLayer]);
@@ -140,19 +139,19 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
if (toolMode === "2D-Delete") {
if (hoveredDeletablePoint.current !== null) {
deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket, projectId);
deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',);
}
if (hoveredDeletableLine.current !== null) {
deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket, projectId);
deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',);
}
}
if (toolMode === "Wall") {
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId);
drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',);
}
if (toolMode === "Floor") {
drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId);
drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',);
}
}

View File

@@ -19,6 +19,8 @@ import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
import AddWallItems from "../geomentries/walls/addWallItems";
import useModuleStore from "../../../store/useModuleStore";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const WallItemsGroup = ({
currentWallItem,
@@ -37,12 +39,16 @@ const WallItemsGroup = ({
const { setSelectedWallItem } = useSelectedWallItem();
const { activeModule } = useModuleStore();
const { selectedItem } = useSelectedItem();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization } = getUserData();
useEffect(() => {
// Load Wall Items from the backend
loadInitialWallItems(setWallItems, projectId);
}, []);
if (!projectId || !selectedVersion) return;
loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId);
}, [selectedVersion?.versionId]);
////////// Update the Position value changes in the selected item //////////
@@ -120,9 +126,6 @@ const WallItemsGroup = ({
});
setTimeout(async () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const userId = localStorage.getItem("userId");
//REST
@@ -142,7 +145,7 @@ const WallItemsGroup = ({
//SOCKET
const data = {
organization: organization,
organization,
modelUuid: currentItem.model?.uuid!,
assetId: currentItem.assetId,
modelName: currentItem.modelName!,
@@ -153,6 +156,7 @@ const WallItemsGroup = ({
quaternion: currentItem.quaternion,
scale: currentItem.scale!,
socketId: socket.id,
versionId: selectedVersion?.versionId || '',
projectId,
userId
};
@@ -172,7 +176,7 @@ const WallItemsGroup = ({
canvasElement.removeEventListener("pointermove", handlePointerMove);
canvasElement.removeEventListener("pointerup", handlePointerUp);
};
}, [selectedItemsIndex]);
}, [selectedItemsIndex, selectedVersion?.versionId]);
useEffect(() => {
const canvasElement = state.gl.domElement;
@@ -194,7 +198,9 @@ const WallItemsGroup = ({
hoveredDeletableWallItem,
setWallItems,
wallItems,
socket, projectId
socket,
projectId,
selectedVersion?.versionId || '',
);
}
}
@@ -214,7 +220,7 @@ const WallItemsGroup = ({
raycaster.setFromCamera(pointer, camera);
if (selectedItem.id) {
if (selectedItem.id && selectedVersion && projectId) {
if (selectedItem.subCategory) {
AddWallItems(
selectedItem,
@@ -222,7 +228,8 @@ const WallItemsGroup = ({
CSGGroup,
setWallItems,
socket,
projectId
projectId,
selectedVersion?.versionId || '',
);
}
event.preventDefault();
@@ -246,7 +253,7 @@ const WallItemsGroup = ({
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [toolMode, wallItems, selectedItem, camera]);
}, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]);
useEffect(() => {
if (toolMode && activeModule === "builder") {

View File

@@ -10,18 +10,20 @@ import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLines
import loadWalls from "../geomentries/walls/loadWalls";
import texturePath from "../../../assets/textures/floor/wall-tex.png";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const WallsMeshComponent = ({ lines }: any) => {
const { walls, setWalls } = useWalls();
const { updateScene, setUpdateScene } = useUpdateScene();
const { projectId } = useParams();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { organization } = getUserData();
useEffect(() => {
if (updateScene) {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
getLines(organization,projectId).then((data) => {
getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
localStorage.setItem("Lines", JSON.stringify(Lines));
@@ -31,7 +33,7 @@ const WallsMeshComponent = ({ lines }: any) => {
});
setUpdateScene(false);
}
}, [updateScene]);
}, [updateScene, selectedVersion?.versionId]);
const textureLoader = new THREE.TextureLoader();
const wallTexture = textureLoader.load(texturePath);

View File

@@ -18,6 +18,8 @@ import * as turf from "@turf/turf";
import { computeArea } from "../functions/computeArea";
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData";
import { useVersionContext } from "../version/versionContext";
const ZoneGroup: React.FC = () => {
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
@@ -27,19 +29,17 @@ const ZoneGroup: React.FC = () => {
const { zonePoints, setZonePoints } = useZonePoints();
const [isDragging, setIsDragging] = useState(false);
const { selectedZone } = useSelectedZoneStore();
const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(
null
);
const plane = useMemo(
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
[]
);
const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(null);
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { removedLayer, setRemovedLayer } = useRemovedLayer();
const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { userId, organization, email } = getUserData();
const groupsRef = useRef<any>();
@@ -72,15 +72,8 @@ const ZoneGroup: React.FC = () => {
);
useEffect(() => {
const fetchZones = async () => {
const email = localStorage.getItem("email");
if (!email) return;
const organization = email.split("@")[1].split(".")[0];
const data = await getZonesApi(organization, projectId);
// console.log('data: ', data);
if (data.length > 0) {
getZonesApi(organization, projectId, selectedVersion?.versionId || '').then((data) => {
if (data && data.length > 0) {
const fetchedZones = data.map((zone: any) => ({
zoneUuid: zone.zoneUuid,
zoneName: zone.zoneName,
@@ -97,11 +90,13 @@ const ZoneGroup: React.FC = () => {
);
setZonePoints(fetchedPoints);
}else{
setZones([]);
}
};
fetchZones();
}, []);
}).catch((err)=>{
console.error(err);
})
}, [selectedVersion?.versionId]);
useEffect(() => {
localStorage.setItem("zones", JSON.stringify(zones));
@@ -142,9 +137,6 @@ const ZoneGroup: React.FC = () => {
points: [number, number, number][];
layer: string;
}) => {
const email = localStorage.getItem("email");
const userId = localStorage.getItem("userId");
const organization = email!.split("@")[1].split(".")[0];
const calculateCenter = (points: number[][]) => {
if (!points || points.length === 0) return null;
@@ -171,8 +163,9 @@ const ZoneGroup: React.FC = () => {
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization: organization,
organization,
zoneData: {
zoneName: zone.zoneName,
zoneUuid: zone.zoneUuid,
@@ -193,9 +186,6 @@ const ZoneGroup: React.FC = () => {
points: [number, number, number][];
layer: string;
}) => {
const email = localStorage.getItem("email");
const userId = localStorage.getItem("userId");
const organization = email!.split("@")[1].split(".")[0];
const calculateCenter = (points: number[][]) => {
if (!points || points.length === 0) return null;
@@ -222,8 +212,9 @@ const ZoneGroup: React.FC = () => {
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization: organization,
organization,
zoneData: {
zoneName: zone.zoneName,
zoneUuid: zone.zoneUuid,
@@ -238,14 +229,12 @@ const ZoneGroup: React.FC = () => {
};
const deleteZoneFromBackend = async (zoneUuid: string) => {
const email = localStorage.getItem("email");
const userId = localStorage.getItem("userId");
const organization = email!.split("@")[1].split(".")[0];
const input = {
userId: userId,
versionId: selectedVersion?.versionId || '',
projectId,
organization: organization,
organization,
zoneUuid: zoneUuid,
};
@@ -447,7 +436,7 @@ const ZoneGroup: React.FC = () => {
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContext);
};
}, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, zonePoints, draggedSphere, activeLayer, raycaster, pointer, controls, plane, setZones, setZonePoints, addZoneToBackend, handleDeleteZone, updateZoneToBackend,]);
}, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, zonePoints, draggedSphere, activeLayer, raycaster, pointer, controls, plane, setZones, setZonePoints, addZoneToBackend, handleDeleteZone, updateZoneToBackend, selectedVersion?.versionId]);
useFrame(() => {
if (!startPoint) return;

View File

@@ -46,9 +46,10 @@ function Line({ points }: Readonly<LineProps>) {
return (
<Tube
name={`${points[0].pointType}-Line`}
key={`${points[0].pointUuid}-${points[1].pointUuid}`}
uuid={`${points[0].pointUuid}-${points[1].pointUuid}`}
userData={points}
userData={{ points, path }}
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
>
<meshStandardMaterial color={colors.defaultLineColor} />

View File

@@ -1,74 +0,0 @@
import { useCallback } from 'react';
import * as THREE from 'three';
import { useAisleStore } from '../../../../store/builder/useAisleStore';
const SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters
const CAN_SNAP = true; // Whether snapping is enabled or not
export function useAislePointSnapping(point: Point) {
const { getConnectedPoints } = useAisleStore();
const snapPosition = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
if (!CAN_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const connectedPoints = getConnectedPoints(point.pointUuid);
if (connectedPoints.length === 0) {
return {
position: newPosition,
isSnapped: false,
snapSources: []
};
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
for (const connectedPoint of connectedPoints) {
const cPos = new THREE.Vector3(...connectedPoint.position);
const xDist = Math.abs(newPos.x - cPos.x);
const zDist = Math.abs(newPos.z - cPos.z);
if (xDist < SNAP_DISTANCE_THRESHOLD) {
if (!closestX || xDist < closestX.dist) {
closestX = { pos: cPos, dist: xDist };
}
}
if (zDist < SNAP_DISTANCE_THRESHOLD) {
if (!closestZ || zDist < closestZ.dist) {
closestZ = { pos: cPos, dist: zDist };
}
}
}
const snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = [];
if (closestX) {
snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone());
}
if (closestZ) {
snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone());
}
const isSnapped = snapSources.length > 0;
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
};
}, [point.pointUuid, getConnectedPoints]);
return { snapPosition };
}

View File

@@ -1,15 +1,107 @@
import { useCallback } from 'react';
import { useAisleStore } from '../../../../store/builder/useAisleStore';
import * as THREE from 'three';
import { useCallback } from 'react';
import { useWallStore } from '../../../../store/builder/useWallStore';
import { useSceneContext } from '../../../scene/sceneContext';
const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
const CAN_SNAP = true; // Whether snapping is enabled or not
const CAN_POINT_SNAP = true; // Whether snapping is enabled or not
const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters
const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
const { aisles } = useAisleStore();
const { walls } = useWallStore();
const { aisleStore } = useSceneContext();
const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
const { walls, getConnectedPoints: getConnectedWallPoints } = useWallStore();
// Wall Snapping
const getAllOtherWallPoints = useCallback(() => {
if (!currentPoint) return [];
return walls.flatMap(wall =>
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [walls, currentPoint]);
const snapWallPoint = useCallback((position: [number, number, number]) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherWallPoints();
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherWallPoints]);
const snapWallAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const connectedPoints = getConnectedWallPoints(currentPoint.uuid);
if (connectedPoints.length === 0) {
return {
position: newPosition,
isSnapped: false,
snapSources: []
};
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
for (const connectedPoint of connectedPoints) {
const cPos = new THREE.Vector3(...connectedPoint.position);
const xDist = Math.abs(newPos.x - cPos.x);
const zDist = Math.abs(newPos.z - cPos.z);
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestX || xDist < closestX.dist) {
closestX = { pos: cPos, dist: xDist };
}
}
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestZ || zDist < closestZ.dist) {
closestZ = { pos: cPos, dist: zDist };
}
}
}
const snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = [];
if (closestX) {
snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone());
}
if (closestZ) {
snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone());
}
const isSnapped = snapSources.length > 0;
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
};
}, [currentPoint, getConnectedAislePoints]);
// Aisle Snapping
const getAllOtherAislePoints = useCallback(() => {
if (!currentPoint) return [];
@@ -19,24 +111,15 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
);
}, [aisles, currentPoint]);
const getAllOtherWallPoints = useCallback(() => {
if (!currentPoint) return [];
return walls.flatMap(wall =>
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [walls, currentPoint]);
const checkSnapForAisle = useCallback((position: [number, number, number]) => {
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const snapAislePoint = useCallback((position: [number, number, number]) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherAislePoints();
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') {
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
@@ -44,23 +127,71 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherAislePoints]);
const checkSnapForWall = useCallback((position: [number, number, number]) => {
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const snapAisleAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const otherPoints = getAllOtherWallPoints();
const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
return { position: point.position, isSnapped: true, snappedPoint: point };
const connectedPoints = getConnectedAislePoints(currentPoint.uuid);
if (connectedPoints.length === 0) {
return {
position: newPosition,
isSnapped: false,
snapSources: []
};
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
for (const connectedPoint of connectedPoints) {
const cPos = new THREE.Vector3(...connectedPoint.position);
const xDist = Math.abs(newPos.x - cPos.x);
const zDist = Math.abs(newPos.z - cPos.z);
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestX || xDist < closestX.dist) {
closestX = { pos: cPos, dist: xDist };
}
}
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
if (!closestZ || zDist < closestZ.dist) {
closestZ = { pos: cPos, dist: zDist };
}
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherWallPoints]);
const snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = [];
if (closestX) {
snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone());
}
if (closestZ) {
snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone());
}
const isSnapped = snapSources.length > 0;
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
};
}, [currentPoint, getConnectedAislePoints]);
return {
checkSnapForAisle,
checkSnapForWall,
snapAislePoint,
snapAisleAngle,
snapWallPoint,
snapWallAngle,
};
};

View File

@@ -3,15 +3,15 @@ import * as Constants from '../../../types/world/worldConstants';
import { useRef, useState, useEffect, useMemo } from 'react';
import { useToolMode } from '../../../store/builder/store';
import { DragControls } from '@react-three/drei';
import { useAisleStore } from '../../../store/builder/useAisleStore';
import { useThree } from '@react-three/fiber';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { usePointSnapping } from './helpers/usePointSnapping';
import { useAislePointSnapping } from './helpers/useAisleDragSnap';
import { useWallStore } from '../../../store/builder/useWallStore';
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
import { useParams } from 'react-router-dom';
import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi';
import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext';
function Point({ point }: { readonly point: Point }) {
const materialRef = useRef<THREE.ShaderMaterial>(null);
@@ -19,11 +19,13 @@ function Point({ point }: { readonly point: Point }) {
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const [isHovered, setIsHovered] = useState(false);
const { toolMode } = useToolMode();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = useAisleStore();
const { aisleStore } = useSceneContext();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, removePoint: removeWallPoint } = useWallStore();
const { snapPosition } = useAislePointSnapping(point);
const { checkSnapForAisle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
const colors = getColor(point);
@@ -96,8 +98,8 @@ function Point({ point }: { readonly point: Point }) {
if (point.pointType === 'Aisle') {
if (position) {
const newPosition: [number, number, number] = [position.x, position.y, position.z];
const aisleSnappedPosition = snapPosition(newPosition);
const finalSnappedPosition = checkSnapForAisle(aisleSnappedPosition.position);
const aisleSnappedPosition = snapAisleAngle(newPosition);
const finalSnappedPosition = snapAislePoint(aisleSnappedPosition.position);
setAislePosition(point.pointUuid, finalSnappedPosition.position);
@@ -105,8 +107,10 @@ function Point({ point }: { readonly point: Point }) {
} else if (point.pointType === 'Wall') {
if (position) {
const newPosition: [number, number, number] = [position.x, position.y, position.z];
const wallSnappedPosition = snapWallAngle(newPosition);
const finalSnappedPosition = snapWallPoint(wallSnappedPosition.position);
setWallPosition(point.pointUuid, newPosition);
setWallPosition(point.pointUuid, finalSnappedPosition.position);
}
}
}
@@ -118,7 +122,7 @@ function Point({ point }: { readonly point: Point }) {
const updatedAisles = getAislesByPointId(point.pointUuid);
if (updatedAisles.length > 0 && projectId) {
updatedAisles.forEach((updatedAisle) => {
createAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId)
createAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
})
}
} else if (point.pointType === 'Wall') {
@@ -133,7 +137,7 @@ function Point({ point }: { readonly point: Point }) {
if (removedAisles.length > 0) {
removedAisles.forEach(aisle => {
if (projectId)
deleteAisleApi(aisle.aisleUuid, projectId)
deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '')
});
setHoveredPoint(null);
}
@@ -179,7 +183,7 @@ function Point({ point }: { readonly point: Point }) {
}
}}
onPointerOut={() => {
if (hoveredPoint && hoveredPoint.pointUuid === point.pointUuid) {
if (hoveredPoint) {
setHoveredPoint(null);
}
setIsHovered(false)

View File

@@ -0,0 +1,37 @@
import { createContext, useContext, useMemo } from 'react';
import { createSelectedVersionStore, SelectedVersionType } from '../../../store/simulation/useSimulationStore';
type VersionContextValue = {
selectedVersionStore: SelectedVersionType,
};
const VersionContext = createContext<VersionContextValue | null>(null);
export function VersionProvider({
children,
}: {
readonly children: React.ReactNode;
}) {
const selectedVersionStore = useMemo(() => createSelectedVersionStore(), []);
const contextValue = useMemo(() => (
{
selectedVersionStore
}
), [selectedVersionStore]);
return (
<VersionContext.Provider value={contextValue}>
{children}
</VersionContext.Provider>
);
}
// Base hook to get the context
export function useVersionContext() {
const context = useContext(VersionContext);
if (!context) {
throw new Error('useVersionContext must be used within a VersionProvider');
}
return context;
}

View File

@@ -2,218 +2,131 @@ import { useMemo } from 'react';
import * as turf from '@turf/turf';
export function useWallClassification(walls: Walls) {
// Find all minimal rooms from the given walls
const findRooms = () => {
if (walls.length < 3) return [];
// Build a graph of point connections
const graph = new Map<string, string[]>();
const pointMap = new Map<string, Point>();
// Map pointUuid to list of connected line segments
const pointMap = new Map<string, Wall[]>();
walls.forEach(wall => {
const [p1, p2] = wall.points;
pointMap.set(p1.pointUuid, p1);
pointMap.set(p2.pointUuid, p2);
for (const wall of walls) {
for (const point of wall.points) {
const list = pointMap.get(point.pointUuid) || [];
list.push(wall);
pointMap.set(point.pointUuid, list);
}
}
// Add connection from p1 to p2
if (!graph.has(p1.pointUuid)) graph.set(p1.pointUuid, []);
graph.get(p1.pointUuid)?.push(p2.pointUuid);
// Create graph of connected walls using pointUuid
const visited = new Set<string>();
const mergedLineStrings = [];
// Add connection from p2 to p1
if (!graph.has(p2.pointUuid)) graph.set(p2.pointUuid, []);
graph.get(p2.pointUuid)?.push(p1.pointUuid);
const wallKey = (p1: Point, p2: Point) => `${p1.pointUuid}-${p2.pointUuid}`;
for (const wall of walls) {
const key = wallKey(wall.points[0], wall.points[1]);
if (visited.has(key) || visited.has(wallKey(wall.points[1], wall.points[0]))) continue;
let line: Point[] = [...wall.points];
visited.add(key);
// Try to extend the line forward and backward by matching endpoints
let extended = true;
while (extended) {
extended = false;
const last = line[line.length - 1];
const nextWalls = pointMap.get(last.pointUuid) || [];
for (const next of nextWalls) {
const [n1, n2] = next.points;
const nextKey = wallKey(n1, n2);
if (visited.has(nextKey) || visited.has(wallKey(n2, n1))) continue;
if (n1.pointUuid === last.pointUuid && n2.pointUuid !== line[line.length - 2]?.pointUuid) {
line.push(n2);
visited.add(nextKey);
extended = true;
break;
} else if (n2.pointUuid === last.pointUuid && n1.pointUuid !== line[line.length - 2]?.pointUuid) {
line.push(n1);
visited.add(nextKey);
extended = true;
break;
}
}
const first = line[0];
const prevWalls = pointMap.get(first.pointUuid) || [];
for (const prev of prevWalls) {
const [p1, p2] = prev.points;
const prevKey = wallKey(p1, p2);
if (visited.has(prevKey) || visited.has(wallKey(p2, p1))) continue;
if (p1.pointUuid === first.pointUuid && p2.pointUuid !== line[1]?.pointUuid) {
line.unshift(p2);
visited.add(prevKey);
extended = true;
break;
} else if (p2.pointUuid === first.pointUuid && p1.pointUuid !== line[1]?.pointUuid) {
line.unshift(p1);
visited.add(prevKey);
extended = true;
break;
}
}
}
// Create merged LineString
const coords = line.map(p => [p.position[0], p.position[2]]);
mergedLineStrings.push(turf.lineString(coords, {
pointUuids: line.map(p => p.pointUuid)
}));
}
const lineStrings = turf.featureCollection(mergedLineStrings);
// Now polygonize merged line strings
const polygons = turf.polygonize(lineStrings);
const rooms: Point[][] = [];
polygons.features.forEach(feature => {
if (feature.geometry.type === 'Polygon') {
const coordinates = feature.geometry.coordinates[0];
const roomPoints: Point[] = [];
for (const [x, z] of coordinates) {
const matchingPoint = walls.flatMap(wall => wall.points)
.find(p =>
p.position[0].toFixed(10) === x.toFixed(10) &&
p.position[2].toFixed(10) === z.toFixed(10)
);
if (matchingPoint) {
roomPoints.push(matchingPoint);
}
}
if (roomPoints.length > 0 &&
roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
roomPoints.push(roomPoints[0]);
}
if (roomPoints.length >= 4) {
rooms.push(roomPoints);
}
}
});
// Find all minimal cycles (rooms) in the graph
const allCycles: string[][] = [];
const findCycles = (startNode: string, currentNode: string, path: string[], depth = 0) => {
if (depth > 20) return; // Prevent infinite recursion
path.push(currentNode);
const neighbors = graph.get(currentNode) || [];
for (const neighbor of neighbors) {
if (path.length > 2 && neighbor === startNode) {
// Found a cycle that returns to start
const cycle = [...path, neighbor];
// Check if this is a new unique cycle
if (!cycleExists(allCycles, cycle)) {
allCycles.push(cycle);
}
continue;
}
if (!path.includes(neighbor)) {
findCycles(startNode, neighbor, [...path], depth + 1);
}
}
};
// Start cycle detection from each node
for (const [pointUuid] of graph) {
findCycles(pointUuid, pointUuid, []);
}
// Convert cycles to Point arrays and validate them
const potentialRooms = allCycles
.map(cycle => cycle.map(uuid => pointMap.get(uuid)!))
.filter(room => isValidRoom(room));
const uniqueRooms = removeDuplicateRooms(potentialRooms);
// ✅ New logic that only removes redundant supersets
const filteredRooms = removeSupersetLikeRooms(uniqueRooms, walls);
return filteredRooms;
console.log('rooms: ', rooms);
return rooms;
};
const removeSupersetLikeRooms = (rooms: Point[][], walls: Wall[]): Point[][] => {
const toRemove = new Set<number>();
const getPolygon = (points: Point[]) =>
turf.polygon([points.map(p => [p.position[0], p.position[2]])]);
const getWallSet = (room: Point[]) => {
const set = new Set<string>();
for (let i = 0; i < room.length - 1; i++) {
const p1 = room[i].pointUuid;
const p2 = room[i + 1].pointUuid;
const wall = walls.find(w =>
(w.points[0].pointUuid === p1 && w.points[1].pointUuid === p2) ||
(w.points[0].pointUuid === p2 && w.points[1].pointUuid === p1)
);
if (wall) {
set.add(wall.wallUuid);
}
}
return set;
};
const roomPolygons = rooms.map(getPolygon);
const roomAreas = roomPolygons.map(poly => turf.area(poly));
const wallSets = rooms.map(getWallSet);
// First, identify all rooms that are completely contained within others
for (let i = 0; i < rooms.length; i++) {
for (let j = 0; j < rooms.length; j++) {
if (i === j) continue;
// If room i completely contains room j
if (turf.booleanContains(roomPolygons[i], roomPolygons[j])) {
// Check if the contained room shares most of its walls with the containing room
const sharedWalls = [...wallSets[j]].filter(w => wallSets[i].has(w));
const shareRatio = sharedWalls.length / wallSets[j].size;
// If they share more than 50% of walls, mark the larger one for removal
// UNLESS the smaller one is significantly smaller (likely a real room)
if (shareRatio > 0.5 && (roomAreas[i] / roomAreas[j] > 2)) {
toRemove.add(i);
}
}
}
}
// Second pass: handle cases where a room is divided by segmented walls
for (let i = 0; i < rooms.length; i++) {
if (toRemove.has(i)) continue;
for (let j = 0; j < rooms.length; j++) {
if (i === j || toRemove.has(j)) continue;
// Check if these rooms share a significant portion of walls
const sharedWalls = [...wallSets[i]].filter(w => wallSets[j].has(w));
const shareRatio = Math.max(
sharedWalls.length / wallSets[i].size,
sharedWalls.length / wallSets[j].size
);
// If they share more than 30% of walls and one is much larger
if (shareRatio > 0.3) {
const areaRatio = roomAreas[i] / roomAreas[j];
if (areaRatio > 2) {
// The larger room is likely the undivided version
toRemove.add(i);
} else if (areaRatio < 0.5) {
// The smaller room might be an artifact
toRemove.add(j);
}
}
}
}
return rooms.filter((_, idx) => !toRemove.has(idx));
};
// Check if a cycle already exists in our list (considering different orders)
const cycleExists = (allCycles: string[][], newCycle: string[]) => {
const newSet = new Set(newCycle);
return allCycles.some(existingCycle => {
if (existingCycle.length !== newCycle.length) return false;
const existingSet = new Set(existingCycle);
return setsEqual(newSet, existingSet);
});
};
// Check if two sets are equal
const setsEqual = <T,>(a: Set<T>, b: Set<T>) => {
if (a.size !== b.size) return false;
for (const item of a) if (!b.has(item)) return false;
return true;
};
// Remove duplicate rooms (same set of points in different orders)
const removeDuplicateRooms = (rooms: Point[][]) => {
const uniqueRooms: Point[][] = [];
const roomHashes = new Set<string>();
for (const room of rooms) {
// Create a consistent hash for the room regardless of point order
const hash = room
.map(p => p.pointUuid)
.sort()
.join('-');
if (!roomHashes.has(hash)) {
roomHashes.add(hash);
uniqueRooms.push(room);
}
}
return uniqueRooms;
};
// Check if a room is valid (closed, non-self-intersecting polygon)
const isValidRoom = (points: Point[]): boolean => {
// Must have at least 4 points (first and last are same)
if (points.length < 4) return false;
// Must be a closed loop
if (points[0].pointUuid !== points[points.length - 1].pointUuid) {
return false;
}
try {
const coordinates = points.map(p => [p.position[0], p.position[2]]);
const polygon = turf.polygon([coordinates]);
return turf.booleanValid(polygon);
} catch (e) {
return false;
}
};
// Rest of the implementation remains the same...
const rooms = useMemo(() => findRooms(), [walls]);
// Determine wall orientation relative to room
const findWallType = (wall: Wall) => {
// Find all rooms that contain this wall
const containingRooms = rooms.filter(room => {
for (let i = 0; i < room.length - 1; i++) {
const p1 = room[i];
@@ -244,11 +157,7 @@ export function useWallClassification(walls: Walls) {
}
};
// Update the other functions to use the new return type
const getWallType = (wall: Wall): {
type: string;
rooms: Point[][];
} => {
const getWallType = (wall: Wall) => {
return findWallType(wall);
};
@@ -261,11 +170,32 @@ export function useWallClassification(walls: Walls) {
return findWallType(wall).type === 'segment';
};
const isWallFlipped = (wall: Wall): boolean => {
const wallType = findWallType(wall);
if (wallType.type === 'segment') return false;
for (const room of wallType.rooms) {
for (let i = 0; i < room.length - 1; i++) {
const p1 = room[i];
const p2 = room[i + 1];
if (wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) {
return false;
}
if (wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid) {
return true;
}
}
}
return false;
};
return {
rooms,
getWallType,
isRoomWall,
isSegmentWall,
findRooms,
isWallFlipped
};
}

View File

@@ -1,64 +0,0 @@
import * as THREE from 'three';
import { useMemo } from 'react';
function useWallGeometry(wallLength: number, wallHeight: number, wallThickness: number) {
return useMemo(() => {
const geometry = new THREE.BufferGeometry();
const halfLength = wallLength / 2;
const halfThickness = wallThickness / 2;
const height = wallHeight;
const vertices = [
-halfLength, -height / 2, halfThickness,
-halfLength, height / 2, halfThickness,
halfLength, height / 2, halfThickness,
halfLength, -height / 2, halfThickness,
-halfLength, -height / 2, -halfThickness,
-halfLength, height / 2, -halfThickness,
halfLength, height / 2, -halfThickness,
halfLength, -height / 2, -halfThickness,
-halfLength, height / 2, halfThickness,
-halfLength, height / 2, -halfThickness,
halfLength, height / 2, -halfThickness,
halfLength, height / 2, halfThickness,
-halfLength, -height / 2, halfThickness,
-halfLength, -height / 2, -halfThickness,
halfLength, -height / 2, -halfThickness,
halfLength, -height / 2, halfThickness,
];
const indices = [
0, 1, 2, 0, 2, 3,
4, 6, 5, 4, 7, 6,
0, 4, 5, 0, 5, 1,
3, 2, 6, 3, 6, 7,
8, 9, 10, 8, 10, 11,
12, 14, 13, 12, 15, 14
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute([
0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0
], 2));
geometry.computeVertexNormals();
geometry.addGroup(0, 6, 0); // Front
geometry.addGroup(6, 6, 1); // Back
geometry.addGroup(12, 6, 2); // Left
geometry.addGroup(18, 6, 3); // Right
geometry.addGroup(24, 6, 4); // Top
geometry.addGroup(30, 6, 5); // Bottom
return geometry;
}, [wallLength, wallHeight, wallThickness]);
}
export default useWallGeometry;

View File

@@ -2,26 +2,29 @@ import * as THREE from 'three';
import { useMemo, useRef, useState } from 'react';
import * as Constants from '../../../../../types/world/worldConstants';
import insideMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import outsideMaterial from '../../../../../assets/textures/floor/factory wall texture.jpg';
import useWallGeometry from './helpers/useWallGeometry';
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
import { useWallStore } from '../../../../../store/builder/useWallStore';
import { useWallClassification } from './helpers/useWallClassification';
import { useFrame, useThree } from '@react-three/fiber';
import { useWallVisibility } from '../../../../../store/builder/store';
import { Decal } from '@react-three/drei';
import { Base } from '@react-three/csg';
import { Decal, PivotControls } from '@react-three/drei';
import { Base, Geometry, Subtraction } from '@react-three/csg';
function Wall({ wall }: { readonly wall: Wall }) {
const { walls } = useWallStore();
const { getWallType } = useWallClassification(walls);
const { getWallType, isWallFlipped } = useWallClassification(walls);
const wallType = getWallType(wall);
const [startPoint, endPoint] = wall.points;
const [visible, setVisible] = useState(true);
const { wallVisibility } = useWallVisibility();
const meshRef = useRef<any>();
const { camera } = useThree();
const wallFlipped = isWallFlipped(wall);
const [rawStart, rawEnd] = wall.points;
const [startPoint, endPoint] = wallFlipped ? [rawStart, rawEnd] : [rawEnd, rawStart];
const startX = startPoint.position[0];
const startZ = startPoint.position[2];
const endX = endPoint.position[0];
@@ -36,13 +39,13 @@ function Wall({ wall }: { readonly wall: Wall }) {
const textureLoader = new THREE.TextureLoader();
const [insideWallTexture, outsideWallTexture] = useMemo(() => {
const inside = textureLoader.load(insideMaterial);
const [defaultWallTexture, material1WallTexture] = useMemo(() => {
const inside = textureLoader.load(defaultMaterial);
inside.wrapS = inside.wrapT = THREE.RepeatWrapping;
inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
inside.colorSpace = THREE.SRGBColorSpace;
const outside = textureLoader.load(outsideMaterial);
const outside = textureLoader.load(material1);
outside.wrapS = outside.wrapT = THREE.RepeatWrapping;
outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
outside.colorSpace = THREE.SRGBColorSpace;
@@ -51,29 +54,25 @@ function Wall({ wall }: { readonly wall: Wall }) {
}, [wallLength, wall.wallHeight]);
const materials = useMemo(() => {
const frontMaterial = insideWallTexture;
const backMaterial = outsideWallTexture;
return [
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Left
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Right
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Top
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Bottom
new THREE.MeshStandardMaterial({
color: Constants.wallConfig.defaultColor,
side: THREE.DoubleSide,
map: frontMaterial
map: wall.insideMaterial === 'Default Material' ? defaultWallTexture : material1WallTexture,
}),
new THREE.MeshStandardMaterial({
color: Constants.wallConfig.defaultColor,
side: THREE.DoubleSide,
map: backMaterial
map: wall.outsideMaterial === 'Default Material`' ? defaultWallTexture : material1WallTexture,
}),
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Left
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Right
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }), // Top
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }) // Bottom
];
}, [insideWallTexture, outsideWallTexture, wall]);
}, [defaultWallTexture, material1WallTexture, wall]);
const geometry = useWallGeometry(wallLength, wall.wallHeight, wall.wallThickness);
const geometry = useMemo(() => new THREE.BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
useFrame(() => {
if (!meshRef.current) return;
@@ -83,6 +82,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
if (!wallVisibility && wallType.type === 'room') {
meshRef.current.getWorldDirection(v);
camera.getWorldDirection(u);
if (!u || !v) return;
setVisible((2 * v.dot(u)) <= 0.1);
} else {
@@ -91,16 +91,19 @@ function Wall({ wall }: { readonly wall: Wall }) {
})
return (
<group
<mesh
name={`Wall-${wall.wallUuid}`}
key={wall.wallUuid}
userData={wall}
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
visible={visible}
>
<Base ref={meshRef} geometry={geometry} visible>
<Base
ref={meshRef}
geometry={geometry}
position={[centerX, centerY, centerZ]}
rotation={[0, -angle, 0]}
>
{materials.map((material, index) => (
<primitive key={index} object={material} attach={`material-${index}`} />
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
))}
{wall.decals.map((decal) => {
@@ -112,7 +115,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
scale={[decal.decalScale, decal.decalScale, 0.001]}
>
<meshBasicMaterial
map={outsideWallTexture}
map={material1WallTexture}
side={THREE.DoubleSide}
polygonOffset
polygonOffsetFactor={-1}
@@ -121,7 +124,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
)
})}
</Base>
</group>
</mesh>
);
}

View File

@@ -4,13 +4,11 @@ import WallInstance from './instance/wallInstance';
import Line from '../../line/line';
import Point from '../../point/point';
import { useToggleView } from '../../../../store/builder/store';
import { Base, Geometry, Subtraction } from '@react-three/csg';
import { BoxGeometry } from 'three';
import { Geometry } from '@react-three/csg';
function WallInstances() {
const { walls } = useWallStore();
const { toggleView } = useToggleView();
const ref = useRef<any>();
useEffect(() => {
// console.log('walls: ', walls);
@@ -34,26 +32,25 @@ function WallInstances() {
return (
<>
<group name='Walls-Group' ref={ref}>
<Geometry computeVertexNormals>
{walls.map((wall) => (
<WallInstance key={wall.wallUuid} wall={wall} />
))}
{!toggleView && (
{/* <Base geometry={new BoxGeometry()} >
<meshStandardMaterial />
</Base>
<mesh name='Walls-Group'>
{/* <Base name="base" geometry={box} scale={[3, 3, 3]} /> */}
<Geometry>
<Subtraction scale={[5, 11, 5]} >
<Geometry>
<Base geometry={new BoxGeometry()} />
</Geometry>
</Subtraction>
</Geometry> */}
</Geometry>
</group>
<Geometry useGroups>
{walls.map((wall) => (
<WallInstance key={wall.wallUuid} wall={wall} />
))}
</Geometry>
{/* <Subtraction rotation={[0, Math.PI / 2, 0]} position={[-1.425, -0.45, 0]} scale={[1, 3, 1]}>
<Geometry>
<Base geometry={box} />
</Geometry>
</Subtraction> */}
</mesh>
)}
{toggleView && (
<>
@@ -76,4 +73,4 @@ function WallInstances() {
)
}
export default WallInstances
export default WallInstances

View File

@@ -26,7 +26,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
const { checkSnapForWall } = usePointSnapping({ uuid: 'temp-wall', pointType: 'Wall', position: directionalSnap.position || [0, 0, 0] });
const { snapWallPoint } = usePointSnapping({ uuid: 'temp-wall', pointType: 'Wall', position: directionalSnap.position || [0, 0, 0] });
useFrame(() => {
if (toolMode === 'Wall' && toggleView && tempPoints.length === 1) {
@@ -37,7 +37,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (intersectionPoint) {
const snapped = checkSnapForWall([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (snapped.isSnapped && snapped.snappedPoint) {
finalPosition.current = snapped.position;
@@ -68,8 +68,8 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
setTempWall({
wallUuid: 'temp-wall',
points: wallPoints,
outSideMaterial: 'default',
inSideMaterial: 'default',
outsideMaterial: 'default',
insideMaterial: 'default',
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []

View File

@@ -2,10 +2,12 @@ import * as THREE from 'three'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import * as Constants from '../../../../types/world/worldConstants';
import { useWallStore } from '../../../../store/builder/useWallStore';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceWall from './referenceWall';
import getClosestIntersection from '../../geomentries/lines/getClosestIntersection';
function WallCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree();
@@ -14,10 +16,10 @@ function WallCreator() {
const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { addWall, getWallPointById } = useWallStore();
const { addWall, getWallPointById, removeWall, getWallByPoints } = useWallStore();
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const { wallThickness, wallHeight, snappedPosition, snappedPoint } = useBuilderStore();
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore();
const [tempPoints, setTempPoints] = useState<Point[]>([]);
const [isCreating, setIsCreating] = useState(false);
@@ -52,7 +54,118 @@ function WallCreator() {
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (!position) return;
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Point');
const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Point');
const wallIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Line');
if (wallIntersect && !pointIntersects) {
const wall = getWallByPoints(wallIntersect.object.userData.points);
if (wall) {
const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
const point1Vec = new THREE.Vector3(...wall.points[0].position);
const point2Vec = new THREE.Vector3(...wall.points[1].position);
const lineDir = new THREE.Vector3().subVectors(point2Vec, point1Vec).normalize();
const point1ToIntersect = new THREE.Vector3().subVectors(intersectionPoint, point1Vec);
const dotProduct = point1ToIntersect.dot(lineDir);
const projection = new THREE.Vector3().copy(lineDir).multiplyScalar(dotProduct).add(point1Vec);
const lineLength = point1Vec.distanceTo(point2Vec);
let t = point1Vec.distanceTo(projection) / lineLength;
t = Math.max(0, Math.min(1, t));
const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t);
removeWall(wall.wallUuid);
const point1: Point = {
pointUuid: wall.points[0].pointUuid,
pointType: 'Wall',
position: wall.points[0].position,
layer: wall.points[0].layer
};
const point2: Point = {
pointUuid: wall.points[1].pointUuid,
pointType: 'Wall',
position: wall.points[1].position,
layer: wall.points[1].layer
};
const newPoint: Point = {
pointUuid: THREE.MathUtils.generateUUID(),
pointType: 'Wall',
position: closestPoint.toArray(),
layer: activeLayer
};
if (tempPoints.length === 0) {
const wall2: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [point1, newPoint],
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
}
addWall(wall2);
const wall3: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [point2, newPoint],
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
}
addWall(wall3);
setTempPoints([newPoint]);
setIsCreating(true);
} else {
const wall1: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
};
addWall(wall1);
const wall2: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [point1, newPoint],
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
}
addWall(wall2);
const wall3: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [point2, newPoint],
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
}
addWall(wall3);
setTempPoints([newPoint]);
}
return;
}
}
const newPoint: Point = {
pointUuid: THREE.MathUtils.generateUUID(),
@@ -71,8 +184,8 @@ function WallCreator() {
newPoint.position = snappedPosition;
}
if (intersects && !snappedPoint) {
const point = getWallPointById(intersects.object.uuid);
if (pointIntersects && !snappedPoint) {
const point = getWallPointById(pointIntersects.object.uuid);
if (point) {
newPoint.pointUuid = point.pointUuid;
newPoint.position = point.position;
@@ -87,8 +200,8 @@ function WallCreator() {
const wall: Wall = {
wallUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
outSideMaterial: 'default',
inSideMaterial: 'default',
outsideMaterial: insideMaterial,
insideMaterial: outsideMaterial,
wallThickness: wallThickness,
wallHeight: wallHeight,
decals: []
@@ -114,10 +227,8 @@ function WallCreator() {
canvasElement.addEventListener("click", onMouseClick);
canvasElement.addEventListener("contextmenu", onContext);
} else {
if (tempPoints.length > 0 || isCreating) {
setTempPoints([]);
setIsCreating(false);
}
setTempPoints([]);
setIsCreating(false);
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);