feat: Implement undo and redo functionality for 2D scene controls

- Added useRedoHandler to manage redo actions, including socket communication for wall, floor, zone, and aisle updates.
- Added useUndoHandler to manage undo actions, reversing the effects of previous actions with corresponding socket updates.
- Created UndoRedo2DControls component to handle keyboard shortcuts for undo (Ctrl+Z) and redo (Ctrl+Y).
- Established a Zustand store (useUndoRedo2DStore) to maintain undo and redo stacks, with methods for pushing, popping, and peeking actions.
This commit is contained in:
2025-07-29 17:20:34 +05:30
parent 253b3db2ed
commit fcd924eb31
15 changed files with 1701 additions and 301 deletions

View File

@@ -19,8 +19,9 @@ function FloorCreator() {
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { floorStore } = useSceneContext(); const { floorStore, undoRedo2DStore } = useSceneContext();
const { addFloor, getFloorPointById, getFloorByPoints } = floorStore(); const { addFloor, getFloorPointById } = floorStore();
const { push2D } = undoRedo2DStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -103,6 +104,21 @@ function FloorCreator() {
}; };
addFloor(floor); addFloor(floor);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Floor',
lineData: floor,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -142,6 +158,21 @@ function FloorCreator() {
}; };
addFloor(floor); addFloor(floor);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Floor',
lineData: floor,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -191,6 +222,21 @@ function FloorCreator() {
}; };
addFloor(floor); addFloor(floor);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Floor',
lineData: floor,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -243,7 +289,7 @@ function FloorCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); canvasElement.removeEventListener("contextmenu", onContext);
}; };
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]); }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
return ( return (
<> <>

View File

@@ -30,10 +30,11 @@ function Line({ points }: Readonly<LineProps>) {
const [isDeletable, setIsDeletable] = useState(false); const [isDeletable, setIsDeletable] = useState(false);
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { wallStore, floorStore, zoneStore } = useSceneContext(); const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
const { push2D } = undoRedo2DStore();
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore();
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore(); const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
@@ -117,10 +118,26 @@ function Line({ points }: Readonly<LineProps>) {
} }
socket.emit('v1:model-Wall:delete', data); socket.emit('v1:model-Wall:delete', data);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Delete',
point: {
type: 'Wall',
lineData: removedWall,
timeStamp: new Date().toISOString(),
}
}
]
});
} }
setHoveredLine(null); setHoveredLine(null);
} }
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
const Floors = getFloorsByPoints(points);
const { removedFloors, updatedFloors } = removeFloorByPoints(points); const { removedFloors, updatedFloors } = removeFloorByPoints(points);
if (removedFloors.length > 0) { if (removedFloors.length > 0) {
removedFloors.forEach(floor => { removedFloors.forEach(floor => {
@@ -143,6 +160,22 @@ function Line({ points }: Readonly<LineProps>) {
socket.emit('v1:model-Floor:delete', data); socket.emit('v1:model-Floor:delete', data);
} }
}); });
const removedFloorsData = removedFloors.map((floor) => ({
type: "Floor" as const,
lineData: floor,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedFloorsData
}
]
});
} }
if (updatedFloors.length > 0) { if (updatedFloors.length > 0) {
updatedFloors.forEach(floor => { updatedFloors.forEach(floor => {
@@ -165,11 +198,29 @@ function Line({ points }: Readonly<LineProps>) {
socket.emit('v1:model-Floor:add', data); socket.emit('v1:model-Floor:add', data);
} }
}); });
const updatedFloorsData = updatedFloors.map((floor) => ({
type: "Floor" as const,
lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor,
newData: floor,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: updatedFloorsData
}
]
});
} }
setHoveredLine(null); setHoveredLine(null);
} }
if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') {
const Zones = getZonesByPoints(points);
const { removedZones, updatedZones } = removeZoneByPoints(points); const { removedZones, updatedZones } = removeZoneByPoints(points);
if (removedZones.length > 0) { if (removedZones.length > 0) {
removedZones.forEach(zone => { removedZones.forEach(zone => {
@@ -192,6 +243,22 @@ function Line({ points }: Readonly<LineProps>) {
socket.emit('v1:zone:delete', data); socket.emit('v1:zone:delete', data);
} }
}); });
const removedZonesData = removedZones.map((zone) => ({
type: "Zone" as const,
lineData: zone,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedZonesData
}
]
});
} }
if (updatedZones.length > 0) { if (updatedZones.length > 0) {
updatedZones.forEach(zone => { updatedZones.forEach(zone => {
@@ -214,7 +281,26 @@ function Line({ points }: Readonly<LineProps>) {
socket.emit('v1:zone:add', data); socket.emit('v1:zone:add', data);
} }
}); });
const updatedZonesData = updatedZones.map((zone) => ({
type: "Zone" as const,
lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone,
newData: zone,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: updatedZonesData
}
]
});
} }
setHoveredLine(null);
} }
handleCanvasCursors('default'); handleCanvasCursors('default');
} }

View File

@@ -32,13 +32,14 @@ function Point({ point }: { readonly point: Point }) {
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null); const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
const { push2D } = undoRedo2DStore();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore(); const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore(); const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore(); const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint,hoveredLine, setHoveredPoint } = useBuilderStore(); const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore();
const { selectedPoints } = useSelectedPoints(); const { selectedPoints } = useSelectedPoints();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -47,6 +48,13 @@ function Point({ point }: { readonly point: Point }) {
const boxScale: [number, number, number] = Constants.pointConfig.boxScale; const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
const colors = getColor(point); const colors = getColor(point);
const [initialPositions, setInitialPositions] = useState<{
aisles?: Aisle[],
walls?: Wall[],
floors?: Floor[],
zones?: Zone[]
}>({});
useEffect(() => { useEffect(() => {
handleCanvasCursors('default'); handleCanvasCursors('default');
}, [toolMode]) }, [toolMode])
@@ -152,6 +160,20 @@ function Point({ point }: { readonly point: Point }) {
const currentPosition = new THREE.Vector3(...point.position); const currentPosition = new THREE.Vector3(...point.position);
const offset = new THREE.Vector3().subVectors(currentPosition, hit); const offset = new THREE.Vector3().subVectors(currentPosition, hit);
setDragOffset(offset); setDragOffset(offset);
if (point.pointType === 'Aisle') {
const aisles = getAislesByPointId(point.pointUuid);
setInitialPositions({ aisles });
} else if (point.pointType === 'Wall') {
const walls = getWallsByPointId(point.pointUuid);
setInitialPositions({ walls });
} else if (point.pointType === 'Floor') {
const floors = getFloorsByPointId(point.pointUuid);
setInitialPositions({ floors });
} else if (point.pointType === 'Zone') {
const zones = getZonesByPointId(point.pointUuid);
setInitialPositions({ zones });
}
} }
}; };
@@ -159,6 +181,7 @@ function Point({ point }: { readonly point: Point }) {
handleCanvasCursors('default'); handleCanvasCursors('default');
setDragOffset(null); setDragOffset(null);
if (toolMode !== 'move') return; if (toolMode !== 'move') return;
if (point.pointType === 'Aisle') { if (point.pointType === 'Aisle') {
const updatedAisles = getAislesByPointId(point.pointUuid); const updatedAisles = getAislesByPointId(point.pointUuid);
if (updatedAisles.length > 0 && projectId) { if (updatedAisles.length > 0 && projectId) {
@@ -180,6 +203,23 @@ function Point({ point }: { readonly point: Point }) {
type: updatedAisle.type type: updatedAisle.type
}) })
}) })
if (initialPositions.aisles && initialPositions.aisles.length > 0) {
const updatedPoints = initialPositions.aisles.map((aisle) => ({
type: "Aisle" as const,
lineData: aisle,
newData: updatedAisles.find(a => a.aisleUuid === aisle.aisleUuid),
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [{
actionType: 'Lines-Update',
points: updatedPoints,
}]
});
}
} }
} else if (point.pointType === 'Wall') { } else if (point.pointType === 'Wall') {
const updatedWalls = getWallsByPointId(point.pointUuid); const updatedWalls = getWallsByPointId(point.pointUuid);
@@ -203,6 +243,23 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Wall:add', data); socket.emit('v1:model-Wall:add', data);
}); });
} }
if (initialPositions.walls && initialPositions.walls.length > 0) {
const updatedPoints = initialPositions.walls.map((wall) => ({
type: "Wall" as const,
lineData: wall,
newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid),
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [{
actionType: 'Lines-Update',
points: updatedPoints,
}]
});
}
} else if (point.pointType === 'Floor') { } else if (point.pointType === 'Floor') {
const updatedFloors = getFloorsByPointId(point.pointUuid); const updatedFloors = getFloorsByPointId(point.pointUuid);
if (updatedFloors && updatedFloors.length > 0 && projectId) { if (updatedFloors && updatedFloors.length > 0 && projectId) {
@@ -225,6 +282,23 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Floor:add', data); socket.emit('v1:model-Floor:add', data);
}); });
} }
if (initialPositions.floors && initialPositions.floors.length > 0) {
const updatedPoints = initialPositions.floors.map((floor) => ({
type: "Floor" as const,
lineData: floor,
newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid),
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [{
actionType: 'Lines-Update',
points: updatedPoints,
}]
});
}
} else if (point.pointType === 'Zone') { } else if (point.pointType === 'Zone') {
const updatedZones = getZonesByPointId(point.pointUuid); const updatedZones = getZonesByPointId(point.pointUuid);
if (updatedZones && updatedZones.length > 0 && projectId) { if (updatedZones && updatedZones.length > 0 && projectId) {
@@ -247,13 +321,33 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:zone:add', data); socket.emit('v1:zone:add', data);
}); });
} }
if (initialPositions.zones && initialPositions.zones.length > 0) {
const updatedPoints = initialPositions.zones.map((zone) => ({
type: "Zone" as const,
lineData: zone,
newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid),
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [{
actionType: 'Lines-Update',
points: updatedPoints,
}]
});
}
} }
setInitialPositions({});
} }
const handlePointClick = (point: Point) => { const handlePointClick = (point: Point) => {
if (toolMode === '2D-Delete') { if (toolMode === '2D-Delete') {
if (point.pointType === 'Aisle') { if (point.pointType === 'Aisle') {
const removedAisles = removeAislePoint(point.pointUuid); const removedAisles = removeAislePoint(point.pointUuid);
setHoveredPoint(null);
if (removedAisles.length > 0) { if (removedAisles.length > 0) {
removedAisles.forEach(aisle => { removedAisles.forEach(aisle => {
if (projectId) { if (projectId) {
@@ -273,9 +367,25 @@ function Point({ point }: { readonly point: Point }) {
} }
socket.emit('v1:model-aisle:delete', data); socket.emit('v1:model-aisle:delete', data);
} }
}); });
setHoveredPoint(null);
const removedAislesData = removedAisles.map((aisle) => ({
type: "Aisle" as const,
lineData: aisle,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedAislesData
}
]
});
} }
} }
if (point.pointType === 'Wall') { if (point.pointType === 'Wall') {
@@ -302,9 +412,26 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Wall:delete', data); socket.emit('v1:model-Wall:delete', data);
} }
}); });
const removedWallsData = removedWalls.map((wall) => ({
type: "Wall" as const,
lineData: wall,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedWallsData
}
]
});
} }
} }
if (point.pointType === 'Floor') { if (point.pointType === 'Floor') {
const Floors = getFloorsByPointId(point.pointUuid);
const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid); const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid);
setHoveredPoint(null); setHoveredPoint(null);
if (removedFloors.length > 0) { if (removedFloors.length > 0) {
@@ -328,6 +455,22 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Floor:delete', data); socket.emit('v1:model-Floor:delete', data);
} }
}); });
const removedFloorsData = removedFloors.map((floor) => ({
type: "Floor" as const,
lineData: floor,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedFloorsData
}
]
});
} }
if (updatedFloors.length > 0) { if (updatedFloors.length > 0) {
updatedFloors.forEach(floor => { updatedFloors.forEach(floor => {
@@ -350,9 +493,27 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Floor:add', data); socket.emit('v1:model-Floor:add', data);
} }
}); });
const updatedFloorsData = updatedFloors.map((floor) => ({
type: "Floor" as const,
lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor,
newData: floor,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: updatedFloorsData
}
]
});
} }
} }
if (point.pointType === 'Zone') { if (point.pointType === 'Zone') {
const Zones = getZonesByPointId(point.pointUuid);
const { removedZones, updatedZones } = removeZonePoint(point.pointUuid); const { removedZones, updatedZones } = removeZonePoint(point.pointUuid);
setHoveredPoint(null); setHoveredPoint(null);
if (removedZones.length > 0) { if (removedZones.length > 0) {
@@ -376,6 +537,22 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:zone:delete', data); socket.emit('v1:zone:delete', data);
} }
}); });
const removedZonesData = removedZones.map((zone) => ({
type: "Zone" as const,
lineData: zone,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Delete',
points: removedZonesData
}
]
});
} }
if (updatedZones.length > 0) { if (updatedZones.length > 0) {
updatedZones.forEach(zone => { updatedZones.forEach(zone => {
@@ -398,6 +575,23 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:zone:add', data); socket.emit('v1:zone:add', data);
} }
}); });
const updatedZonesData = updatedZones.map((zone) => ({
type: "Zone" as const,
lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone,
newData: zone,
timeStamp: new Date().toISOString(),
}));
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: updatedZonesData
}
]
});
} }
} }
handleCanvasCursors('default'); handleCanvasCursors('default');
@@ -422,7 +616,6 @@ function Point({ point }: { readonly point: Point }) {
return null; return null;
} }
return ( return (
<> <>
{!isSelected ? {!isSelected ?
@@ -453,7 +646,7 @@ function Point({ point }: { readonly point: Point }) {
onPointerOut={() => { onPointerOut={() => {
if (hoveredPoint) { if (hoveredPoint) {
setHoveredPoint(null); setHoveredPoint(null);
if(!hoveredLine){ if (!hoveredLine) {
handleCanvasCursors('default'); handleCanvasCursors('default');
} }
} }

View File

@@ -22,8 +22,9 @@ function WallCreator() {
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { wallStore } = useSceneContext(); const { wallStore, undoRedo2DStore } = useSceneContext();
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore(); const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
const { push2D } = undoRedo2DStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -91,6 +92,7 @@ function WallCreator() {
const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t); const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t);
removeWall(wall.wallUuid); removeWall(wall.wallUuid);
if (projectId) { if (projectId) {
// API // API
@@ -142,6 +144,7 @@ function WallCreator() {
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
} }
addWall(wall2); addWall(wall2);
// API // API
@@ -171,8 +174,36 @@ function WallCreator() {
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
} }
addWall(wall3); addWall(wall3);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Create',
points: [
{
type: 'Wall',
lineData: wall3,
timeStamp: new Date().toISOString(),
}, {
type: 'Wall',
lineData: wall2,
timeStamp: new Date().toISOString(),
}
]
}, {
actionType: 'Line-Delete',
point: {
type: 'Wall',
lineData: wall,
timeStamp: new Date().toISOString(),
}
}
],
})
// API // API
// if (projectId) { // if (projectId) {
@@ -202,7 +233,8 @@ function WallCreator() {
wallThickness: wallThickness, wallThickness: wallThickness,
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
}; }
addWall(wall1); addWall(wall1);
// API // API
@@ -232,6 +264,7 @@ function WallCreator() {
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
} }
addWall(wall2); addWall(wall2);
// API // API
@@ -261,8 +294,40 @@ function WallCreator() {
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
} }
addWall(wall3); addWall(wall3);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Create',
points: [
{
type: 'Wall',
lineData: wall3,
timeStamp: new Date().toISOString(),
}, {
type: 'Wall',
lineData: wall1,
timeStamp: new Date().toISOString(),
}, {
type: 'Wall',
lineData: wall2,
timeStamp: new Date().toISOString(),
}
]
}, {
actionType: 'Line-Delete',
point: {
type: 'Wall',
lineData: wall,
timeStamp: new Date().toISOString(),
}
}
],
})
// API // API
// if (projectId) { // if (projectId) {
@@ -328,9 +393,24 @@ function WallCreator() {
wallThickness: wallThickness, wallThickness: wallThickness,
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: []
}; }
addWall(wall); addWall(wall);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Wall',
lineData: wall,
timeStamp: new Date().toISOString(),
}
}
],
})
// API // API
// if (projectId) { // if (projectId) {

View File

@@ -19,8 +19,9 @@ function ZoneCreator() {
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { zoneStore } = useSceneContext(); const { zoneStore, undoRedo2DStore } = useSceneContext();
const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore(); const { addZone, getZonePointById } = zoneStore();
const { push2D } = undoRedo2DStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -102,6 +103,21 @@ function ZoneCreator() {
}; };
addZone(zone); addZone(zone);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Zone',
lineData: zone,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -139,6 +155,21 @@ function ZoneCreator() {
}; };
addZone(zone); addZone(zone);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Zone',
lineData: zone,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -186,6 +217,21 @@ function ZoneCreator() {
}; };
addZone(zone); addZone(zone);
push2D({
type: 'Draw',
actions: [
{
actionType: 'Line-Create',
point: {
type: 'Zone',
lineData: zone,
timeStamp: new Date().toISOString(),
}
}
],
})
if (projectId) { if (projectId) {
// API // API
@@ -238,7 +284,7 @@ function ZoneCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); canvasElement.removeEventListener("contextmenu", onContext);
}; };
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]); }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, zoneColor, zoneHeight, snappedPosition, snappedPoint]);
return ( return (
<> <>

View File

@@ -14,6 +14,7 @@ import TransformControl from "./transformControls/transformControls";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { getUserData } from "../../../functions/getUserData"; import { getUserData } from "../../../functions/getUserData";
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
export default function Controls() { export default function Controls() {
const controlsRef = useRef<CameraControls>(null); const controlsRef = useRef<CameraControls>(null);
@@ -142,6 +143,8 @@ export default function Controls() {
<SelectionControls2D /> <SelectionControls2D />
<UndoRedo2DControls />
<TransformControl /> <TransformControl />
</> </>

View File

@@ -0,0 +1,355 @@
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useVersionContext } from "../../../../builder/version/versionContext";
import { useSceneContext } from "../../../sceneContext";
import { useSocketStore } from "../../../../../store/builder/store";
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi";
// import { deleteZoneApi } from "../../../../../services/factoryBuilder/zone/deleteZoneApi";
// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi";
// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi";
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi";
function useRedoHandler() {
const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext();
const { redo2D, peekRedo2D } = undoRedo2DStore();
const { addWall, removeWall, updateWall } = wallStore();
const { addFloor, removeFloor, updateFloor } = floorStore();
const { addZone, removeZone, updateZone } = zoneStore();
const { addAisle, removeAisle, updateAisle } = aisleStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { socket } = useSocketStore();
const handleRedo = () => {
const redoData = peekRedo2D();
if (!redoData) return;
if (redoData.type === 'Draw') {
const { actions } = redoData;
actions.forEach(action => {
const { actionType } = action;
if ('point' in action) {
const point = action.point;
if (actionType === 'Line-Create') {
handleCreate(point);
} else if (actionType === 'Line-Update') {
handleUpdate(point);
} else if (actionType === 'Line-Delete') {
handleRemove(point);
}
} else if ('points' in action) {
const points = action.points;
if (actionType === 'Lines-Create') {
points.forEach(handleCreate);
} else if (actionType === 'Lines-Update') {
points.forEach(handleUpdate);
} else if (actionType === 'Lines-Delete') {
points.forEach(handleRemove);
}
}
});
} else if (redoData.type === 'UI') {
// Handle UI actions if needed
}
redo2D();
};
const handleCreate = (point: UndoRedo2DDataTypeSchema) => {
switch (point.type) {
case 'Wall': createWallFromBackend(point.lineData); break;
case 'Floor': createFloorFromBackend(point.lineData); break;
case 'Zone': createZoneFromBackend(point.lineData); break;
case 'Aisle': createAisleFromBackend(point.lineData); break;
}
};
const handleRemove = (point: UndoRedo2DDataTypeSchema) => {
switch (point.type) {
case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break;
case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break;
case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break;
case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break;
}
};
const handleUpdate = (point: UndoRedo2DDataTypeSchema) => {
if (!point.newData) return;
switch (point.type) {
case 'Wall': updateWallFromBackend(point.newData.wallUuid, point.newData); break;
case 'Floor': updateFloorFromBackend(point.newData.floorUuid, point.newData); break;
case 'Zone': updateZoneFromBackend(point.newData.zoneUuid, point.newData); break;
case 'Aisle': updateAisleFromBackend(point.newData.aisleUuid, point.newData); break;
}
};
const createWallFromBackend = (wallData: Wall) => {
addWall(wallData);
if (projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', wallData);
// SOCKET
const data = {
wallData: wallData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
};
const removeWallFromBackend = (wallUuid: string) => {
removeWall(wallUuid);
if (projectId) {
// API
// deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid);
// SOCKET
const data = {
wallUuid: wallUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:delete', data);
}
};
const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => {
updateWall(wallUuid, updatedData);
if (projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
wallData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
};
const createFloorFromBackend = (floorData: Floor) => {
addFloor(floorData);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData);
// SOCKET
const data = {
floorData: floorData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
const removeFloorFromBackend = (floorUuid: string) => {
removeFloor(floorUuid);
if (projectId) {
// API
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid);
// SOCKET
const data = {
floorUuid: floorUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:delete', data);
}
};
const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => {
updateFloor(floorUuid, updatedData);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
floorData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
const createZoneFromBackend = (zoneData: Zone) => {
addZone(zoneData);
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData);
// SOCKET
const data = {
zoneData: zoneData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
}
};
const removeZoneFromBackend = (zoneUuid: string) => {
removeZone(zoneUuid);
if (projectId) {
// API
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid);
// SOCKET
const data = {
zoneUuid,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:zone:delete', data);
}
};
const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => {
updateZone(zoneUuid, updatedData);
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
zoneData: updatedData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:zone:add', data);
}
};
const createAisleFromBackend = (aisleData: Aisle) => {
addAisle(aisleData);
if (projectId) {
// API
// upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData);
// SOCKET
const data = {
aisleData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:add', data);
}
};
const removeAisleFromBackend = (aisleUuid: string) => {
removeAisle(aisleUuid);
if (projectId) {
// API
// deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid);
// SOCKET
const data = {
aisleUuid,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:delete', data);
}
};
const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => {
updateAisle(aisleUuid, updatedData);
if (projectId) {
// API
// upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
aisleData: updatedData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:add', data);
}
};
return { handleRedo };
}
export default useRedoHandler;

View File

@@ -0,0 +1,356 @@
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useVersionContext } from "../../../../builder/version/versionContext";
import { useSceneContext } from "../../../sceneContext";
import { useSocketStore } from "../../../../../store/builder/store";
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi";
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi";
// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi";
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi";
function useUndoHandler() {
const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext();
const { undo2D, peekUndo2D } = undoRedo2DStore();
const { addWall, removeWall, updateWall } = wallStore();
const { addFloor, removeFloor, updateFloor } = floorStore();
const { addZone, removeZone, updateZone } = zoneStore();
const { addAisle, removeAisle, updateAisle } = aisleStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { socket } = useSocketStore();
const handleUndo = () => {
const unDoData = peekUndo2D();
if (!unDoData) return;
if (unDoData.type === 'Draw') {
const { actions } = unDoData;
actions.forEach(action => {
const { actionType } = action;
if ('point' in action) {
const point = action.point;
if (actionType === 'Line-Create') {
handleRemove(point);
} else if (actionType === 'Line-Update') {
handleUpdate(point);
} else if (actionType === 'Line-Delete') {
handleCreate(point);
}
} else if ('points' in action) {
const points = action.points;
if (actionType === 'Lines-Create') {
points.forEach(handleRemove);
} else if (actionType === 'Lines-Update') {
points.forEach(handleUpdate);
} else if (actionType === 'Lines-Delete') {
points.forEach(handleCreate);
}
}
});
} else if (unDoData.type === 'UI') {
// Handle UI actions if needed
}
undo2D();
};
const handleCreate = (point: UndoRedo2DDataTypeSchema) => {
switch (point.type) {
case 'Wall': createWallFromBackend(point.lineData); break;
case 'Floor': createFloorFromBackend(point.lineData); break;
case 'Zone': createZoneFromBackend(point.lineData); break;
case 'Aisle': createAisleFromBackend(point.lineData); break;
}
};
const handleRemove = (point: UndoRedo2DDataTypeSchema) => {
switch (point.type) {
case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break;
case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break;
case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break;
case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break;
}
};
const handleUpdate = (point: UndoRedo2DDataTypeSchema) => {
switch (point.type) {
case 'Wall': updateWallFromBackend(point.lineData.wallUuid, point.lineData); break;
case 'Floor': updateFloorFromBackend(point.lineData.floorUuid, point.lineData); break;
case 'Zone': updateZoneFromBackend(point.lineData.zoneUuid, point.lineData); break;
case 'Aisle': updateAisleFromBackend(point.lineData.aisleUuid, point.lineData); break;
}
};
const createWallFromBackend = (wallData: Wall) => {
addWall(wallData);
if (projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', wallData);
// SOCKET
const data = {
wallData: wallData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
};
const removeWallFromBackend = (wallUuid: string) => {
removeWall(wallUuid);
if (projectId) {
// API
// deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid);
// SOCKET
const data = {
wallUuid: wallUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:delete', data);
}
};
const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => {
updateWall(wallUuid, updatedData);
if (projectId) {
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
wallData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Wall:add', data);
}
};
const createFloorFromBackend = (floorData: Floor) => {
addFloor(floorData);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData);
// SOCKET
const data = {
floorData: floorData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
const removeFloorFromBackend = (floorUuid: string) => {
removeFloor(floorUuid);
if (projectId) {
// API
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid);
// SOCKET
const data = {
floorUuid: floorUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:delete', data);
}
};
const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => {
updateFloor(floorUuid, updatedData);
if (projectId) {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
floorData: updatedData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
}
};
const createZoneFromBackend = (zoneData: Zone) => {
addZone(zoneData);
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData);
// SOCKET
const data = {
zoneData: zoneData,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:zone:add', data);
}
};
const removeZoneFromBackend = (zoneUuid: string) => {
removeZone(zoneUuid);
if (projectId) {
// API
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid);
// SOCKET
const data = {
zoneUuid,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:zone:delete', data);
}
};
const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => {
updateZone(zoneUuid, updatedData);
if (projectId) {
// API
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
zoneData: updatedData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:zone:add', data);
}
};
const createAisleFromBackend = (aisleData: Aisle) => {
addAisle(aisleData);
if (projectId) {
// API
// upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData);
// SOCKET
const data = {
aisleData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:add', data);
}
};
const removeAisleFromBackend = (aisleUuid: string) => {
removeAisle(aisleUuid);
if (projectId) {
// API
// deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid);
// SOCKET
const data = {
aisleUuid,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:delete', data);
}
};
const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => {
updateAisle(aisleUuid, updatedData);
if (projectId) {
// API
// upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData);
// SOCKET
const data = {
aisleData: updatedData,
projectId,
versionId: selectedVersion?.versionId || '',
userId,
organization
};
socket.emit('v1:model-aisle:add', data);
}
};
return { handleUndo };
}
export default useUndoHandler;

View File

@@ -0,0 +1,49 @@
import { useEffect } from 'react'
import { useSceneContext } from '../../../sceneContext'
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
import { useSocketStore, useToggleView } from '../../../../../store/builder/store';
import { useVersionContext } from '../../../../builder/version/versionContext';
import useUndoHandler from '../handlers/useUndoHandler';
import useRedoHandler from '../handlers/useRedoHandler';
function UndoRedo2DControls() {
const { undoRedo2DStore } = useSceneContext();
const { undoStack, redoStack } = undoRedo2DStore();
const { toggleView } = useToggleView();
const { handleUndo } = useUndoHandler();
const { handleRedo } = useRedoHandler();
const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
useEffect(() => {
console.log(undoStack, redoStack);
}, [undoStack, redoStack]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === 'Ctrl+Z') {
handleUndo();
}
if (keyCombination === 'Ctrl+Y') {
handleRedo();
}
};
if (toggleView) {
window.addEventListener('keydown', handleKeyDown);
}
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [toggleView, undoStack, redoStack, socket, selectedVersion]);
return null;
}
export default UndoRedo2DControls;

View File

@@ -7,6 +7,8 @@ import { createAisleStore, AisleStoreType } from '../../store/builder/useAisleSt
import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore'; import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore';
import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore'; import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore';
import { createUndoRedo2DStore, UndoRedo2DStoreType } from '../../store/builder/useUndoRedo2DStore';
import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore'; import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore';
import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore'; import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore';
@@ -27,6 +29,8 @@ type SceneContextValue = {
zoneStore: ZoneStoreType, zoneStore: ZoneStoreType,
floorStore: FloorStoreType, floorStore: FloorStoreType,
undoRedo2DStore: UndoRedo2DStoreType,
eventStore: EventStoreType, eventStore: EventStoreType,
productStore: ProductStoreType, productStore: ProductStoreType,
@@ -62,6 +66,8 @@ export function SceneProvider({
const zoneStore = useMemo(() => createZoneStore(), []); const zoneStore = useMemo(() => createZoneStore(), []);
const floorStore = useMemo(() => createFloorStore(), []); const floorStore = useMemo(() => createFloorStore(), []);
const undoRedo2DStore = useMemo(() => createUndoRedo2DStore(), []);
const eventStore = useMemo(() => createEventStore(), []); const eventStore = useMemo(() => createEventStore(), []);
const productStore = useMemo(() => createProductStore(), []); const productStore = useMemo(() => createProductStore(), []);
@@ -82,6 +88,7 @@ export function SceneProvider({
aisleStore.getState().clearAisles(); aisleStore.getState().clearAisles();
zoneStore.getState().clearZones(); zoneStore.getState().clearZones();
floorStore.getState().clearFloors(); floorStore.getState().clearFloors();
undoRedo2DStore.getState().clearUndoRedo2D();
eventStore.getState().clearEvents(); eventStore.getState().clearEvents();
productStore.getState().clearProducts(); productStore.getState().clearProducts();
materialStore.getState().clearMaterials(); materialStore.getState().clearMaterials();
@@ -92,7 +99,7 @@ export function SceneProvider({
storageUnitStore.getState().clearStorageUnits(); storageUnitStore.getState().clearStorageUnits();
humanStore.getState().clearHumans(); humanStore.getState().clearHumans();
humanEventManagerRef.current.humanStates = []; humanEventManagerRef.current.humanStates = [];
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]); }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
const contextValue = useMemo(() => ( const contextValue = useMemo(() => (
{ {
@@ -102,6 +109,7 @@ export function SceneProvider({
aisleStore, aisleStore,
zoneStore, zoneStore,
floorStore, floorStore,
undoRedo2DStore,
eventStore, eventStore,
productStore, productStore,
materialStore, materialStore,
@@ -115,7 +123,7 @@ export function SceneProvider({
clearStores, clearStores,
layout layout
} }
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]); ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]);
return ( return (
<SceneContext.Provider value={contextValue}> <SceneContext.Provider value={contextValue}>

View File

@@ -28,7 +28,7 @@ interface FloorStore {
getFloorById: (uuid: string) => Floor | undefined; getFloorById: (uuid: string) => Floor | undefined;
getFloorsByPointId: (uuid: string) => Floor[] | []; getFloorsByPointId: (uuid: string) => Floor[] | [];
getFloorByPoints: (points: Point[]) => Floor | undefined; getFloorsByPoints: (points: [Point, Point]) => Floor[] | [];
getFloorPointById: (uuid: string) => Point | undefined; getFloorPointById: (uuid: string) => Point | undefined;
getConnectedPoints: (uuid: string) => Point[]; getConnectedPoints: (uuid: string) => Point[];
} }
@@ -74,10 +74,13 @@ export const createFloorStore = () => {
const updatedFloors: Floor[] = []; const updatedFloors: Floor[] = [];
set(state => { set(state => {
const newFloors: Floor[] = [];
for (const floor of state.floors) { for (const floor of state.floors) {
const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid); const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid);
if (pointIndex === -1) { if (pointIndex === -1) {
updatedFloors.push(JSON.parse(JSON.stringify(floor))); newFloors.push(floor);
continue; continue;
} }
@@ -87,11 +90,13 @@ export const createFloorStore = () => {
removedFloors.push(JSON.parse(JSON.stringify(floor))); removedFloors.push(JSON.parse(JSON.stringify(floor)));
continue; continue;
} }
floor.points = remainingPoints;
updatedFloors.push(JSON.parse(JSON.stringify(floor))); const updatedFloor = { ...floor, points: remainingPoints };
updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor)));
newFloors.push(updatedFloor);
} }
state.floors = updatedFloors; state.floors = newFloors;
}); });
return { removedFloors, updatedFloors }; return { removedFloors, updatedFloors };
@@ -102,6 +107,7 @@ export const createFloorStore = () => {
const updatedFloors: Floor[] = []; const updatedFloors: Floor[] = [];
set(state => { set(state => {
const newFloors: Floor[] = [];
for (const floor of state.floors) { for (const floor of state.floors) {
const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
@@ -110,7 +116,7 @@ export const createFloorStore = () => {
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
if (idxA === -1 || idxB === -1) { if (idxA === -1 || idxB === -1) {
updatedFloors.push(JSON.parse(JSON.stringify(floor))); newFloors.push(floor);
continue; continue;
} }
@@ -120,7 +126,7 @@ export const createFloorStore = () => {
(idxB === 0 && idxA === floor.points.length - 1); (idxB === 0 && idxA === floor.points.length - 1);
if (!areAdjacent) { if (!areAdjacent) {
updatedFloors.push(JSON.parse(JSON.stringify(floor))); newFloors.push(floor);
continue; continue;
} }
@@ -129,14 +135,15 @@ export const createFloorStore = () => {
); );
if (remainingPoints.length > 2) { if (remainingPoints.length > 2) {
floor.points = remainingPoints; const updatedFloor = { ...floor, points: remainingPoints };
updatedFloors.push(JSON.parse(JSON.stringify(floor))); updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor)));
newFloors.push(updatedFloor);
} else { } else {
removedFloors.push(JSON.parse(JSON.stringify(floor))); removedFloors.push(JSON.parse(JSON.stringify(floor)));
} }
} }
state.floors = updatedFloors; state.floors = newFloors;
}); });
return { removedFloors, updatedFloors }; return { removedFloors, updatedFloors };
@@ -253,12 +260,32 @@ export const createFloorStore = () => {
}); });
}, },
getFloorByPoints: (points) => { getFloorsByPoints: ([pointA, pointB]) => {
return get().floors.find(floor => { const Floors: Floor[] = [];
const floorPointIds = new Set(floor.points.map(p => p.pointUuid));
const givenPointIds = new Set(points.map(p => p.pointUuid)); for (const floor of get().floors) {
return floorPointIds.size === givenPointIds.size && [...floorPointIds].every(id => givenPointIds.has(id)); 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) {
continue;
}
const areAdjacent =
Math.abs(idxA - idxB) === 1 ||
(idxA === 0 && idxB === floor.points.length - 1) ||
(idxB === 0 && idxA === floor.points.length - 1);
if (!areAdjacent) {
continue;
}
Floors.push(JSON.parse(JSON.stringify(floor)));
}
return Floors;
}, },
getFloorPointById: (pointUuid) => { getFloorPointById: (pointUuid) => {

View File

@@ -0,0 +1,78 @@
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { undoRedoConfig } from '../../types/world/worldConstants';
type UndoRedo2DStore = {
undoStack: UndoRedo2DTypes[];
redoStack: UndoRedo2DTypes[];
push2D: (entry: UndoRedo2DTypes) => void;
undo2D: () => UndoRedo2DTypes | undefined;
redo2D: () => UndoRedo2DTypes | undefined;
clearUndoRedo2D: () => void;
peekUndo2D: () => UndoRedo2DTypes | undefined;
peekRedo2D: () => UndoRedo2DTypes | undefined;
};
export const createUndoRedo2DStore = () => {
return create<UndoRedo2DStore>()(
immer((set, get) => ({
undoStack: [],
redoStack: [],
push2D: (entry) => {
set((state) => {
state.undoStack.push(entry);
if (state.undoStack.length > undoRedoConfig.undoRedoCount) {
state.undoStack.shift();
}
state.redoStack = [];
});
},
undo2D: () => {
let lastAction: UndoRedo2DTypes | undefined;
set((state) => {
lastAction = state.undoStack.pop();
if (lastAction) {
state.redoStack.unshift(lastAction);
}
});
return lastAction;
},
redo2D: () => {
let redoAction: UndoRedo2DTypes | undefined;
set((state) => {
redoAction = state.redoStack.shift();
if (redoAction) {
state.undoStack.push(redoAction);
}
});
return redoAction;
},
clearUndoRedo2D: () => {
set((state) => {
state.undoStack = [];
state.redoStack = [];
});
},
peekUndo2D: () => {
const stack = get().undoStack;
return stack.length > 0 ? stack[stack.length - 1] : undefined;
},
peekRedo2D: () => {
const stack = get().redoStack;
return stack.length > 0 ? stack[0] : undefined;
},
}))
)
}
export type UndoRedo2DStoreType = ReturnType<typeof createUndoRedo2DStore>;

View File

@@ -21,7 +21,7 @@ interface ZoneStore {
getZoneById: (uuid: string) => Zone | undefined; getZoneById: (uuid: string) => Zone | undefined;
getZonesByPointId: (uuid: string) => Zone[] | []; getZonesByPointId: (uuid: string) => Zone[] | [];
getZoneByPoints: (points: Point[]) => Zone | undefined; getZonesByPoints: (points: Point[]) => Zone[] | [];
getZonePointById: (uuid: string) => Point | undefined; getZonePointById: (uuid: string) => Point | undefined;
getConnectedPoints: (uuid: string) => Point[]; getConnectedPoints: (uuid: string) => Point[];
} }
@@ -76,10 +76,13 @@ export const createZoneStore = () => {
const updatedZones: Zone[] = []; const updatedZones: Zone[] = [];
set(state => { set(state => {
const newZones: Zone[] = [];
for (const zone of state.zones) { for (const zone of state.zones) {
const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid); const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid);
if (pointIndex === -1) { if (pointIndex === -1) {
updatedZones.push(JSON.parse(JSON.stringify(zone))); newZones.push(zone);
continue; continue;
} }
@@ -89,11 +92,13 @@ export const createZoneStore = () => {
removedZones.push(JSON.parse(JSON.stringify(zone))); removedZones.push(JSON.parse(JSON.stringify(zone)));
continue; continue;
} }
zone.points = remainingPoints;
updatedZones.push(JSON.parse(JSON.stringify(zone))); const updatedZone = { ...zone, points: remainingPoints };
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
newZones.push(updatedZone);
} }
state.zones = updatedZones; state.zones = newZones;
}); });
return { removedZones, updatedZones }; return { removedZones, updatedZones };
@@ -104,6 +109,7 @@ export const createZoneStore = () => {
const updatedZones: Zone[] = []; const updatedZones: Zone[] = [];
set(state => { set(state => {
const newZones: Zone[] = [];
for (const zone of state.zones) { for (const zone of state.zones) {
const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
@@ -112,7 +118,7 @@ export const createZoneStore = () => {
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
if (idxA === -1 || idxB === -1) { if (idxA === -1 || idxB === -1) {
updatedZones.push(JSON.parse(JSON.stringify(zone))); newZones.push(zone);
continue; continue;
} }
@@ -122,7 +128,7 @@ export const createZoneStore = () => {
(idxB === 0 && idxA === zone.points.length - 1); (idxB === 0 && idxA === zone.points.length - 1);
if (!areAdjacent) { if (!areAdjacent) {
updatedZones.push(JSON.parse(JSON.stringify(zone))); newZones.push(zone);
continue; continue;
} }
@@ -131,14 +137,15 @@ export const createZoneStore = () => {
); );
if (remainingPoints.length > 2) { if (remainingPoints.length > 2) {
zone.points = remainingPoints; const updatedZone = { ...zone, points: remainingPoints };
updatedZones.push(JSON.parse(JSON.stringify(zone))); updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
newZones.push(updatedZone);
} else { } else {
removedZones.push(JSON.parse(JSON.stringify(zone))); removedZones.push(JSON.parse(JSON.stringify(zone)));
} }
} }
state.zones = updatedZones; state.zones = newZones;
}); });
return { removedZones, updatedZones }; return { removedZones, updatedZones };
@@ -180,12 +187,32 @@ export const createZoneStore = () => {
}); });
}, },
getZoneByPoints: (points) => { getZonesByPoints: ([pointA, pointB]) => {
return get().zones.find(zone => { const Zones: Zone[] = [];
const zonePointIds = new Set(zone.points.map(p => p.pointUuid));
const givenPointIds = new Set(points.map(p => p.pointUuid)); for (const zone of get().zones) {
return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id)); const indices = zone.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) {
continue;
}
const areAdjacent =
Math.abs(idxA - idxB) === 1 ||
(idxA === 0 && idxB === zone.points.length - 1) ||
(idxB === 0 && idxA === zone.points.length - 1);
if (!areAdjacent) {
continue;
}
Zones.push(JSON.parse(JSON.stringify(zone)));
}
return Zones;
}, },
getZonePointById: (pointUuid) => { getZonePointById: (pointUuid) => {

View File

@@ -215,3 +215,41 @@ interface Aisle {
} }
type Aisles = Aisle[]; type Aisles = Aisle[];
// Undo/Redo 2D
type UndoRedo2DDataTypeSchema =
| { type: 'Wall'; lineData: Wall; newData?: Wall; timeStamp: string }
| { type: 'Floor'; lineData: Floor; newData?: Floor; timeStamp: string }
| { type: 'Zone'; lineData: Zone; newData?: Zone; timeStamp: string }
| { type: 'Aisle'; lineData: Aisle; newData?: Aisle; timeStamp: string };
type UndoRedo2DLineActionSchema = {
actionType: 'Line-Create' | 'Line-Update' | 'Line-Delete';
point: UndoRedo2DDataTypeSchema;
}
type UndoRedo2DLinesActionSchema = {
actionType: 'Lines-Create' | 'Lines-Update' | 'Lines-Delete';
points: UndoRedo2DDataTypeSchema[];
}
type UndoRedo2DAction = UndoRedo2DLineActionSchema | UndoRedo2DLinesActionSchema;
type UndoRedo2DDraw = {
type: 'Draw';
actions: UndoRedo2DAction[];
};
type UndoRedo2DUi = {
type: 'UI';
action: any; // Define UI actions as needed
}
type UndoRedo2DTypes = UndoRedo2DDraw | UndoRedo2DUi
type UndoRedo2D = {
undoStack: UndoRedo2DTypes[];
redoStack: UndoRedo2DTypes[];
};

View File

@@ -1,375 +1,383 @@
const savedTheme: string | null = localStorage.getItem("theme"); const savedTheme: string | null = localStorage.getItem("theme");
export type Controls = { export type Controls = {
azimuthRotateSpeed: number; azimuthRotateSpeed: number;
polarRotateSpeed: number; polarRotateSpeed: number;
truckSpeed: number; truckSpeed: number;
minDistance: number; minDistance: number;
maxDistance: number; maxDistance: number;
maxPolarAngle: number; maxPolarAngle: number;
leftMouse: number; leftMouse: number;
forwardSpeed: number; forwardSpeed: number;
backwardSpeed: number; backwardSpeed: number;
leftSpeed: number; leftSpeed: number;
rightSpeed: number; rightSpeed: number;
}; };
export type ThirdPersonControls = { export type ThirdPersonControls = {
azimuthRotateSpeed: number; azimuthRotateSpeed: number;
polarRotateSpeed: number; polarRotateSpeed: number;
truckSpeed: number; truckSpeed: number;
maxDistance: number; maxDistance: number;
maxPolarAngle: number; maxPolarAngle: number;
minZoom: number; minZoom: number;
maxZoom: number; maxZoom: number;
targetOffset: number; targetOffset: number;
cameraHeight: number; cameraHeight: number;
leftMouse: number; leftMouse: number;
rightMouse: number; rightMouse: number;
wheelMouse: number; wheelMouse: number;
middleMouse: number; middleMouse: number;
}; };
export type ControlsTransition = { export type ControlsTransition = {
leftMouse: number; leftMouse: number;
rightMouse: number; rightMouse: number;
wheelMouse: number; wheelMouse: number;
middleMouse: number; middleMouse: number;
}; };
export type TwoDimension = { export type TwoDimension = {
defaultPosition: [x: number, y: number, z: number]; defaultPosition: [x: number, y: number, z: number];
defaultTarget: [x: number, y: number, z: number]; defaultTarget: [x: number, y: number, z: number];
defaultAzimuth: number; defaultAzimuth: number;
minDistance: number; minDistance: number;
leftMouse: number; leftMouse: number;
rightMouse: number; rightMouse: number;
}; };
export type ThreeDimension = { export type ThreeDimension = {
defaultPosition: [x: number, y: number, z: number]; defaultPosition: [x: number, y: number, z: number];
defaultTarget: [x: number, y: number, z: number]; defaultTarget: [x: number, y: number, z: number];
defaultRotation: [x: number, y: number, z: number]; defaultRotation: [x: number, y: number, z: number];
defaultAzimuth: number; defaultAzimuth: number;
boundaryBottom: [x: number, y: number, z: number]; boundaryBottom: [x: number, y: number, z: number];
boundaryTop: [x: number, y: number, z: number]; boundaryTop: [x: number, y: number, z: number];
minDistance: number; minDistance: number;
leftMouse: number; leftMouse: number;
rightMouse: number; rightMouse: number;
}; };
export type GridConfig = { export type GridConfig = {
size: number; size: number;
divisions: number; divisions: number;
primaryColor: string; primaryColor: string;
secondaryColor: string; secondaryColor: string;
position2D: [x: number, y: number, z: number]; position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number];
}; };
export type PlaneConfig = { export type PlaneConfig = {
position2D: [x: number, y: number, z: number]; position2D: [x: number, y: number, z: number];
position3D: [x: number, y: number, z: number]; position3D: [x: number, y: number, z: number];
rotation: number; rotation: number;
width: number; width: number;
height: number; height: number;
color: string; color: string;
}; };
export type ShadowConfig = { export type ShadowConfig = {
shadowOffset: number; shadowOffset: number;
shadowmapSizewidth: number; shadowmapSizewidth: number;
shadowmapSizeheight: number; shadowmapSizeheight: number;
shadowcamerafar: number; shadowcamerafar: number;
shadowcameranear: number; shadowcameranear: number;
shadowcameratop: number; shadowcameratop: number;
shadowcamerabottom: number; shadowcamerabottom: number;
shadowcameraleft: number; shadowcameraleft: number;
shadowcameraright: number; shadowcameraright: number;
shadowbias: number; shadowbias: number;
shadownormalBias: number; shadownormalBias: number;
shadowMaterialPosition: [x: number, y: number, z: number]; shadowMaterialPosition: [x: number, y: number, z: number];
shadowMaterialRotation: [x: number, y: number, z: number]; shadowMaterialRotation: [x: number, y: number, z: number];
shadowMaterialOpacity: number; shadowMaterialOpacity: number;
}; };
export type SkyConfig = { export type SkyConfig = {
defaultTurbidity: number; defaultTurbidity: number;
maxTurbidity: number; maxTurbidity: number;
minTurbidity: number; minTurbidity: number;
defaultRayleigh: number; defaultRayleigh: number;
mieCoefficient: number; mieCoefficient: number;
mieDirectionalG: number; mieDirectionalG: number;
skyDistance: number; skyDistance: number;
}; };
export type AssetConfig = { export type AssetConfig = {
defaultScaleBeforeGsap: [number, number, number]; defaultScaleBeforeGsap: [number, number, number];
defaultScaleAfterGsap: [number, number, number]; defaultScaleAfterGsap: [number, number, number];
}; };
export type PointConfig = { export type PointConfig = {
defaultInnerColor: string; defaultInnerColor: string;
defaultOuterColor: string; defaultOuterColor: string;
deleteColor: string; deleteColor: string;
boxScale: [number, number, number]; boxScale: [number, number, number];
wallOuterColor: string; wallOuterColor: string;
floorOuterColor: string; floorOuterColor: string;
aisleOuterColor: string; aisleOuterColor: string;
zoneOuterColor: string; zoneOuterColor: string;
snappingThreshold: number; snappingThreshold: number;
helperColor: string; helperColor: string;
}; };
export type LineConfig = { export type LineConfig = {
tubularSegments: number; tubularSegments: number;
radius: number; radius: number;
radialSegments: number; radialSegments: number;
wallName: string; wallName: string;
floorName: string; floorName: string;
aisleName: string; aisleName: string;
zoneName: string; zoneName: string;
referenceName: string; referenceName: string;
lineIntersectionPoints: number; lineIntersectionPoints: number;
defaultColor: string; defaultColor: string;
wallColor: string; wallColor: string;
floorColor: string; floorColor: string;
aisleColor: string; aisleColor: string;
zoneColor: string; zoneColor: string;
deleteColor: string; deleteColor: string;
helperColor: string; helperColor: string;
}; };
export type WallConfig = { export type WallConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
width: number; width: number;
}; };
export type FloorConfig = { export type FloorConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
textureScale: number; textureScale: number;
}; };
export type RoofConfig = { export type RoofConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
}; };
export type AisleConfig = { export type AisleConfig = {
width: number; width: number;
height: number; height: number;
defaultColor: string; defaultColor: string;
}; };
export type ZoneConfig = { export type ZoneConfig = {
defaultColor: string; defaultColor: string;
height: number; height: number;
color: string; color: string;
}; };
export type ColumnConfig = { export type ColumnConfig = {
defaultColor: string; defaultColor: string;
}; };
export type OutlineConfig = { export type OutlineConfig = {
assetSelectColor: number; assetSelectColor: number;
assetDeleteColor: number; assetDeleteColor: number;
}; };
export type DistanceConfig = { export type DistanceConfig = {
minDistance: number; minDistance: number;
maxDistance: number; maxDistance: number;
};
export type undoRedoCount = {
undoRedoCount: number;
}; };
export const firstPersonControls: Controls = { export const firstPersonControls: Controls = {
azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis
polarRotateSpeed: 0.3, // Speed of rotation around the polar axis polarRotateSpeed: 0.3, // Speed of rotation around the polar axis
truckSpeed: 10, // Speed of truck movement truckSpeed: 10, // Speed of truck movement
minDistance: 0, // Minimum distance from the target minDistance: 0, // Minimum distance from the target
maxDistance: 0, // Maximum distance from the target maxDistance: 0, // Maximum distance from the target
maxPolarAngle: Math.PI, // Maximum polar angle maxPolarAngle: Math.PI, // Maximum polar angle
leftMouse: 1, // Mouse button for rotation (ROTATE) leftMouse: 1, // Mouse button for rotation (ROTATE)
forwardSpeed: 0.1, // Speed of forward movement forwardSpeed: 0.1, // Speed of forward movement
backwardSpeed: -0.1, // Speed of backward movement backwardSpeed: -0.1, // Speed of backward movement
leftSpeed: -0.1, // Speed of left movement leftSpeed: -0.1, // Speed of left movement
rightSpeed: 0.1, // Speed of right movement rightSpeed: 0.1, // Speed of right movement
}; };
export const thirdPersonControls: ThirdPersonControls = { export const thirdPersonControls: ThirdPersonControls = {
azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis
polarRotateSpeed: 1, // Speed of rotation around the polar axis polarRotateSpeed: 1, // Speed of rotation around the polar axis
truckSpeed: 2, // Speed of truck movement truckSpeed: 2, // Speed of truck movement
maxDistance: 100, // Maximum distance from the target maxDistance: 100, // Maximum distance from the target
maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle
minZoom: 6, // Minimum zoom level minZoom: 6, // Minimum zoom level
maxZoom: 100, // Maximum zoom level maxZoom: 100, // Maximum zoom level
targetOffset: 20, // Offset of the target from the camera targetOffset: 20, // Offset of the target from the camera
cameraHeight: 30, // Height of the camera cameraHeight: 30, // Height of the camera
leftMouse: 2, // Mouse button for panning leftMouse: 2, // Mouse button for panning
rightMouse: 1, // Mouse button for rotation rightMouse: 1, // Mouse button for rotation
wheelMouse: 8, // Mouse button for zooming wheelMouse: 8, // Mouse button for zooming
middleMouse: 8, // Mouse button for zooming middleMouse: 8, // Mouse button for zooming
}; };
export const controlsTransition: ControlsTransition = { export const controlsTransition: ControlsTransition = {
leftMouse: 0, // Mouse button for no action leftMouse: 0, // Mouse button for no action
rightMouse: 0, // Mouse button for no action rightMouse: 0, // Mouse button for no action
wheelMouse: 0, // Mouse button for no action wheelMouse: 0, // Mouse button for no action
middleMouse: 0, // Mouse button for no action middleMouse: 0, // Mouse button for no action
}; };
export const twoDimension: TwoDimension = { export const twoDimension: TwoDimension = {
defaultPosition: [0, 100, 0], // Default position of the camera defaultPosition: [0, 100, 0], // Default position of the camera
defaultTarget: [0, 0, 0], // Default target of the camera defaultTarget: [0, 0, 0], // Default target of the camera
defaultAzimuth: 0, // Default azimuth of the camera defaultAzimuth: 0, // Default azimuth of the camera
minDistance: 25, // Minimum distance from the target minDistance: 25, // Minimum distance from the target
leftMouse: 2, // Mouse button for panning leftMouse: 2, // Mouse button for panning
rightMouse: 0, // Mouse button for no action rightMouse: 0, // Mouse button for no action
}; };
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
export const gridConfig: GridConfig = { export const gridConfig: GridConfig = {
size: 150, // Size of the grid size: 150, // Size of the grid
divisions: 75, // Number of divisions in the grid divisions: 75, // Number of divisions in the grid
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
position2D: [0, 0.1, 0], // Position of the grid in 2D view position2D: [0, 0.1, 0], // Position of the grid in 2D view
position3D: [0, -0.5, 0], // Position of the grid in 3D view position3D: [0, -0.5, 0], // Position of the grid in 3D view
}; };
export const threeDimension: ThreeDimension = { export const threeDimension: ThreeDimension = {
defaultPosition: [0, 40, 30], // Default position of the camera defaultPosition: [0, 40, 30], // Default position of the camera
defaultTarget: [0, 0, 0], // Default target of the camera defaultTarget: [0, 0, 0], // Default target of the camera
defaultRotation: [0, 0, 0], // Default rotation of the camera defaultRotation: [0, 0, 0], // Default rotation of the camera
defaultAzimuth: 0, // Default azimuth of the camera defaultAzimuth: 0, // Default azimuth of the camera
boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement
boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement
minDistance: 1, // Minimum distance from the target minDistance: 1, // Minimum distance from the target
leftMouse: 2, // Mouse button for panning leftMouse: 2, // Mouse button for panning
rightMouse: 1, // Mouse button for rotation rightMouse: 1, // Mouse button for rotation
}; };
export const planeConfig: PlaneConfig = { export const planeConfig: PlaneConfig = {
position2D: [0, -0.5, 0], // Position of the plane position2D: [0, -0.5, 0], // Position of the plane
position3D: [0, -0.65, 0], // Position of the plane position3D: [0, -0.65, 0], // Position of the plane
rotation: -Math.PI / 2, // Rotation of the plane rotation: -Math.PI / 2, // Rotation of the plane
width: 150, // Width of the plane width: 150, // Width of the plane
height: 150, // Height of the plane height: 150, // Height of the plane
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
}; };
export const shadowConfig: ShadowConfig = { export const shadowConfig: ShadowConfig = {
shadowOffset: 50, // Offset of the shadow shadowOffset: 50, // Offset of the shadow
// shadowmapSizewidth: 1024, // Width of the shadow map // shadowmapSizewidth: 1024, // Width of the shadow map
// shadowmapSizeheight: 1024, // Height of the shadow map // shadowmapSizeheight: 1024, // Height of the shadow map
shadowmapSizewidth: 2048, // Width of the shadow map shadowmapSizewidth: 2048, // Width of the shadow map
shadowmapSizeheight: 2048, // Height of the shadow map shadowmapSizeheight: 2048, // Height of the shadow map
// shadowmapSizewidth: 8192, // Width of the shadow map // shadowmapSizewidth: 8192, // Width of the shadow map
// shadowmapSizeheight: 8192, // Height of the shadow map // shadowmapSizeheight: 8192, // Height of the shadow map
shadowcamerafar: 70, // Far plane of the shadow camera shadowcamerafar: 70, // Far plane of the shadow camera
shadowcameranear: 0.1, // Near plane of the shadow camera shadowcameranear: 0.1, // Near plane of the shadow camera
shadowcameratop: 30, // Top plane of the shadow camera shadowcameratop: 30, // Top plane of the shadow camera
shadowcamerabottom: -30, // Bottom plane of the shadow camera shadowcamerabottom: -30, // Bottom plane of the shadow camera
shadowcameraleft: -30, // Left plane of the shadow camera shadowcameraleft: -30, // Left plane of the shadow camera
shadowcameraright: 30, // Right plane of the shadow camera shadowcameraright: 30, // Right plane of the shadow camera
shadowbias: -0.001, // Bias of the shadow shadowbias: -0.001, // Bias of the shadow
shadownormalBias: 0.02, // Normal bias of the shadow shadownormalBias: 0.02, // Normal bias of the shadow
shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material
shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material
shadowMaterialOpacity: 0.1, // Opacity of the shadow material shadowMaterialOpacity: 0.1, // Opacity of the shadow material
}; };
export const skyConfig: SkyConfig = { export const skyConfig: SkyConfig = {
defaultTurbidity: 10.0, // Default turbidity of the sky defaultTurbidity: 10.0, // Default turbidity of the sky
maxTurbidity: 20.0, // Maximum turbidity of the sky maxTurbidity: 20.0, // Maximum turbidity of the sky
minTurbidity: 0.0, // Minimum turbidity of the sky minTurbidity: 0.0, // Minimum turbidity of the sky
defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient
mieCoefficient: 0.1, // Mie scattering coefficient mieCoefficient: 0.1, // Mie scattering coefficient
mieDirectionalG: 1.0, // Mie directional G mieDirectionalG: 1.0, // Mie directional G
skyDistance: 2000, // Distance of the sky skyDistance: 2000, // Distance of the sky
}; };
export const assetConfig: AssetConfig = { export const assetConfig: AssetConfig = {
defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets
defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets
}; };
export const pointConfig: PointConfig = { export const pointConfig: PointConfig = {
defaultInnerColor: "#ffffff", // Default inner color of the points defaultInnerColor: "#ffffff", // Default inner color of the points
defaultOuterColor: "#ffffff", // Default outer color of the points defaultOuterColor: "#ffffff", // Default outer color of the points
deleteColor: "#ff0000", // Color of the points when deleting deleteColor: "#ff0000", // Color of the points when deleting
boxScale: [0.5, 0.5, 0.5], // Scale of the points boxScale: [0.5, 0.5, 0.5], // Scale of the points
wallOuterColor: "#C7C7C7", // Outer color of the wall points wallOuterColor: "#C7C7C7", // Outer color of the wall points
floorOuterColor: "#808080", // Outer color of the floor points floorOuterColor: "#808080", // Outer color of the floor points
aisleOuterColor: "#FBBC05", // Outer color of the aisle points aisleOuterColor: "#FBBC05", // Outer color of the aisle points
zoneOuterColor: "#007BFF", // Outer color of the zone points zoneOuterColor: "#007BFF", // Outer color of the zone points
snappingThreshold: 1, // Threshold for snapping snappingThreshold: 1, // Threshold for snapping
helperColor: "#C164FF", // Color of the helper lines helperColor: "#C164FF", // Color of the helper lines
}; };
export const lineConfig: LineConfig = { export const lineConfig: LineConfig = {
tubularSegments: 64, // Number of tubular segments tubularSegments: 64, // Number of tubular segments
radius: 0.15, // Radius of the lines radius: 0.15, // Radius of the lines
radialSegments: 8, // Number of radial segments radialSegments: 8, // Number of radial segments
wallName: "WallLine", // Name of the wall lines wallName: "WallLine", // Name of the wall lines
floorName: "FloorLine", // Name of the floor lines floorName: "FloorLine", // Name of the floor lines
aisleName: "AisleLine", // Name of the aisle lines aisleName: "AisleLine", // Name of the aisle lines
zoneName: "ZoneLine", // Name of the zone lines zoneName: "ZoneLine", // Name of the zone lines
referenceName: "ReferenceLine", // Name of the reference lines referenceName: "ReferenceLine", // Name of the reference lines
lineIntersectionPoints: 300, // Number of intersection points lineIntersectionPoints: 300, // Number of intersection points
defaultColor: "#000000", // Default color of the lines defaultColor: "#000000", // Default color of the lines
wallColor: "#C7C7C7", // Color of the wall lines wallColor: "#C7C7C7", // Color of the wall lines
floorColor: "#808080", // Color of the floor lines floorColor: "#808080", // Color of the floor lines
aisleColor: "#FBBC05", // Color of the aisle lines aisleColor: "#FBBC05", // Color of the aisle lines
zoneColor: "#007BFF", // Color of the zone lines zoneColor: "#007BFF", // Color of the zone lines
deleteColor: "#ff0000", // Color of the line when deleting deleteColor: "#ff0000", // Color of the line when deleting
helperColor: "#C164FF", // Color of the helper lines helperColor: "#C164FF", // Color of the helper lines
}; };
export const wallConfig: WallConfig = { export const wallConfig: WallConfig = {
defaultColor: "#f2f2f2", // Default color of the walls defaultColor: "#f2f2f2", // Default color of the walls
height: 7.5, // Height of the walls height: 7.5, // Height of the walls
width: 0.05, // Width of the walls width: 0.05, // Width of the walls
}; };
export const floorConfig: FloorConfig = { export const floorConfig: FloorConfig = {
defaultColor: "grey", // Default color of the floors defaultColor: "grey", // Default color of the floors
height: 0.1, // Height of the floors height: 0.1, // Height of the floors
textureScale: 1, // Scale of the floor texture textureScale: 1, // Scale of the floor texture
}; };
export const roofConfig: RoofConfig = { export const roofConfig: RoofConfig = {
defaultColor: "grey", // Default color of the roofs defaultColor: "grey", // Default color of the roofs
height: 0.1, // Height of the roofs height: 0.1, // Height of the roofs
}; };
export const aisleConfig: AisleConfig = { export const aisleConfig: AisleConfig = {
width: 0.1, // Width of the aisles width: 0.1, // Width of the aisles
height: 0.01, // Height of the aisles height: 0.01, // Height of the aisles
defaultColor: '#E2AC09', // Default color of the aisles defaultColor: '#E2AC09', // Default color of the aisles
}; };
export const zoneConfig: ZoneConfig = { export const zoneConfig: ZoneConfig = {
defaultColor: "black", // Default color of the zones defaultColor: "black", // Default color of the zones
height: 3, height: 3,
color: "#8656DF", // Color of the zones color: "#8656DF", // Color of the zones
}; };
export const columnConfig: ColumnConfig = { export const columnConfig: ColumnConfig = {
defaultColor: "White", // Default color of the columns defaultColor: "White", // Default color of the columns
}; };
export const outlineConfig: OutlineConfig = { export const outlineConfig: OutlineConfig = {
assetSelectColor: 0x0054fe, // Color of the selected assets assetSelectColor: 0x0054fe, // Color of the selected assets
assetDeleteColor: 0xff0000, // Color of the deleted assets assetDeleteColor: 0xff0000, // Color of the deleted assets
}; };
export const distanceConfig: DistanceConfig = { export const distanceConfig: DistanceConfig = {
minDistance: 20, minDistance: 20,
maxDistance: 75, maxDistance: 75,
}; };
export const undoRedoConfig: undoRedoCount = {
undoRedoCount: 50,
}