Refactor aisle API services: remove createAisleApi, update deleteAisleApi to include versionId, and modify wall asset APIs for consistency in endpoint naming. Enhance floor and wall store functionality with new setters and methods for managing floors and walls, including decal handling. Introduce FloorInstance and FloorInstances components for rendering floor data, and implement FloorCreator for interactive floor creation. Add reference floor visualization with snapping capabilities. Update wall API services for improved error handling and response management.
This commit is contained in:
@@ -6,10 +6,20 @@ import wallTexture1 from '../../../../assets/textures/floor/factory wall texture
|
||||
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useSocketStore } from "../../../../store/builder/store";
|
||||
|
||||
const SelectedWallProperties = () => {
|
||||
const { selectedWall } = useBuilderStore();
|
||||
const { wallStore } = useSceneContext();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { getWallById, updateWall } = wallStore();
|
||||
|
||||
const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1");
|
||||
@@ -24,14 +34,50 @@ const SelectedWallProperties = () => {
|
||||
const handleHeightChange = (val: string) => {
|
||||
const height = parseFloat(val);
|
||||
if (!isNaN(height) && wall) {
|
||||
updateWall(wall.wallUuid, { wallHeight: height });
|
||||
const updatedWall = updateWall(wall.wallUuid, { wallHeight: height });
|
||||
if (updatedWall && projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleThicknessChange = (val: string) => {
|
||||
const thickness = parseFloat(val);
|
||||
if (!isNaN(thickness) && wall) {
|
||||
updateWall(wall.wallUuid, { wallThickness: thickness });
|
||||
const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness });
|
||||
if (updatedWall && projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -39,8 +85,25 @@ const SelectedWallProperties = () => {
|
||||
if (!wall) return;
|
||||
|
||||
const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId })
|
||||
const updatedWall = updateWall(wall.wallUuid, updated);
|
||||
if (updatedWall && projectId) {
|
||||
|
||||
updateWall(wall.wallUuid, updated);
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
if (!wall) return null;
|
||||
|
||||
@@ -2,13 +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 ReferenceAisle from './referenceAisle';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import { createAisleApi } from '../../../../services/factoryBuilder/aisle/createAisleApi';
|
||||
import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import ReferenceAisle from './referenceAisle';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
|
||||
function AisleCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
@@ -74,6 +74,8 @@ function AisleCreator() {
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return }
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
@@ -104,7 +106,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -127,7 +129,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -149,7 +151,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -170,7 +172,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -193,7 +195,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -215,7 +217,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -236,7 +238,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -258,7 +260,7 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import * as Types from "../../types/world/worldTypes";
|
||||
|
||||
import SocketResponses from "../collaboration/socket/socketResponses.dev";
|
||||
import FloorPlanGroup from "./groups/floorPlanGroup";
|
||||
import FloorGroup from "./groups/floorGroup";
|
||||
// import FloorGroup from "./groups/floorGroup";
|
||||
import Draw from "./functions/draw";
|
||||
import WallsAndWallItems from "./groups/wallsAndWallItems";
|
||||
import Ground from "../scene/environment/ground";
|
||||
@@ -52,6 +52,7 @@ import AislesGroup from "./aisle/aislesGroup";
|
||||
import WallGroup from "./wall/wallGroup";
|
||||
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import FloorGroup from "./floor/floorGroup";
|
||||
|
||||
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.
|
||||
@@ -185,7 +186,7 @@ export default function Builder() {
|
||||
<Ground grid={grid} plane={plane} />
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
<DistanceText key={toggleView} />
|
||||
{/* <DistanceText key={toggleView} /> */}
|
||||
</Bvh>
|
||||
|
||||
<ReferenceDistanceText
|
||||
@@ -207,7 +208,7 @@ export default function Builder() {
|
||||
/>
|
||||
</Bvh>
|
||||
|
||||
<WallsAndWallItems
|
||||
{/* <WallsAndWallItems
|
||||
CSGGroup={CSGGroup}
|
||||
setSelectedItemsIndex={setSelectedItemsIndex}
|
||||
selectedItemsIndex={selectedItemsIndex}
|
||||
@@ -215,16 +216,16 @@ export default function Builder() {
|
||||
csg={csg}
|
||||
lines={lines}
|
||||
hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
|
||||
<FloorGroup
|
||||
{/* <FloorGroup
|
||||
floorGroup={floorGroup}
|
||||
lines={lines}
|
||||
referencePole={referencePole}
|
||||
hoveredDeletablePillar={hoveredDeletablePillar}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<FloorPlanGroup
|
||||
floorPlanGroup={floorPlanGroup}
|
||||
@@ -276,7 +277,9 @@ export default function Builder() {
|
||||
<LayoutImage />
|
||||
</Bvh>
|
||||
|
||||
{/* <WallGroup /> */}
|
||||
<WallGroup />
|
||||
|
||||
<FloorGroup />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
|
||||
function FloorInstance({ floor }: { floor: Floor }) {
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorInstance
|
||||
121
app/src/modules/builder/floor/Instances/floorInstances.tsx
Normal file
121
app/src/modules/builder/floor/Instances/floorInstances.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { Vector3 } from 'three';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
import FloorInstance from './Instance/floorInstance';
|
||||
|
||||
function FloorInstances() {
|
||||
const { floorStore } = useSceneContext();
|
||||
const { floors } = floorStore();
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('floors: ', floors);
|
||||
}, [floors])
|
||||
|
||||
const allPoints = useMemo(() => {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
floors.forEach(floor => {
|
||||
floor.points.forEach(point => {
|
||||
if (!seenUuids.has(point.pointUuid)) {
|
||||
seenUuids.add(point.pointUuid);
|
||||
points.push(point);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return points;
|
||||
}, [floors]);
|
||||
|
||||
const allLines = useMemo(() => {
|
||||
const lines: { start: Point; end: Point; key: string }[] = [];
|
||||
|
||||
floors.forEach((floor) => {
|
||||
const points = floor.points;
|
||||
if (points.length < 2) return;
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const current = points[i];
|
||||
const next = points[(i + 1) % points.length];
|
||||
if (current.pointUuid !== next.pointUuid) {
|
||||
lines.push({
|
||||
start: current,
|
||||
end: next,
|
||||
key: `${current.pointUuid}-${next.pointUuid}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return lines;
|
||||
}, [floors]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
{!toggleView && floors.length > 1 && (
|
||||
<mesh name='Floors-Group'>
|
||||
{floors.map((floor) => (
|
||||
<FloorInstance key={floor.floorUuid} floor={floor} />
|
||||
))}
|
||||
</mesh>
|
||||
)}
|
||||
|
||||
{toggleView && (
|
||||
<>
|
||||
<group name='Floor-Points-Group'>
|
||||
{allPoints.map((point) => (
|
||||
<Point key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
<group name='Floor-Lines-Group'>
|
||||
|
||||
{allLines.map(({ start, end, key }) => (
|
||||
<Line key={key} points={[start, end]} />
|
||||
))}
|
||||
|
||||
{allLines.map((line) => {
|
||||
const { start, end, key } = line;
|
||||
const textPosition = new Vector3().addVectors(new Vector3(...start.position), new Vector3(...end.position)).divideScalar(2);
|
||||
const distance = new Vector3(...start.position).distanceTo(new Vector3(...end.position));
|
||||
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{toggleView &&
|
||||
<Html
|
||||
key={`${start.pointUuid}_${end.pointUuid}`}
|
||||
userData={line}
|
||||
position={[textPosition.x, 1, textPosition.z]}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
key={key}
|
||||
className={`distance ${key}`}
|
||||
>
|
||||
{distance.toFixed(2)} m
|
||||
</div>
|
||||
</Html>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
|
||||
</group>
|
||||
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorInstances;
|
||||
175
app/src/modules/builder/floor/floorCreator/floorCreator.tsx
Normal file
175
app/src/modules/builder/floor/floorCreator/floorCreator.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
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 { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import ReferenceFloor from './referenceFloor';
|
||||
|
||||
function FloorCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { floorStore } = useSceneContext();
|
||||
const { addFloor, removeDecal, getFloorPointById, getFloorByPoints } = floorStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const { floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = true;
|
||||
drag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseClick = () => {
|
||||
if (drag.current || !toggleView) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (!position) return;
|
||||
|
||||
const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Point');
|
||||
|
||||
const floorIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Line');
|
||||
|
||||
if (floorIntersect && !pointIntersects) {
|
||||
|
||||
}
|
||||
|
||||
const newPoint: Point = {
|
||||
pointUuid: THREE.MathUtils.generateUUID(),
|
||||
pointType: 'Floor',
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
};
|
||||
|
||||
if (snappedPosition && snappedPoint) {
|
||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||
newPoint.position = snappedPosition;
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return }
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
|
||||
if (pointIntersects && !snappedPoint) {
|
||||
if (tempPoints.length > 2 && isCreating && pointIntersects.object.userData.pointUuid === tempPoints[0].pointUuid) {
|
||||
if (tempPoints.length >= 3) {
|
||||
const floor: Floor = {
|
||||
floorUuid: THREE.MathUtils.generateUUID(),
|
||||
points: tempPoints,
|
||||
topMaterial,
|
||||
sideMaterial,
|
||||
floorDepth,
|
||||
isBeveled,
|
||||
bevelStrength,
|
||||
decals: [],
|
||||
};
|
||||
|
||||
addFloor(floor);
|
||||
}
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
}
|
||||
|
||||
setTempPoints(prev => [...prev, newPoint]);
|
||||
setIsCreating(true);
|
||||
|
||||
};
|
||||
|
||||
const onContext = (event: any) => {
|
||||
event.preventDefault();
|
||||
if (isCreating) {
|
||||
if (tempPoints.length >= 3) {
|
||||
const floor: Floor = {
|
||||
floorUuid: THREE.MathUtils.generateUUID(),
|
||||
points: tempPoints,
|
||||
topMaterial,
|
||||
sideMaterial,
|
||||
floorDepth,
|
||||
isBeveled,
|
||||
bevelStrength,
|
||||
decals: [],
|
||||
};
|
||||
|
||||
addFloor(floor);
|
||||
}
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (toolMode === "Floor" && toggleView) {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("click", onMouseClick);
|
||||
canvasElement.addEventListener("contextmenu", onContext);
|
||||
} else {
|
||||
setTempPoints([]);
|
||||
setIsCreating(false);
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, removeDecal, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView &&
|
||||
<>
|
||||
<group name='Wall-Reference-Points-Group'>
|
||||
{tempPoints.map((point) => (
|
||||
<ReferencePoint key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
{tempPoints.length > 0 &&
|
||||
<ReferenceFloor tempPoints={tempPoints} />
|
||||
}
|
||||
</>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorCreator
|
||||
164
app/src/modules/builder/floor/floorCreator/referenceFloor.tsx
Normal file
164
app/src/modules/builder/floor/floorCreator/referenceFloor.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import { useEffect, useRef, useState, useMemo } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { Extrude, Html } from '@react-three/drei';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||
import ReferenceLine from '../../line/reference/referenceLine';
|
||||
|
||||
interface ReferenceFloorProps {
|
||||
tempPoints: Point[];
|
||||
}
|
||||
|
||||
function ReferenceFloor({ tempPoints }: Readonly<ReferenceFloorProps>) {
|
||||
const { floorDepth, isBeveled, bevelStrength, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
const { pointer, raycaster, camera } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
const finalPosition = useRef<[number, number, number] | null>(null);
|
||||
|
||||
const [tempFloor, setTempFloor] = useState<Floor | null>(null);
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[tempPoints.length - 1]?.position || null);
|
||||
const { snapFloorPoint } = usePointSnapping({ uuid: 'temp-floor', pointType: 'Floor', position: directionalSnap.position || [0, 0, 0], });
|
||||
|
||||
useFrame(() => {
|
||||
if (toolMode === 'Floor' && toggleView && tempPoints.length > 0) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (!intersectionPoint) return;
|
||||
const snapped = snapFloorPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.slice(0, -2));
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
setSnappedPosition(snapped.position);
|
||||
setSnappedPoint(snapped.snappedPoint);
|
||||
} else if (directionalSnap.isSnapped) {
|
||||
finalPosition.current = directionalSnap.position;
|
||||
setSnappedPosition(directionalSnap.position);
|
||||
setSnappedPoint(null);
|
||||
} else {
|
||||
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
const floorPoints: Point[] = [
|
||||
...tempPoints,
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Floor',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
},
|
||||
];
|
||||
|
||||
setTempFloor({
|
||||
floorUuid: 'temp-floor',
|
||||
points: floorPoints,
|
||||
topMaterial: 'default',
|
||||
sideMaterial: 'default',
|
||||
floorDepth,
|
||||
bevelStrength,
|
||||
isBeveled,
|
||||
decals: [],
|
||||
});
|
||||
|
||||
} else if (tempFloor !== null) {
|
||||
setTempFloor(null);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTempFloor(null);
|
||||
}, [toolMode, toggleView, tempPoints.length, floorDepth, bevelStrength, isBeveled]);
|
||||
|
||||
const allLines = useMemo(() => {
|
||||
if (!tempFloor || tempFloor.points.length < 2) return [];
|
||||
|
||||
const lines: [Point, Point][] = [];
|
||||
const pts = tempFloor.points;
|
||||
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
lines.push([pts[i], pts[i + 1]]);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}, [tempFloor]);
|
||||
|
||||
if (!tempFloor) return null;
|
||||
|
||||
return (
|
||||
<group name="Floor-Reference-Group">
|
||||
{allLines.map(([p1, p2], idx) => {
|
||||
const v1 = new THREE.Vector3(...p1.position);
|
||||
const v2 = new THREE.Vector3(...p2.position);
|
||||
const mid = new THREE.Vector3().addVectors(v1, v2).multiplyScalar(0.5);
|
||||
const dist = v1.distanceTo(v2);
|
||||
|
||||
return (
|
||||
<group key={`${p1.pointUuid}-${p2.pointUuid}`}>
|
||||
<ReferenceLine points={[p1, p2]} />
|
||||
{toggleView && (
|
||||
<Html
|
||||
position={[mid.x, 1, mid.z]}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div className={`distance ref-line-${idx}`}>{dist.toFixed(2)} m</div>
|
||||
</Html>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
|
||||
{tempPoints.length >= 3 && (
|
||||
<Floor floor={tempFloor.points} />
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceFloor;
|
||||
|
||||
|
||||
function Floor({ floor }: { floor: Point[] }) {
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
|
||||
const shape = useMemo(() => {
|
||||
const shape = new THREE.Shape();
|
||||
const points = floor.map(p => new THREE.Vector2(p.position[0], p.position[2]));
|
||||
if (points.length < 3) return null;
|
||||
shape.moveTo(points[0].x, points[0].y);
|
||||
points.forEach((pt) => { shape.lineTo(pt.x, pt.y); });
|
||||
return shape;
|
||||
}, [floor]);
|
||||
|
||||
if (!shape) return null;
|
||||
|
||||
return (
|
||||
<group name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
|
||||
<Extrude
|
||||
args={[shape, { depth: 0.001, bevelEnabled: false }]}
|
||||
position={[0, 0, 0]}
|
||||
receiveShadow
|
||||
>
|
||||
<meshStandardMaterial color={savedTheme === "dark" ? "#d2baff" : "#6f42c1"} depthWrite={false} transparent opacity={0.3} side={THREE.DoubleSide} />
|
||||
</Extrude>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
31
app/src/modules/builder/floor/floorGroup.tsx
Normal file
31
app/src/modules/builder/floor/floorGroup.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useToggleView } from '../../../store/builder/store'
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import FloorCreator from './floorCreator/floorCreator';
|
||||
import FloorInstances from './Instances/floorInstances';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
|
||||
function FloorGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
setSelectedFloor(null);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}, [togglView, activeModule])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<FloorCreator />
|
||||
|
||||
<FloorInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorGroup
|
||||
@@ -148,11 +148,11 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
||||
}
|
||||
|
||||
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, selectedVersion?.versionId || '',);
|
||||
// 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, selectedVersion?.versionId || '',);
|
||||
// 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 || '',);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,15 @@ import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { DragControls, Tube } from '@react-three/drei';
|
||||
import { useToolMode } from '../../../store/builder/store';
|
||||
import { useSocketStore, useToolMode } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
|
||||
interface LineProps {
|
||||
points: [Point, Point];
|
||||
@@ -16,9 +21,15 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const { raycaster, camera, pointer, gl } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const [isDeletable, setIsDeletable] = useState(false);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { wallStore } = useSceneContext();
|
||||
const { removeWallByPoints, setPosition } = wallStore();
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
|
||||
const { removeFloorByPoints, setPosition: setFloorPosition } = floorStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
|
||||
|
||||
@@ -79,7 +90,29 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const handlePointClick = (points: [Point, Point]) => {
|
||||
if (toolMode === '2D-Delete') {
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
removeWallByPoints(points);
|
||||
const removedWall = removeWallByPoints(points);
|
||||
if (removedWall && projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: removedWall.wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
setHoveredLine(null);
|
||||
}
|
||||
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
|
||||
removeFloorByPoints(points);
|
||||
setHoveredLine(null);
|
||||
}
|
||||
gl.domElement.style.cursor = 'default';
|
||||
@@ -105,8 +138,14 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const newStart = new THREE.Vector3().addVectors(start, delta);
|
||||
const newEnd = new THREE.Vector3().addVectors(end, delta);
|
||||
|
||||
setPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
|
||||
setPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
setWallPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
|
||||
setWallPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
|
||||
}
|
||||
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
|
||||
setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
|
||||
setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -127,11 +166,38 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
};
|
||||
|
||||
const handleDragEnd = (points: [Point, Point]) => {
|
||||
if (toolMode !== 'move' || !dragOffset) return;
|
||||
gl.domElement.style.cursor = 'default';
|
||||
setDragOffset(null);
|
||||
if (toolMode !== 'move') return;
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
// console.log('Wall after drag: ', points);
|
||||
const updatedWalls1 = getWallsByPointId(points[0].pointUuid);
|
||||
const updatedWalls2 = getWallsByPointId(points[1].pointUuid);
|
||||
const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
|
||||
|
||||
if (updatedWalls.length > 0 && projectId) {
|
||||
updatedWalls.forEach(updatedWall => {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall).catch((error) => {
|
||||
// console.error('Error updating wall:', error);
|
||||
// });
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
})
|
||||
}
|
||||
} else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
|
||||
// Handle floor update logic here if needed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping i
|
||||
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 { aisleStore, wallStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore } = useSceneContext();
|
||||
const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
|
||||
const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore();
|
||||
const { floors, getConnectedPoints: getConnectedFloorPoints } = floorStore();
|
||||
|
||||
// Wall Snapping
|
||||
|
||||
@@ -24,10 +25,13 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
||||
);
|
||||
}, [walls, currentPoint]);
|
||||
|
||||
const snapWallPoint = useCallback((position: [number, number, number]) => {
|
||||
const snapWallPoint = useCallback((position: [number, number, number], tempPoints?: Point) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherWallPoints();
|
||||
if (tempPoints) {
|
||||
otherPoints.push(tempPoints);
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
@@ -187,10 +191,96 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
||||
};
|
||||
}, [currentPoint, getConnectedAislePoints]);
|
||||
|
||||
// Floor Snapping
|
||||
|
||||
const getAllOtherFloorPoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return floors.flatMap(floor =>
|
||||
floor.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
}, [floors, currentPoint]);
|
||||
|
||||
const snapFloorPoint = useCallback((position: [number, number, number], tempPoints: Point[] | []) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = [...getAllOtherFloorPoints(), ...tempPoints];
|
||||
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 === 'Floor') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherFloorPoints]);
|
||||
|
||||
const snapFloorAngle = 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 = getConnectedFloorPoints(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, getConnectedFloorPoints]);
|
||||
|
||||
return {
|
||||
snapAislePoint,
|
||||
snapAisleAngle,
|
||||
snapWallPoint,
|
||||
snapWallAngle,
|
||||
snapFloorPoint,
|
||||
snapFloorAngle,
|
||||
};
|
||||
};
|
||||
@@ -1,29 +1,36 @@
|
||||
import * as THREE from 'three';
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import { useRef, useState, useEffect, useMemo } from 'react';
|
||||
import { useToolMode } from '../../../store/builder/store';
|
||||
import { useSocketStore, useToolMode } from '../../../store/builder/store';
|
||||
import { DragControls } from '@react-three/drei';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { usePointSnapping } from './helpers/usePointSnapping';
|
||||
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';
|
||||
|
||||
import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
|
||||
import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
|
||||
function Point({ point }: { readonly point: Point }) {
|
||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||
const { raycaster, camera, pointer, gl } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { aisleStore, wallStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore } = useSceneContext();
|
||||
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
|
||||
const { setPosition: setWallPosition, removePoint: removeWallPoint } = wallStore();
|
||||
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
|
||||
const { setPosition: setFloorPosition, removePoint: removeFloorPoint } = floorStore();
|
||||
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
|
||||
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
@@ -113,6 +120,10 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const wallSnapped = snapWallAngle(newPosition);
|
||||
const finalSnapped = snapWallPoint(wallSnapped.position);
|
||||
setWallPosition(point.pointUuid, finalSnapped.position);
|
||||
} else if (point.pointType === 'Floor') {
|
||||
const floorSnapped = snapWallAngle(newPosition);
|
||||
const finalSnapped = snapWallPoint(floorSnapped.position);
|
||||
setFloorPosition(point.pointUuid, finalSnapped.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,11 +149,34 @@ 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, selectedVersion?.versionId || '')
|
||||
upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
|
||||
})
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
const updatedWalls = getWallsByPointId(point.pointUuid);
|
||||
if (updatedWalls && updatedWalls.length > 0 && projectId) {
|
||||
updatedWalls.forEach((updatedWall) => {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
});
|
||||
}
|
||||
// console.log('Wall after drag: ', point);
|
||||
} else if (point.pointType === 'Floor') {
|
||||
// console.log('Floor after drag: ', point);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,16 +186,43 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const removedAisles = removeAislePoint(point.pointUuid);
|
||||
if (removedAisles.length > 0) {
|
||||
removedAisles.forEach(aisle => {
|
||||
if (projectId)
|
||||
if (projectId) {
|
||||
deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '')
|
||||
}
|
||||
});
|
||||
setHoveredPoint(null);
|
||||
}
|
||||
}
|
||||
if (point.pointType === 'Wall') {
|
||||
const removedAisles = removeWallPoint(point.pointUuid);
|
||||
if (removedAisles.length > 0) {
|
||||
const removedWalls = removeWallPoint(point.pointUuid);
|
||||
if (removedWalls.length > 0) {
|
||||
setHoveredPoint(null);
|
||||
removedWalls.forEach(wall => {
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: wall.wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (point.pointType === 'Floor') {
|
||||
const removedFloors = removeFloorPoint(point.pointUuid);
|
||||
setHoveredPoint(null);
|
||||
if (removedFloors.length > 0) {
|
||||
}
|
||||
}
|
||||
gl.domElement.style.cursor = 'default';
|
||||
|
||||
@@ -21,9 +21,15 @@ function ReferencePoint({ point }: { readonly point: Point }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pointName = point.pointType === 'Wall' ? 'Wall-Point' :
|
||||
point.pointType === 'Floor' ? 'Floor-Point' :
|
||||
point.pointType === 'Aisle' ? 'Aisle-Point' :
|
||||
point.pointType === 'Zone' ? 'Zone-Point' : 'Point';
|
||||
|
||||
return (
|
||||
<mesh
|
||||
position={new THREE.Vector3(...point.position)}
|
||||
name={pointName}
|
||||
>
|
||||
<boxGeometry args={boxScale} />
|
||||
<shaderMaterial
|
||||
|
||||
@@ -28,8 +28,8 @@ function WallInstances() {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
walls.forEach(aisle => {
|
||||
aisle.points.forEach(point => {
|
||||
walls.forEach(wall => {
|
||||
wall.points.forEach(point => {
|
||||
if (!seenUuids.has(point.pointUuid)) {
|
||||
seenUuids.add(point.pointUuid);
|
||||
points.push(point);
|
||||
|
||||
@@ -36,46 +36,45 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
|
||||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (intersectionPoint) {
|
||||
const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
setSnappedPosition(snapped.position);
|
||||
setSnappedPoint(snapped.snappedPoint);
|
||||
} else if (directionalSnap.isSnapped) {
|
||||
finalPosition.current = directionalSnap.position;
|
||||
setSnappedPosition(directionalSnap.position);
|
||||
setSnappedPoint(null);
|
||||
} else {
|
||||
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
const wallPoints: [Point, Point] = [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Wall',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
}
|
||||
];
|
||||
|
||||
setTempWall({
|
||||
wallUuid: 'temp-wall',
|
||||
points: wallPoints,
|
||||
outsideMaterial: 'default',
|
||||
insideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
})
|
||||
if (!intersectionPoint) return;
|
||||
const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints[0]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
setSnappedPosition(snapped.position);
|
||||
setSnappedPoint(snapped.snappedPoint);
|
||||
} else if (directionalSnap.isSnapped) {
|
||||
finalPosition.current = directionalSnap.position;
|
||||
setSnappedPosition(directionalSnap.position);
|
||||
setSnappedPoint(null);
|
||||
} else {
|
||||
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
|
||||
if (!finalPosition.current) return;
|
||||
|
||||
const wallPoints: [Point, Point] = [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Wall',
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
}
|
||||
];
|
||||
|
||||
setTempWall({
|
||||
wallUuid: 'temp-wall',
|
||||
points: wallPoints,
|
||||
outsideMaterial: 'default',
|
||||
insideMaterial: 'default',
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
})
|
||||
|
||||
} else if (tempWall !== null) {
|
||||
setTempWall(null);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,15 @@ import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../.
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
import getClosestIntersection from '../../geomentries/lines/getClosestIntersection';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import ReferenceWall from './referenceWall';
|
||||
import getClosestIntersection from '../../geomentries/lines/getClosestIntersection';
|
||||
|
||||
import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
import { deleteWallApi } from '../../../../services/factoryBuilder/wall/deleteWallApi';
|
||||
|
||||
function WallCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
@@ -20,10 +26,14 @@ function WallCreator() {
|
||||
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
@@ -58,6 +68,7 @@ function WallCreator() {
|
||||
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) {
|
||||
@@ -80,6 +91,25 @@ function WallCreator() {
|
||||
const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t);
|
||||
|
||||
removeWall(wall.wallUuid);
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: wall.wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
|
||||
}
|
||||
|
||||
const point1: Point = {
|
||||
pointUuid: wall.points[0].pointUuid,
|
||||
@@ -114,6 +144,24 @@ function WallCreator() {
|
||||
}
|
||||
addWall(wall2);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall2);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wall2,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
|
||||
const wall3: Wall = {
|
||||
wallUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [point2, newPoint],
|
||||
@@ -125,6 +173,24 @@ function WallCreator() {
|
||||
}
|
||||
addWall(wall3);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall3);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data2 = {
|
||||
wallData: wall3,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data2);
|
||||
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
@@ -139,6 +205,24 @@ function WallCreator() {
|
||||
};
|
||||
addWall(wall1);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall1);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wall1,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
|
||||
const wall2: Wall = {
|
||||
wallUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [point1, newPoint],
|
||||
@@ -150,6 +234,24 @@ function WallCreator() {
|
||||
}
|
||||
addWall(wall2);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall2);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data1 = {
|
||||
wallData: wall2,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data1);
|
||||
|
||||
const wall3: Wall = {
|
||||
wallUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [point2, newPoint],
|
||||
@@ -161,6 +263,24 @@ function WallCreator() {
|
||||
}
|
||||
addWall(wall3);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall3);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data3 = {
|
||||
wallData: wall3,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data3);
|
||||
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
@@ -181,6 +301,8 @@ function WallCreator() {
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return }
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
@@ -208,6 +330,25 @@ function WallCreator() {
|
||||
decals: []
|
||||
};
|
||||
addWall(wall);
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall);
|
||||
// }
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
@@ -222,6 +363,10 @@ function WallCreator() {
|
||||
};
|
||||
|
||||
if (toolMode === "Wall" && toggleView) {
|
||||
if (tempPoints.length === 0) {
|
||||
setSnappedPosition(null);
|
||||
setSnappedPoint(null);
|
||||
}
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
@@ -244,7 +389,7 @@ function WallCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, selectedVersion?.versionId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useToggleView } from '../../../store/builder/store'
|
||||
import { useToggleView } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import WallCreator from './wallCreator/wallCreator'
|
||||
import WallInstances from './Instances/wallInstances'
|
||||
import WallCreator from './wallCreator/wallCreator';
|
||||
import WallInstances from './Instances/wallInstances';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi';
|
||||
|
||||
function WallGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { wallStore } = useSceneContext();
|
||||
const { setWalls } = wallStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
@@ -17,6 +26,20 @@ function WallGroup() {
|
||||
}
|
||||
}, [togglView, activeModule])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
getWallsApi(projectId, selectedVersion?.versionId || '').then((walls) => {
|
||||
if (walls && walls.length > 0) {
|
||||
setWalls(walls);
|
||||
} else {
|
||||
setWalls([]);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
})
|
||||
}
|
||||
}, [projectId, selectedVersion?.versionId])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export const deleteAisleApi = async (aisleUuid: string, projectId: string, versi
|
||||
token: localStorage.getItem("token") || "", // Coerce null to empty string
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify({ aisleUuid, projectId }),
|
||||
body: JSON.stringify({ aisleUuid, projectId, versionId }),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const createAisleApi = async (
|
||||
export const upsertAisleApi = async (
|
||||
aisleUuid: string,
|
||||
points: any,
|
||||
type: Object,
|
||||
@@ -7,7 +7,7 @@ export const deleteWallItem = async (
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v1/deleteWallItem`,
|
||||
`${url_Backend_dwinzo}/api/V1/wallItems/delete`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
|
||||
@@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
|
||||
export const getWallItems = async (organization: string, projectId?: string, versionId?: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/walls/${projectId}/${versionId}`,
|
||||
`${url_Backend_dwinzo}/api/V1/wallItems/${projectId}/${versionId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const setWallItem = async (
|
||||
scale: Object
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v1/setWallItems`, {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/wallItems`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
|
||||
39
app/src/services/factoryBuilder/wall/deleteWallApi.ts
Normal file
39
app/src/services/factoryBuilder/wall/deleteWallApi.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const deleteWallApi = async (
|
||||
projectId: string,
|
||||
versionId: string,
|
||||
wallUuid: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/deleteWall`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify({ projectId, versionId, wallUuid }),
|
||||
});
|
||||
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Failed to delete wall:", response.statusText);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to delete wall");
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
37
app/src/services/factoryBuilder/wall/getWallsApi.ts
Normal file
37
app/src/services/factoryBuilder/wall/getWallsApi.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const getWallsApi = async (
|
||||
projectId: string,
|
||||
versionId: string,
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/Walls/${projectId}/${versionId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
});
|
||||
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Failed to get wall:", response.statusText);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to get wall");
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
39
app/src/services/factoryBuilder/wall/upsertWallApi.ts
Normal file
39
app/src/services/factoryBuilder/wall/upsertWallApi.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const upsertWallApi = async (
|
||||
projectId: string,
|
||||
versionId: string,
|
||||
wallData: Wall
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertWall`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify({ projectId, versionId, wallData }),
|
||||
});
|
||||
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Failed to upsert wall:", response.statusText);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to upsert wall");
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -16,6 +16,14 @@ interface BuilderState {
|
||||
outsideMaterial: string;
|
||||
insideMaterial: string;
|
||||
|
||||
// Floor Settings
|
||||
selectedFloor: Object3D | null;
|
||||
floorDepth: number;
|
||||
isBeveled: boolean;
|
||||
bevelStrength: number;
|
||||
sideMaterial: string;
|
||||
topMaterial: string;
|
||||
|
||||
// Decal Settings
|
||||
selectedDecal: Object3D | null;
|
||||
|
||||
@@ -44,6 +52,13 @@ interface BuilderState {
|
||||
setWallHeight: (height: number) => void;
|
||||
setWallMaterial: (material: string, side: 'inside' | 'outside') => void;
|
||||
|
||||
// Setters - Floor
|
||||
setSelectedFloor: (floor: Object3D | null) => void;
|
||||
setFloorDepth: (depth: number) => void;
|
||||
setBeveled: (isBeveled: boolean) => void;
|
||||
setBevelStrength: (strength: number) => void;
|
||||
setFloorMaterial: (material: string, side: 'side' | 'top') => void;
|
||||
|
||||
// Setters - Decal
|
||||
setSelectedDecal: (decal: Object3D | null) => void;
|
||||
|
||||
@@ -81,6 +96,13 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
outsideMaterial: 'Default Material',
|
||||
insideMaterial: 'Material 1',
|
||||
|
||||
selectedFloor: null,
|
||||
floorDepth: 0.3,
|
||||
isBeveled: false,
|
||||
bevelStrength: 0.05,
|
||||
sideMaterial: 'Default Side',
|
||||
topMaterial: 'Default Top',
|
||||
|
||||
selectedDecal: null,
|
||||
|
||||
selectedAisle: null,
|
||||
@@ -147,6 +169,38 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
});
|
||||
},
|
||||
|
||||
// === Setters: Floor ===
|
||||
setSelectedFloor: (floor: Object3D | null) => {
|
||||
set((state) => {
|
||||
state.selectedFloor = floor;
|
||||
});
|
||||
},
|
||||
|
||||
setFloorDepth: (depth: number) => {
|
||||
set((state) => {
|
||||
state.floorDepth = depth;
|
||||
});
|
||||
},
|
||||
|
||||
setBeveled: (isBeveled: boolean) => {
|
||||
set((state) => {
|
||||
state.isBeveled = isBeveled;
|
||||
});
|
||||
},
|
||||
|
||||
setBevelStrength: (strength: number) => {
|
||||
set((state) => {
|
||||
state.bevelStrength = strength;
|
||||
});
|
||||
},
|
||||
|
||||
setFloorMaterial: (material: string, side: 'side' | 'top') => {
|
||||
set((state) => {
|
||||
if (side === 'side') state.sideMaterial = material;
|
||||
else state.topMaterial = material;
|
||||
});
|
||||
},
|
||||
|
||||
// === Setters: Decal ===
|
||||
|
||||
setSelectedDecal: (decal: Object3D | null) => {
|
||||
|
||||
@@ -7,14 +7,29 @@ interface FloorStore {
|
||||
addFloor: (floor: Floor) => void;
|
||||
updateFloor: (uuid: string, updated: Partial<Floor>) => void;
|
||||
removeFloor: (uuid: string) => void;
|
||||
removePointFromFloors: (pointUuid: string) => void;
|
||||
removePoint: (pointUuid: string) => Floor[];
|
||||
removeFloorByPoints: (Points: [Point, Point]) => Floor[];
|
||||
clearFloors: () => void;
|
||||
setPosition: (
|
||||
pointUuid: string,
|
||||
position: [number, number, number]
|
||||
) => Floor | undefined;
|
||||
setIsBeveled: (uuid: string, isBeveled: boolean) => void;
|
||||
setBevelStrength: (uuid: string, strength: number) => void;
|
||||
setDepth: (uuid: string, depth: number) => void;
|
||||
setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void;
|
||||
addDecal: (floors: string, decal: Decal) => void;
|
||||
updateDecal: (decalUuid: string, decal: Decal) => void;
|
||||
removeDecal: (decalUuid: string) => void;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
|
||||
updateDecalRotation: (decalUuid: string, rotation: number) => void;
|
||||
updateDecalScale: (decalUuid: string, scale: number) => void;
|
||||
|
||||
getFloorById: (uuid: string) => Floor | undefined;
|
||||
getFloorsByPointId: (uuid: string) => Floor | undefined;
|
||||
getFloorByPoints: (points: Point[]) => Floor | undefined;
|
||||
getFloorPointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
}
|
||||
|
||||
export const createFloorStore = () => {
|
||||
@@ -41,16 +56,98 @@ export const createFloorStore = () => {
|
||||
state.floors = state.floors.filter(f => f.floorUuid !== uuid);
|
||||
}),
|
||||
|
||||
removePointFromFloors: (pointUuid) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
floor.points = floor.points.filter(p => p.pointUuid !== pointUuid);
|
||||
}
|
||||
}),
|
||||
removePoint: (pointUuid) => {
|
||||
const removedFloors: Floor[] = [];
|
||||
|
||||
set(state => {
|
||||
const updatedFloors: Floor[] = [];
|
||||
|
||||
for (const floor of state.floors) {
|
||||
const hasPoint = floor.points.some(p => p.pointUuid === pointUuid);
|
||||
if (!hasPoint) {
|
||||
updatedFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
const remainingPoints = floor.points.filter(p => p.pointUuid !== pointUuid);
|
||||
if (remainingPoints.length > 2) {
|
||||
floor.points = remainingPoints;
|
||||
updatedFloors.push(floor);
|
||||
} else {
|
||||
removedFloors.push(floor);
|
||||
}
|
||||
}
|
||||
|
||||
state.floors = updatedFloors;
|
||||
});
|
||||
|
||||
return removedFloors;
|
||||
},
|
||||
removeFloorByPoints: ([pointA, pointB]) => {
|
||||
const removedFloors: Floor[] = [];
|
||||
|
||||
set(state => {
|
||||
const updatedFloors: Floor[] = [];
|
||||
|
||||
for (const floor of state.floors) {
|
||||
const indices = floor.points
|
||||
.map((p, i) => ({ uuid: p.pointUuid, index: i }));
|
||||
|
||||
const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1;
|
||||
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
|
||||
|
||||
if (idxA === -1 || idxB === -1) {
|
||||
updatedFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
const areAdjacent =
|
||||
Math.abs(idxA - idxB) === 1 ||
|
||||
(idxA === 0 && idxB === floor.points.length - 1) ||
|
||||
(idxB === 0 && idxA === floor.points.length - 1);
|
||||
|
||||
if (!areAdjacent) {
|
||||
updatedFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
const remainingPoints = floor.points.filter(
|
||||
p => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid
|
||||
);
|
||||
|
||||
if (remainingPoints.length > 2) {
|
||||
floor.points = remainingPoints;
|
||||
updatedFloors.push(floor);
|
||||
} else {
|
||||
removedFloors.push(floor);
|
||||
}
|
||||
}
|
||||
|
||||
state.floors = updatedFloors;
|
||||
});
|
||||
|
||||
return removedFloors;
|
||||
},
|
||||
|
||||
clearFloors: () => set(state => {
|
||||
state.floors = [];
|
||||
}),
|
||||
|
||||
setPosition: (pointUuid, position) => {
|
||||
let updatedFloor: Floor | undefined;
|
||||
set((state) => {
|
||||
for (const floor of state.floors) {
|
||||
const point = floor.points.find((p) => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.position = position;
|
||||
updatedFloor = JSON.parse(JSON.stringify(floor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return updatedFloor;
|
||||
},
|
||||
|
||||
setIsBeveled: (uuid, isBeveled) => set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === uuid);
|
||||
if (floor) {
|
||||
@@ -80,9 +177,97 @@ export const createFloorStore = () => {
|
||||
}
|
||||
}),
|
||||
|
||||
addDecal: (floorUuid, decal) => set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === floorUuid);
|
||||
if (floor) {
|
||||
floor.decals.push(decal);
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecal: (decalUuid, updatedDecal) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const index = floor.decals.findIndex(d => d.decalUuid === decalUuid);
|
||||
if (index !== -1) {
|
||||
floor.decals[index] = updatedDecal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
removeDecal: (decalUuid) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid);
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecalPosition: (decalUuid, position) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const decal = floor.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decal) {
|
||||
decal.decalPosition = position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecalRotation: (decalUuid, rotation) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const decal = floor.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decal) {
|
||||
decal.decalRotation = rotation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecalScale: (decalUuid, scale) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const decal = floor.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decal) {
|
||||
decal.decalScale = scale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
getFloorById: (uuid) => {
|
||||
return get().floors.find(f => f.floorUuid === uuid);
|
||||
},
|
||||
|
||||
getFloorsByPointId: (pointUuid) => {
|
||||
return get().floors.find(floor =>
|
||||
floor.points.some(p => p.pointUuid === pointUuid)
|
||||
);
|
||||
},
|
||||
|
||||
getFloorByPoints: (points) => {
|
||||
return get().floors.find(floor => {
|
||||
const floorPointIds = new Set(floor.points.map(p => p.pointUuid));
|
||||
const givenPointIds = new Set(points.map(p => p.pointUuid));
|
||||
return floorPointIds.size === givenPointIds.size &&
|
||||
[...floorPointIds].every(id => givenPointIds.has(id));
|
||||
});
|
||||
},
|
||||
|
||||
getFloorPointById: (pointUuid) => {
|
||||
for (const floor of get().floors) {
|
||||
const point = floor.points.find(p => p.pointUuid === pointUuid);
|
||||
if (point) return point;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getConnectedPoints: (pointUuid) => {
|
||||
const connected: Point[] = [];
|
||||
for (const floor of get().floors) {
|
||||
if (floor.points.some(p => p.pointUuid === pointUuid)) {
|
||||
connected.push(...floor.points.filter(p => p.pointUuid !== pointUuid));
|
||||
}
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ interface WallStore {
|
||||
walls: Wall[];
|
||||
setWalls: (walls: Wall[]) => void;
|
||||
addWall: (wall: Wall) => void;
|
||||
updateWall: (uuid: string, updated: Partial<Wall>) => void;
|
||||
updateWall: (uuid: string, updated: Partial<Wall>) => Wall | undefined;
|
||||
removeWall: (uuid: string) => void;
|
||||
clearWalls: () => void;
|
||||
removeWallByPoints: (Points: [Point, Point]) => Wall | undefined;
|
||||
@@ -17,11 +17,11 @@ interface WallStore {
|
||||
updateDecalScale: (decalUuid: string, scale: number) => void;
|
||||
|
||||
removePoint: (pointUuid: string) => Wall[];
|
||||
setPosition: (pointUuid: string, position: [number, number, number]) => void;
|
||||
setPosition: (pointUuid: string, position: [number, number, number]) => Wall[] | [];
|
||||
setLayer: (pointUuid: string, layer: number) => void;
|
||||
|
||||
getWallById: (uuid: string) => Wall | undefined;
|
||||
getWallByPointId: (uuid: string) => Wall | undefined;
|
||||
getWallsByPointId: (uuid: string) => Wall[] | [];
|
||||
getWallByPoints: (points: Point[]) => Wall | undefined;
|
||||
getWallPointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
@@ -40,12 +40,17 @@ export const createWallStore = () => {
|
||||
state.walls.push(wall);
|
||||
}),
|
||||
|
||||
updateWall: (uuid, updated) => set((state) => {
|
||||
const wall = state.walls.find(w => w.wallUuid === uuid);
|
||||
if (wall) {
|
||||
Object.assign(wall, updated);
|
||||
}
|
||||
}),
|
||||
updateWall: (uuid, updated) => {
|
||||
let updatedWall: Wall | undefined;
|
||||
set((state) => {
|
||||
const wall = state.walls.find(w => w.wallUuid === uuid);
|
||||
if (wall) {
|
||||
Object.assign(wall, updated);
|
||||
updatedWall = JSON.parse(JSON.stringify(wall));
|
||||
}
|
||||
});
|
||||
return updatedWall;
|
||||
},
|
||||
|
||||
removeWall: (uuid) => set((state) => {
|
||||
state.walls = state.walls.filter(w => w.wallUuid !== uuid);
|
||||
@@ -144,14 +149,19 @@ export const createWallStore = () => {
|
||||
return removedWalls;
|
||||
},
|
||||
|
||||
setPosition: (pointUuid, position) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const point = wall.points.find(p => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.position = position;
|
||||
setPosition: (pointUuid, position) => {
|
||||
let updatedWalls: Wall[] = [];
|
||||
set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const point = wall.points.find(p => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.position = position;
|
||||
updatedWalls.push(wall);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedWalls;
|
||||
},
|
||||
|
||||
setLayer: (pointUuid, layer) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
@@ -166,13 +176,10 @@ export const createWallStore = () => {
|
||||
return get().walls.find(w => w.wallUuid === uuid);
|
||||
},
|
||||
|
||||
getWallByPointId: (uuid) => {
|
||||
for (const wall of get().walls) {
|
||||
if (wall.points.some(p => p.pointUuid === uuid)) {
|
||||
return wall;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
getWallsByPointId: (uuid) => {
|
||||
return get().walls.filter((a) => {
|
||||
return a.points.some((p) => p.pointUuid === uuid);
|
||||
})
|
||||
},
|
||||
|
||||
getWallByPoints: (point) => {
|
||||
|
||||
Reference in New Issue
Block a user