Merge remote-tracking branch 'origin/main-dev' into main-demo
This commit is contained in:
48
app/package-lock.json
generated
48
app/package-lock.json
generated
@@ -2026,7 +2026,7 @@
|
|||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
},
|
},
|
||||||
@@ -2038,7 +2038,7 @@
|
|||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
@@ -4180,6 +4180,26 @@
|
|||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@testing-library/dom": {
|
||||||
|
"version": "10.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||||
|
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/aria-query": "^5.0.1",
|
||||||
|
"aria-query": "5.3.0",
|
||||||
|
"dom-accessibility-api": "^0.5.9",
|
||||||
|
"lz-string": "^1.5.0",
|
||||||
|
"picocolors": "1.1.1",
|
||||||
|
"pretty-format": "^27.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/jest-dom": {
|
"node_modules/@testing-library/jest-dom": {
|
||||||
"version": "5.17.0",
|
"version": "5.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
|
||||||
@@ -4291,25 +4311,25 @@
|
|||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node12": {
|
"node_modules/@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node14": {
|
"node_modules/@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node16": {
|
"node_modules/@tsconfig/node16": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@turf/along": {
|
"node_modules/@turf/along": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
@@ -9063,7 +9083,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
@@ -9940,7 +9960,7 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
@@ -15324,7 +15344,7 @@
|
|||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
@@ -20801,7 +20821,7 @@
|
|||||||
"version": "10.9.2",
|
"version": "10.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
@@ -20844,7 +20864,7 @@
|
|||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.11.0"
|
"acorn": "^8.11.0"
|
||||||
},
|
},
|
||||||
@@ -20856,7 +20876,7 @@
|
|||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
@@ -21352,7 +21372,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul": {
|
"node_modules/v8-to-istanbul": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
@@ -22411,7 +22431,7 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useToggleView } from '../../../../store/builder/store';
|
import { useToggleView } from '../../../../store/builder/store';
|
||||||
import AisleInstance from './instance/aisleInstance';
|
import AisleInstance from './instance/aisleInstance';
|
||||||
import Point from '../../point/point';
|
import Point from '../../point/point';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { Extrude } from '@react-three/drei';
|
import { Instances, Instance } from '@react-three/drei';
|
||||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||||
import { useToolMode } from '../../../../../../store/builder/store';
|
import { useToolMode } from '../../../../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||||
@@ -10,8 +10,16 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||||
|
|
||||||
const arrows = useMemo(() => {
|
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return [];
|
const result = {
|
||||||
|
arrowGeometry: null as THREE.ExtrudeGeometry | null,
|
||||||
|
arrowInstances: [] as {
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
}[],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return result;
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
@@ -24,69 +32,67 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
direction.normalize();
|
direction.normalize();
|
||||||
|
|
||||||
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
||||||
|
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||||
|
|
||||||
const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = [];
|
const shape = new THREE.Shape();
|
||||||
|
const w = width * 0.8;
|
||||||
|
const h = arrowLength;
|
||||||
|
|
||||||
|
shape.moveTo(0, 0);
|
||||||
|
shape.lineTo(w, h * 0.6);
|
||||||
|
shape.lineTo(w * 0.4, h * 0.6);
|
||||||
|
shape.lineTo(w * 0.4, h);
|
||||||
|
shape.lineTo(-w * 0.4, h);
|
||||||
|
shape.lineTo(-w * 0.4, h * 0.6);
|
||||||
|
shape.lineTo(-w, h * 0.6);
|
||||||
|
shape.lineTo(0, 0);
|
||||||
|
|
||||||
|
result.arrowGeometry = new THREE.ExtrudeGeometry(shape, {
|
||||||
|
depth: 0.01,
|
||||||
|
bevelEnabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
result.arrowGeometry.rotateX(Math.PI / 2);
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const initialOffset = arrowLength;
|
const offset = arrowLength + i * (arrowLength + spacing);
|
||||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing));
|
const center = new THREE.Vector3().copy(start).addScaledVector(direction, offset);
|
||||||
|
|
||||||
const shape = new THREE.Shape();
|
result.arrowInstances.push({
|
||||||
const w = width * 0.8;
|
position: [center.x, 0, center.z],
|
||||||
const h = arrowLength;
|
rotation: [0, angle, 0],
|
||||||
|
});
|
||||||
shape.moveTo(0, 0);
|
|
||||||
shape.lineTo(w, h * 0.6);
|
|
||||||
shape.lineTo(w * 0.4, h * 0.6);
|
|
||||||
shape.lineTo(w * 0.4, h);
|
|
||||||
shape.lineTo(-w * 0.4, h);
|
|
||||||
shape.lineTo(-w * 0.4, h * 0.6);
|
|
||||||
shape.lineTo(-w, h * 0.6);
|
|
||||||
shape.lineTo(0, 0);
|
|
||||||
|
|
||||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
|
||||||
|
|
||||||
arrowShapes.push({ shape, position: center, rotationY: angle });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrowShapes;
|
return result;
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (toolMode === 'move' && !hoveredPoint) {
|
if (toolMode === 'move' && !hoveredPoint) {
|
||||||
setSelectedAisle(aisleRef.current);
|
setSelectedAisle(aisleRef.current);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (arrows.length === 0) return null;
|
if (!arrowGeometry || arrowInstances.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
name='Arrows-Aisle'
|
name="Arrows-Aisle"
|
||||||
uuid={aisle.aisleUuid}
|
uuid={aisle.aisleUuid}
|
||||||
ref={aisleRef}
|
ref={aisleRef}
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
userData={aisle}
|
userData={aisle}
|
||||||
onDoubleClick={handleClick}
|
onDoubleClick={handleClick}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
setSelectedAisle(null);
|
setSelectedAisle(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{arrows.map(({ shape, position, rotationY }, index) => (
|
<Instances geometry={arrowGeometry} frustumCulled={false}>
|
||||||
<group key={index} position={[position.x, position.z, 0]} rotation={[0, 0, -rotationY]}>
|
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} side={THREE.DoubleSide} />
|
||||||
<Extrude
|
{arrowInstances.map(({ position, rotation }, i) => (
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
<Instance key={i} position={position} rotation={rotation} />
|
||||||
receiveShadow
|
))}
|
||||||
castShadow
|
</Instances>
|
||||||
>
|
|
||||||
<meshStandardMaterial
|
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
|
||||||
</Extrude>
|
|
||||||
</group>
|
|
||||||
))}
|
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { Extrude } from '@react-three/drei';
|
import { Instances, Instance } from '@react-three/drei';
|
||||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||||
import { useToolMode } from '../../../../../../store/builder/store';
|
import { useToolMode } from '../../../../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||||
@@ -10,82 +10,71 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||||
|
|
||||||
const shapes = useMemo(() => {
|
const dashInstances = useMemo(() => {
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
|
|
||||||
const width = aisle.type.aisleWidth || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
const dashLength = aisle.type.dashLength || 0.5;
|
const dashLength = aisle.type.dashLength || 0.5;
|
||||||
const gapLength = aisle.type.gapLength || 0.3;
|
const gapLength = aisle.type.gapLength || 0.3;
|
||||||
|
|
||||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
const totalVec = new THREE.Vector3().subVectors(end, start);
|
||||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
const totalLength = totalVec.length();
|
||||||
|
const direction = totalVec.clone().normalize();
|
||||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
|
||||||
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
||||||
|
|
||||||
const shapes = [];
|
const instances = [];
|
||||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
|
||||||
|
|
||||||
for (let i = 0; i < segmentCount; i++) {
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength));
|
const center = start.clone().addScaledVector(direction, i * (dashLength + gapLength) + dashLength / 2);
|
||||||
const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength);
|
|
||||||
|
|
||||||
const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2);
|
const rotationY = Math.atan2(direction.x, direction.z);
|
||||||
const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2);
|
|
||||||
const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2);
|
|
||||||
const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2);
|
|
||||||
|
|
||||||
const shape = new THREE.Shape();
|
instances.push({
|
||||||
shape.moveTo(leftStart.x, leftStart.z);
|
position: [center.x, 0, center.z] as [number, number, number],
|
||||||
shape.lineTo(leftEnd.x, leftEnd.z);
|
scale: [width, 0.001, dashLength] as [number, number, number],
|
||||||
shape.lineTo(rightEnd.x, rightEnd.z);
|
rotation: [0, rotationY, 0] as [number, number, number],
|
||||||
shape.lineTo(rightStart.x, rightStart.z);
|
});
|
||||||
shape.closePath();
|
|
||||||
|
|
||||||
shapes.push(shape);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shapes;
|
return instances;
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (toolMode === 'move' && !hoveredPoint) {
|
if (toolMode === 'move' && !hoveredPoint) {
|
||||||
setSelectedAisle(aisleRef.current);
|
setSelectedAisle(aisleRef.current);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (shapes.length === 0) return null;
|
if (dashInstances.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
name='Dashed-Aisle'
|
name="Dashed-Aisle"
|
||||||
uuid={aisle.aisleUuid}
|
uuid={aisle.aisleUuid}
|
||||||
ref={aisleRef}
|
ref={aisleRef}
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
userData={aisle}
|
|
||||||
onDoubleClick={handleClick}
|
onDoubleClick={handleClick}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
setSelectedAisle(null);
|
setSelectedAisle(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{shapes.map((shape, index) => (
|
<Instances frustumCulled={false}>
|
||||||
<Extrude
|
<boxGeometry args={[1, 1, 1]} />
|
||||||
key={index}
|
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} />
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
{dashInstances.map((inst, i) => (
|
||||||
receiveShadow
|
<Instance
|
||||||
castShadow
|
key={i}
|
||||||
>
|
position={inst.position}
|
||||||
<meshStandardMaterial
|
scale={inst.scale}
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
rotation={inst.rotation}
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
))}
|
||||||
))}
|
</Instances>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DashedAisle;
|
export default DashedAisle;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { Extrude } from '@react-three/drei';
|
import { Instance, Instances } from '@react-three/drei';
|
||||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||||
import { useToolMode } from '../../../../../../store/builder/store';
|
import { useToolMode } from '../../../../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||||
@@ -10,31 +10,20 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||||
|
|
||||||
const shapes = useMemo(() => {
|
const dotPositions = useMemo(() => {
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.dotRadius || 0.1;
|
|
||||||
const dotSpacing = aisle.type.gapLength || 0.5;
|
const dotSpacing = aisle.type.gapLength || 0.5;
|
||||||
const dotRadius = width * 0.6;
|
|
||||||
|
|
||||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||||
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing);
|
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing);
|
||||||
|
|
||||||
const shapes = [];
|
|
||||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
|
|
||||||
for (let i = 0; i < dotCount; i++) {
|
return Array.from({ length: dotCount }, (_, i) => {
|
||||||
const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
return new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||||
|
});
|
||||||
const shape = new THREE.Shape();
|
|
||||||
shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false);
|
|
||||||
|
|
||||||
shapes.push(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shapes;
|
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@@ -43,7 +32,14 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shapes.length === 0) return null;
|
const { dotRadius, color } = useMemo(() => {
|
||||||
|
return {
|
||||||
|
dotRadius: aisle.type.aisleType === 'dotted-aisle' ? ((aisle.type as any).dotRadius || 0.1) * 0.6 : 0.06,
|
||||||
|
color: aisle.type.aisleColor || '#ffffff'
|
||||||
|
};
|
||||||
|
}, [aisle]);
|
||||||
|
|
||||||
|
if (dotPositions.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
@@ -51,26 +47,23 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
uuid={aisle.aisleUuid}
|
uuid={aisle.aisleUuid}
|
||||||
ref={aisleRef}
|
ref={aisleRef}
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
userData={aisle}
|
userData={aisle}
|
||||||
onDoubleClick={handleClick}
|
onDoubleClick={handleClick}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
setSelectedAisle(null);
|
setSelectedAisle(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{shapes.map((shape, index) => (
|
<Instances frustumCulled={false}>
|
||||||
<Extrude
|
<circleGeometry args={[dotRadius, 32]} />
|
||||||
key={index}
|
<meshStandardMaterial color={color} side={THREE.DoubleSide} />
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
{dotPositions.map((position, index) => (
|
||||||
receiveShadow
|
<Instance
|
||||||
castShadow
|
key={index}
|
||||||
>
|
position={[position.x, 0, position.z]}
|
||||||
<meshStandardMaterial
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
))}
|
||||||
))}
|
</Instances>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import * as THREE from 'three';
|
|||||||
import { useFrame, useThree } from '@react-three/fiber';
|
import { useFrame, useThree } from '@react-three/fiber';
|
||||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||||
import * as Constants from '../../../../types/world/worldConstants';
|
import * as Constants from '../../../../types/world/worldConstants';
|
||||||
import { Extrude, Html } from '@react-three/drei';
|
import { Extrude, Html, Instance, Instances } from '@react-three/drei';
|
||||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||||
@@ -289,125 +289,119 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
const shapes = useMemo(() => {
|
|
||||||
|
const dashInstances = useMemo(() => {
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
|
|
||||||
const width = aisle.type.aisleWidth || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
const dashLength = aisle.type.dashLength || 0.5;
|
const dashLength = aisle.type.dashLength || 0.5;
|
||||||
const gapLength = aisle.type.gapLength || 0.3;
|
const gapLength = aisle.type.gapLength || 0.3;
|
||||||
|
|
||||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
const totalVec = new THREE.Vector3().subVectors(end, start);
|
||||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
const totalLength = totalVec.length();
|
||||||
|
const direction = totalVec.clone().normalize();
|
||||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
|
||||||
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
||||||
|
|
||||||
const shapes = [];
|
const instances = [];
|
||||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
|
||||||
|
|
||||||
for (let i = 0; i < segmentCount; i++) {
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength));
|
const center = start.clone().addScaledVector(direction, i * (dashLength + gapLength) + dashLength / 2);
|
||||||
const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength);
|
|
||||||
|
|
||||||
const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2);
|
const rotationY = Math.atan2(direction.x, direction.z);
|
||||||
const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2);
|
|
||||||
const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2);
|
|
||||||
const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2);
|
|
||||||
|
|
||||||
const shape = new THREE.Shape();
|
instances.push({
|
||||||
shape.moveTo(leftStart.x, leftStart.z);
|
position: [center.x, 0, center.z] as [number, number, number],
|
||||||
shape.lineTo(leftEnd.x, leftEnd.z);
|
scale: [width, 0.001, dashLength] as [number, number, number],
|
||||||
shape.lineTo(rightEnd.x, rightEnd.z);
|
rotation: [0, rotationY, 0] as [number, number, number],
|
||||||
shape.lineTo(rightStart.x, rightStart.z);
|
});
|
||||||
shape.closePath();
|
|
||||||
|
|
||||||
shapes.push(shape);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shapes;
|
return instances;
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
if (shapes.length === 0) return null;
|
if (dashInstances.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
>
|
>
|
||||||
{shapes.map((shape, index) => (
|
<Instances frustumCulled={false}>
|
||||||
<Extrude
|
<boxGeometry args={[1, 1, 1]} />
|
||||||
key={index}
|
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} />
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
{dashInstances.map((inst, i) => (
|
||||||
receiveShadow
|
<Instance
|
||||||
castShadow
|
key={i}
|
||||||
>
|
position={inst.position}
|
||||||
<meshStandardMaterial
|
scale={inst.scale}
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
rotation={inst.rotation}
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
))}
|
||||||
))}
|
</Instances>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
const shapes = useMemo(() => {
|
|
||||||
|
const dotPositions = useMemo(() => {
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.dotRadius || 0.1;
|
|
||||||
const dotSpacing = aisle.type.gapLength || 0.5;
|
const dotSpacing = aisle.type.gapLength || 0.5;
|
||||||
const dotRadius = width * 0.6;
|
|
||||||
|
|
||||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||||
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing);
|
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing);
|
||||||
|
|
||||||
const shapes = [];
|
|
||||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
|
|
||||||
for (let i = 0; i < dotCount; i++) {
|
return Array.from({ length: dotCount }, (_, i) => {
|
||||||
const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
return new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||||
|
});
|
||||||
const shape = new THREE.Shape();
|
|
||||||
shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false);
|
|
||||||
|
|
||||||
shapes.push(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shapes;
|
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
if (shapes.length === 0) return null;
|
const { dotRadius, color } = useMemo(() => {
|
||||||
|
return {
|
||||||
|
dotRadius: aisle.type.aisleType === 'dotted-aisle' ? ((aisle.type as any).dotRadius || 0.1) * 0.6 : 0.06,
|
||||||
|
color: aisle.type.aisleColor || '#ffffff'
|
||||||
|
};
|
||||||
|
}, [aisle]);
|
||||||
|
|
||||||
|
if (dotPositions.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
>
|
>
|
||||||
{shapes.map((shape, index) => (
|
<Instances frustumCulled={false}>
|
||||||
<Extrude
|
<circleGeometry args={[dotRadius, 32]} />
|
||||||
key={index}
|
<meshStandardMaterial color={color} side={THREE.DoubleSide} />
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
{dotPositions.map((position, index) => (
|
||||||
receiveShadow
|
<Instance
|
||||||
castShadow
|
key={index}
|
||||||
>
|
position={[position.x, 0, position.z]}
|
||||||
<meshStandardMaterial
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
))}
|
||||||
))}
|
</Instances>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
const arrows = useMemo(() => {
|
|
||||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return [];
|
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||||
|
const result = {
|
||||||
|
arrowGeometry: null as THREE.ExtrudeGeometry | null,
|
||||||
|
arrowInstances: [] as {
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
}[],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return result;
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
@@ -420,55 +414,53 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
|||||||
direction.normalize();
|
direction.normalize();
|
||||||
|
|
||||||
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
||||||
|
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||||
|
|
||||||
const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = [];
|
const shape = new THREE.Shape();
|
||||||
|
const w = width * 0.8;
|
||||||
|
const h = arrowLength;
|
||||||
|
|
||||||
|
shape.moveTo(0, 0);
|
||||||
|
shape.lineTo(w, h * 0.6);
|
||||||
|
shape.lineTo(w * 0.4, h * 0.6);
|
||||||
|
shape.lineTo(w * 0.4, h);
|
||||||
|
shape.lineTo(-w * 0.4, h);
|
||||||
|
shape.lineTo(-w * 0.4, h * 0.6);
|
||||||
|
shape.lineTo(-w, h * 0.6);
|
||||||
|
shape.lineTo(0, 0);
|
||||||
|
|
||||||
|
result.arrowGeometry = new THREE.ExtrudeGeometry(shape, {
|
||||||
|
depth: 0.01,
|
||||||
|
bevelEnabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
result.arrowGeometry.rotateX(Math.PI / 2);
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const initialOffset = arrowLength;
|
const offset = arrowLength + i * (arrowLength + spacing);
|
||||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing));
|
const center = new THREE.Vector3().copy(start).addScaledVector(direction, offset);
|
||||||
|
|
||||||
const shape = new THREE.Shape();
|
result.arrowInstances.push({
|
||||||
const w = width * 0.8;
|
position: [center.x, 0, center.z],
|
||||||
const h = arrowLength;
|
rotation: [0, angle, 0],
|
||||||
|
});
|
||||||
shape.moveTo(0, 0);
|
|
||||||
shape.lineTo(w, h * 0.6);
|
|
||||||
shape.lineTo(w * 0.4, h * 0.6);
|
|
||||||
shape.lineTo(w * 0.4, h);
|
|
||||||
shape.lineTo(-w * 0.4, h);
|
|
||||||
shape.lineTo(-w * 0.4, h * 0.6);
|
|
||||||
shape.lineTo(-w, h * 0.6);
|
|
||||||
shape.lineTo(0, 0);
|
|
||||||
|
|
||||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
|
||||||
|
|
||||||
arrowShapes.push({ shape, position: center, rotationY: angle });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrowShapes;
|
return result;
|
||||||
}, [aisle]);
|
}, [aisle]);
|
||||||
|
|
||||||
if (arrows.length === 0) return null;
|
if (!arrowGeometry || arrowInstances.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
rotation={[Math.PI / 2, 0, 0]}
|
|
||||||
>
|
>
|
||||||
{arrows.map(({ shape, position, rotationY }, index) => (
|
<Instances geometry={arrowGeometry} frustumCulled={false}>
|
||||||
<group key={index} position={[position.x, position.z, 0]} rotation={[0, 0, -rotationY]}>
|
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} side={THREE.DoubleSide} />
|
||||||
<Extrude
|
{arrowInstances.map(({ position, rotation }, i) => (
|
||||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
<Instance key={i} position={position} rotation={rotation} />
|
||||||
receiveShadow
|
))}
|
||||||
castShadow
|
</Instances>
|
||||||
>
|
|
||||||
<meshStandardMaterial
|
|
||||||
color={aisle.type.aisleColor || '#ffffff'}
|
|
||||||
side={THREE.DoubleSide}
|
|
||||||
/>
|
|
||||||
</Extrude>
|
|
||||||
</group>
|
|
||||||
))}
|
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
|||||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||||
};
|
};
|
||||||
}, [selectedItem, camera, pointer, activeModule, controls, isRenameMode]);
|
}, [selectedItem, camera, activeModule, controls, isRenameMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Models />
|
<Models />
|
||||||
|
|||||||
@@ -162,8 +162,7 @@ function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boole
|
|||||||
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
|
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
loader.load(
|
loader.load(modelUrl,
|
||||||
modelUrl,
|
|
||||||
handleBackendLoad,
|
handleBackendLoad,
|
||||||
undefined,
|
undefined,
|
||||||
(error) => {
|
(error) => {
|
||||||
@@ -418,26 +417,26 @@ function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boole
|
|||||||
castShadow
|
castShadow
|
||||||
receiveShadow
|
receiveShadow
|
||||||
onDoubleClick={(e) => {
|
onDoubleClick={(e) => {
|
||||||
e.stopPropagation();
|
|
||||||
if (!toggleView) {
|
if (!toggleView) {
|
||||||
|
e.stopPropagation();
|
||||||
handleDblClick(asset);
|
handleDblClick(asset);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
|
||||||
if (!toggleView) {
|
if (!toggleView) {
|
||||||
|
e.stopPropagation();
|
||||||
handleClick(e, asset);
|
handleClick(e, asset);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerOver={(e) => {
|
onPointerOver={(e) => {
|
||||||
e.stopPropagation();
|
|
||||||
if (!toggleView) {
|
if (!toggleView) {
|
||||||
|
e.stopPropagation();
|
||||||
handlePointerOver(asset);
|
handlePointerOver(asset);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerLeave={(e) => {
|
onPointerLeave={(e) => {
|
||||||
e.stopPropagation();
|
|
||||||
if (!toggleView) {
|
if (!toggleView) {
|
||||||
|
e.stopPropagation();
|
||||||
handlePointerOut(e, asset);
|
handlePointerOut(e, asset);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -708,7 +708,6 @@ function Point({ point }: { readonly point: Point }) {
|
|||||||
<meshBasicMaterial color="white" />
|
<meshBasicMaterial color="white" />
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
|||||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||||
|
|
||||||
function Wall({ wall }: { readonly wall: Wall }) {
|
function Wall({ wall }: { readonly wall: Wall }) {
|
||||||
const { wallStore } = useSceneContext();
|
const { wallStore, wallAssetStore } = useSceneContext();
|
||||||
const { walls, addDecal } = wallStore();
|
const { walls, addDecal } = wallStore();
|
||||||
|
const { wallAssets, getAssetsByWall } = wallAssetStore();
|
||||||
|
const assets = getAssetsByWall(wall.wallUuid);
|
||||||
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||||
const { togglView } = useToggleView();
|
const { togglView } = useToggleView();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -105,19 +107,35 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
key={wall.wallUuid}
|
key={wall.wallUuid}
|
||||||
userData={wall}
|
userData={wall}
|
||||||
>
|
>
|
||||||
<Base
|
{(assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0)) ?
|
||||||
castShadow
|
<Base
|
||||||
receiveShadow
|
castShadow
|
||||||
ref={meshRef}
|
receiveShadow
|
||||||
geometry={geometry}
|
ref={meshRef}
|
||||||
position={[centerX, centerY, centerZ]}
|
geometry={geometry}
|
||||||
rotation={[0, -angle, 0]}
|
position={[centerX, centerY, centerZ]}
|
||||||
userData={wall}
|
rotation={[0, -angle, 0]}
|
||||||
>
|
userData={wall}
|
||||||
{materials.map((material, index) => (
|
>
|
||||||
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
|
{materials.map((material, index) => (
|
||||||
))}
|
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
|
||||||
</Base>
|
))}
|
||||||
|
</Base>
|
||||||
|
:
|
||||||
|
<mesh
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
ref={meshRef}
|
||||||
|
geometry={geometry}
|
||||||
|
position={[centerX, centerY, centerZ]}
|
||||||
|
rotation={[0, -angle, 0]}
|
||||||
|
userData={wall}
|
||||||
|
>
|
||||||
|
{materials.map((material, index) => (
|
||||||
|
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
|
||||||
|
))}
|
||||||
|
</mesh>
|
||||||
|
}
|
||||||
<mesh
|
<mesh
|
||||||
castShadow
|
castShadow
|
||||||
receiveShadow
|
receiveShadow
|
||||||
|
|||||||
@@ -114,11 +114,56 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvasElement = gl.domElement;
|
const canvasElement = gl.domElement;
|
||||||
|
|
||||||
const onPointerUp = () => {
|
const onPointerUp = (e: PointerEvent) => {
|
||||||
draggingRef.current = false;
|
draggingRef.current = false;
|
||||||
if (controls) {
|
if (controls) {
|
||||||
(controls as any).enabled = true;
|
(controls as any).enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedWallAsset) {
|
||||||
|
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||||
|
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||||
|
|
||||||
|
raycaster.setFromCamera(pointer, camera);
|
||||||
|
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||||
|
const intersect = intersects.find((i: any) => i.object.name.includes('WallReference'));
|
||||||
|
|
||||||
|
if (intersect && intersect.object.userData.wallUuid && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
||||||
|
const newPoint = closestPointOnLineSegment(
|
||||||
|
new THREE.Vector3(intersect.point.x, 0, intersect.point.z),
|
||||||
|
new THREE.Vector3(...intersect.object.userData.points[0].position),
|
||||||
|
new THREE.Vector3(...intersect.object.userData.points[1].position)
|
||||||
|
);
|
||||||
|
|
||||||
|
const wallRotation = intersect.object.rotation.clone();
|
||||||
|
|
||||||
|
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
||||||
|
wallUuid: intersect.object.userData.wallUuid,
|
||||||
|
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
||||||
|
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (projectId && updatedWallAsset) {
|
||||||
|
|
||||||
|
// API
|
||||||
|
|
||||||
|
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
||||||
|
|
||||||
|
// SOCKET
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
wallAssetData: updatedWallAsset,
|
||||||
|
projectId: projectId,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId: userId,
|
||||||
|
organization: organization
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('v1:wall-asset:add', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerMove = (e: any) => {
|
const onPointerMove = (e: any) => {
|
||||||
@@ -142,31 +187,11 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
|
|
||||||
const wallRotation = intersect.object.rotation.clone();
|
const wallRotation = intersect.object.rotation.clone();
|
||||||
|
|
||||||
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
updateWallAsset(wallAsset.modelUuid, {
|
||||||
wallUuid: intersect.object.userData.wallUuid,
|
wallUuid: intersect.object.userData.wallUuid,
|
||||||
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z],
|
||||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (projectId && updatedWallAsset) {
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
|
||||||
|
|
||||||
// SOCKET
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
wallAssetData: updatedWallAsset,
|
|
||||||
projectId: projectId,
|
|
||||||
versionId: selectedVersion?.versionId || '',
|
|
||||||
userId: userId,
|
|
||||||
organization: organization
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit('v1:wall-asset:add', data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -225,7 +250,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
}
|
}
|
||||||
}, [activeTool, deletableWallAsset]);
|
}, [activeTool, deletableWallAsset]);
|
||||||
|
|
||||||
if (!gltfScene || !boundingBox || !wall) { return null }
|
if (!gltfScene || !boundingBox) { return null }
|
||||||
const size = new THREE.Vector3();
|
const size = new THREE.Vector3();
|
||||||
boundingBox.getSize(size);
|
boundingBox.getSize(size);
|
||||||
const center = new THREE.Vector3();
|
const center = new THREE.Vector3();
|
||||||
@@ -242,7 +267,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
|||||||
visible={wallAsset.isVisible}
|
visible={wallAsset.isVisible}
|
||||||
userData={wallAsset}
|
userData={wallAsset}
|
||||||
>
|
>
|
||||||
<Subtraction position={[center.x, center.y, 0]} scale={[size.x, size.y, wall.wallThickness + 0.05]}>
|
<Subtraction position={[center.x, center.y, 0]} scale={[size.x, size.y, (wall?.wallThickness ?? 0) + 0.05]}>
|
||||||
<Geometry>
|
<Geometry>
|
||||||
<Base geometry={new THREE.BoxGeometry()} />
|
<Base geometry={new THREE.BoxGeometry()} />
|
||||||
</Geometry>
|
</Geometry>
|
||||||
|
|||||||
@@ -9,124 +9,89 @@ import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKe
|
|||||||
import { firstPersonCamera } from "./firstPersonCamera";
|
import { firstPersonCamera } from "./firstPersonCamera";
|
||||||
|
|
||||||
const CamMode: React.FC = () => {
|
const CamMode: React.FC = () => {
|
||||||
const { camMode, setCamMode } = useCamMode();
|
const { camMode, setCamMode } = useCamMode();
|
||||||
const [, get] = useKeyboardControls();
|
const [_, get] = useKeyboardControls();
|
||||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||||
const state: any = useThree();
|
const state: any = useThree();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const [isShiftActive, setIsShiftActive] = useState(false);
|
const [isShiftActive, setIsShiftActive] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handlePointerLockChange = async () => {
|
const handlePointerLockChange = async () => {
|
||||||
if (document.pointerLockElement && !toggleView) {
|
if (document.pointerLockElement && !toggleView) {
|
||||||
// Pointer is locked
|
} else if (camMode === "FirstPerson" && !toggleView) {
|
||||||
} else if (camMode === "FirstPerson" && !toggleView) {
|
setCamMode("ThirdPerson");
|
||||||
// Pointer is unlocked
|
await switchToThirdPerson(state.controls, state.camera);
|
||||||
setCamMode("ThirdPerson");
|
}
|
||||||
await switchToThirdPerson(state.controls, state.camera);
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("pointerlockchange", handlePointerLockChange);
|
document.addEventListener("pointerlockchange", handlePointerLockChange);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener(
|
document.removeEventListener("pointerlockchange", handlePointerLockChange);
|
||||||
"pointerlockchange",
|
};
|
||||||
handlePointerLockChange
|
}, [camMode, toggleView, setCamMode, state.controls, state.camera]);
|
||||||
);
|
|
||||||
};
|
|
||||||
}, [camMode, toggleView, setCamMode, state.controls, state.camera]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyPress = async (event: KeyboardEvent) => {
|
||||||
if (event.key === "Shift") {
|
if (!state.controls) return;
|
||||||
setIsShiftActive(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyUp = (event: KeyboardEvent) => {
|
const keyCombination = detectModifierKeys(event);
|
||||||
if (event.key === "Shift") {
|
|
||||||
setIsShiftActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
||||||
window.addEventListener("keyup", handleKeyUp);
|
firstPersonCamera({
|
||||||
|
setIsTransitioning,
|
||||||
|
state,
|
||||||
|
camMode,
|
||||||
|
setCamMode,
|
||||||
|
switchToFirstPerson,
|
||||||
|
switchToThirdPerson,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
if (keyCombination === 'Shift') {
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
setIsShiftActive(true);
|
||||||
window.removeEventListener("keyup", handleKeyUp);
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const handleKeyUp = (event: KeyboardEvent) => {
|
||||||
const handleKeyPress = async (event: KeyboardEvent) => {
|
if (event.key === "Shift") {
|
||||||
if (!state.controls) return;
|
setIsShiftActive(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const keyCombination = detectModifierKeys(event);
|
window.addEventListener("keydown", handleKeyPress);
|
||||||
|
window.addEventListener("keyup", handleKeyUp);
|
||||||
|
|
||||||
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
return () => {
|
||||||
firstPersonCamera({
|
window.removeEventListener("keydown", handleKeyPress);
|
||||||
setIsTransitioning,
|
window.removeEventListener("keyup", handleKeyUp);
|
||||||
state,
|
};
|
||||||
camMode,
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
setCamMode,
|
}, [camMode, isTransitioning, toggleView, state.controls, state.camera, setCamMode]);
|
||||||
switchToFirstPerson,
|
|
||||||
switchToThirdPerson,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyPress);
|
useFrame(() => {
|
||||||
return () => {
|
const { forward, backward, left, right } = get();
|
||||||
window.removeEventListener("keydown", handleKeyPress);
|
if (!state.controls) return;
|
||||||
};
|
if (camMode === "ThirdPerson" || !document.pointerLockElement) return;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [
|
|
||||||
camMode,
|
|
||||||
isTransitioning,
|
|
||||||
toggleView,
|
|
||||||
state.controls,
|
|
||||||
state.camera,
|
|
||||||
setCamMode,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useFrame(() => {
|
const speedMultiplier = isShiftActive ? 4 : 1;
|
||||||
const { forward, backward, left, right } = get();
|
|
||||||
if (!state.controls) return;
|
|
||||||
if (camMode === "ThirdPerson" || !document.pointerLockElement) return;
|
|
||||||
|
|
||||||
const speedMultiplier = isShiftActive ? 4 : 1;
|
if (forward) {
|
||||||
|
state.controls.forward(CONSTANTS.firstPersonControls.forwardSpeed * speedMultiplier, true);
|
||||||
|
}
|
||||||
|
if (backward) {
|
||||||
|
state.controls.forward(CONSTANTS.firstPersonControls.backwardSpeed * speedMultiplier, true);
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
state.controls.truck(CONSTANTS.firstPersonControls.leftSpeed * speedMultiplier, 0, true);
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
state.controls.truck(CONSTANTS.firstPersonControls.rightSpeed * speedMultiplier, 0, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (forward) {
|
return null;
|
||||||
state.controls.forward(
|
|
||||||
CONSTANTS.firstPersonControls.forwardSpeed * speedMultiplier,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (backward) {
|
|
||||||
state.controls.forward(
|
|
||||||
CONSTANTS.firstPersonControls.backwardSpeed * speedMultiplier,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (left) {
|
|
||||||
state.controls.truck(
|
|
||||||
CONSTANTS.firstPersonControls.leftSpeed * speedMultiplier,
|
|
||||||
0,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (right) {
|
|
||||||
state.controls.truck(
|
|
||||||
CONSTANTS.firstPersonControls.rightSpeed * speedMultiplier,
|
|
||||||
0,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return null; // This component does not render any UI
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CamMode;
|
export default CamMode;
|
||||||
|
|||||||
@@ -1,73 +1,68 @@
|
|||||||
import * as THREE from "three";
|
import { useEffect } from "react";
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { useToggleView } from "../../../store/builder/store";
|
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
import * as THREE from 'three';
|
||||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
import { PerspectiveCamera, OrthographicCamera, CameraControls } from '@react-three/drei';
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||||
|
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||||
import { getUserData } from "../../../functions/getUserData";
|
import { getUserData } from "../../../functions/getUserData";
|
||||||
import { CameraControls } from "@react-three/drei";
|
import { useToggleView } from "../../../store/builder/store";
|
||||||
|
|
||||||
export default function SwitchView() {
|
export default function SwitchView() {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const state: any = useThree();
|
const { controls } = useThree();
|
||||||
const { set } = useThree();
|
const { projectId } = useParams();
|
||||||
const perspectiveCamera = useRef<THREE.PerspectiveCamera | null>(null);
|
const { organization } = getUserData();
|
||||||
const orthoCamera = useRef<THREE.OrthographicCamera | null>(null);
|
|
||||||
orthoCamera.current = new THREE.OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.01, 1000);
|
|
||||||
perspectiveCamera.current = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000);
|
|
||||||
const { projectId } = useParams();
|
|
||||||
const { organization } = getUserData();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!perspectiveCamera.current || !orthoCamera.current) return;
|
if (toggleView && controls) {
|
||||||
if (toggleView) {
|
(controls as any).mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
||||||
orthoCamera.current.zoom = 10;
|
(controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
||||||
orthoCamera.current.position.set(...CONSTANTS.twoDimension.defaultPosition);
|
} else {
|
||||||
orthoCamera.current.lookAt(new THREE.Vector3(...CONSTANTS.twoDimension.defaultTarget));
|
try {
|
||||||
orthoCamera.current.updateProjectionMatrix();
|
getCamera(organization, localStorage.getItem('userId')!, projectId).then((data) => {
|
||||||
set({ camera: orthoCamera.current });
|
if (data && data.position && data.target) {
|
||||||
orthoCamera.current.updateProjectionMatrix();
|
(controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||||
} else if (!toggleView) {
|
(controls as CameraControls)?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||||
perspectiveCamera.current.position.set(...CONSTANTS.threeDimension.defaultPosition);
|
} else {
|
||||||
perspectiveCamera.current.lookAt(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget));
|
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
set({ camera: perspectiveCamera.current });
|
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
}
|
}
|
||||||
}, [toggleView, set]);
|
});
|
||||||
|
} catch (error) {
|
||||||
|
echo.error("Failed to retrieve camera position or target");
|
||||||
|
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
|
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
if (controls) {
|
||||||
if (toggleView && state.controls) {
|
(controls as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
||||||
state.controls.mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
(controls as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||||
state.controls.mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
}
|
||||||
} else {
|
}
|
||||||
try {
|
}, [toggleView, controls]);
|
||||||
getCamera(organization, localStorage.getItem('userId')!, projectId).then((data) => {
|
|
||||||
if (data && data.position && data.target) {
|
|
||||||
// state.controls?.setLookAt(data.position.x, data.position.y, data.position.z, data.target.x, data.target.y, data.target.z, true)
|
|
||||||
state.controls?.setPosition(data.position.x, data.position.y, data.position.z);
|
|
||||||
state.controls?.setTarget(data.target.x, data.target.y, data.target.z);
|
|
||||||
} else {
|
|
||||||
// state.controls?.setLookAt(...CONSTANTS.threeDimension.defaultPosition, ...CONSTANTS.threeDimension.defaultTarget, true);
|
|
||||||
state.controls?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
|
||||||
state.controls?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
echo.error("Failed to retrieve camera position or target");
|
|
||||||
console.error("Failed to retrieve camera position or target:", error);
|
|
||||||
// state.controls?.setLookAt(...CONSTANTS.threeDimension.defaultPosition, ...CONSTANTS.threeDimension.defaultTarget, true);
|
|
||||||
state.controls?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
|
||||||
state.controls?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.controls) {
|
return (
|
||||||
state.controls.mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
<>
|
||||||
state.controls.mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
{toggleView ? (
|
||||||
}
|
<OrthographicCamera
|
||||||
}
|
makeDefault
|
||||||
}, [toggleView, state.controls]);
|
position={CONSTANTS.twoDimension.defaultPosition}
|
||||||
|
zoom={10}
|
||||||
return (
|
near={0.01}
|
||||||
<></>
|
far={1000}
|
||||||
);
|
onUpdate={(self) => self.lookAt(new THREE.Vector3(...CONSTANTS.twoDimension.defaultTarget))}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<PerspectiveCamera
|
||||||
|
makeDefault
|
||||||
|
fov={75}
|
||||||
|
position={CONSTANTS.threeDimension.defaultPosition}
|
||||||
|
near={0.01}
|
||||||
|
far={1000}
|
||||||
|
onUpdate={(self) => self.lookAt(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -13,16 +13,17 @@ import SelectionControls3D from "./selectionControls/selection3D/selectionContro
|
|||||||
import TransformControl from "./transformControls/transformControls";
|
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";
|
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||||
|
|
||||||
export default function Controls() {
|
export default function Controls() {
|
||||||
const controlsRef = useRef<CameraControls>(null);
|
const controlsRef = useRef<CameraControls>(null);
|
||||||
|
|
||||||
|
const state = useThree();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { resetCamera, setResetCamera } = useResetCamera();
|
const { resetCamera, setResetCamera } = useResetCamera();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const state = useThree();
|
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
@@ -33,7 +34,6 @@ export default function Controls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCamera(organization, userId, projectId).then((data) => {
|
getCamera(organization, userId, projectId).then((data) => {
|
||||||
// console.log('data: ', data);
|
|
||||||
if (data && data.position && data.target) {
|
if (data && data.position && data.target) {
|
||||||
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
|
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||||
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
|
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||||
@@ -41,8 +41,7 @@ export default function Controls() {
|
|||||||
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
}
|
}
|
||||||
})
|
}).catch((error) => console.error("Failed to fetch camera data:", error));
|
||||||
.catch((error) => console.error("Failed to fetch camera data:", error));
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -60,7 +59,6 @@ export default function Controls() {
|
|||||||
socketId: socket.id,
|
socketId: socket.id,
|
||||||
projectId
|
projectId
|
||||||
};
|
};
|
||||||
// console.log('CameracamData: ', camData);
|
|
||||||
socket.emit('v1:Camera:set', camData)
|
socket.emit('v1:Camera:set', camData)
|
||||||
|
|
||||||
setResetCamera(false);
|
setResetCamera(false);
|
||||||
@@ -130,7 +128,7 @@ export default function Controls() {
|
|||||||
camera={state.camera}
|
camera={state.camera}
|
||||||
verticalDragToForward={true}
|
verticalDragToForward={true}
|
||||||
boundaryEnclosesCamera={true}
|
boundaryEnclosesCamera={true}
|
||||||
dollyToCursor={toggleView}
|
dollyDragInverted
|
||||||
>
|
>
|
||||||
|
|
||||||
<SwitchView />
|
<SwitchView />
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
|
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
|
||||||
@@ -340,6 +341,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import * as CONSTANTS from '../../../types/world/worldConstants';
|
|||||||
|
|
||||||
export default function Sun() {
|
export default function Sun() {
|
||||||
const savedTheme: string | null = localStorage.getItem("theme");
|
const savedTheme: string | null = localStorage.getItem("theme");
|
||||||
const { elevation, setElevation } = useElevation();
|
const { elevation } = useElevation();
|
||||||
const { sunPosition, setSunPosition } = useSunPosition();
|
const { setSunPosition } = useSunPosition();
|
||||||
const { azimuth, setAzimuth } = useAzimuth();
|
const { azimuth } = useAzimuth();
|
||||||
const [turbidity, setTurbidity] = useState(CONSTANTS.skyConfig.defaultTurbidity);
|
const [turbidity, setTurbidity] = useState(CONSTANTS.skyConfig.defaultTurbidity);
|
||||||
const sunPositionRef = useRef(new THREE.Vector3(0, 0, 0));
|
const sunPositionRef = useRef(new THREE.Vector3(0, 0, 0));
|
||||||
const [_, forceUpdate] = useState(0);
|
const [_, forceUpdate] = useState(0);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
||||||
|
import { useThree } from "@react-three/fiber";
|
||||||
import { BlendFunction } from "postprocessing";
|
import { BlendFunction } from "postprocessing";
|
||||||
import {
|
import {
|
||||||
useDeletableFloorItem,
|
useDeletableFloorItem,
|
||||||
@@ -11,6 +12,7 @@ import { useEffect } from "react";
|
|||||||
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
||||||
|
|
||||||
export default function PostProcessing() {
|
export default function PostProcessing() {
|
||||||
|
const { scene } = useThree();
|
||||||
const { selectedPoints } = useSelectedPoints();
|
const { selectedPoints } = useSelectedPoints();
|
||||||
const { deletableFloorItem } = useDeletableFloorItem();
|
const { deletableFloorItem } = useDeletableFloorItem();
|
||||||
const { selectedWallItem } = useSelectedWallItem();
|
const { selectedWallItem } = useSelectedWallItem();
|
||||||
@@ -77,11 +79,16 @@ export default function PostProcessing() {
|
|||||||
denoiseRadius={6}
|
denoiseRadius={6}
|
||||||
denoiseSamples={16}
|
denoiseSamples={16}
|
||||||
/>
|
/>
|
||||||
|
<DepthOfField
|
||||||
|
focusDistance={0}
|
||||||
|
focalLength={0.15}
|
||||||
|
bokehScale={2}
|
||||||
|
/>
|
||||||
<Bloom
|
<Bloom
|
||||||
intensity={0.1}
|
intensity={0.1}
|
||||||
luminanceThreshold={0.9}
|
luminanceThreshold={0.9}
|
||||||
luminanceSmoothing={0.025}
|
luminanceSmoothing={0.025}
|
||||||
mipmapBlur={false}
|
mipmapBlur={false}
|
||||||
/>
|
/>
|
||||||
{selectedWallAsset && (
|
{selectedWallAsset && (
|
||||||
<Outline
|
<Outline
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { Canvas } from "@react-three/fiber";
|
import { Canvas } from "@react-three/fiber";
|
||||||
import { KeyboardControls } from "@react-three/drei";
|
import { KeyboardControls, Stats } from "@react-three/drei";
|
||||||
import { useSceneContext } from "./sceneContext";
|
import { useSceneContext } from "./sceneContext";
|
||||||
|
|
||||||
import Builder from "../builder/builder";
|
import Builder from "../builder/builder";
|
||||||
@@ -65,6 +65,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
|||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
performance={{ min: 0.9, max: 1.0 }}
|
||||||
onCreated={(e) => {
|
onCreated={(e) => {
|
||||||
e.scene.background = layout === 'Main Layout' ? null : new Color(0x19191d);
|
e.scene.background = layout === 'Main Layout' ? null : new Color(0x19191d);
|
||||||
}}
|
}}
|
||||||
@@ -75,6 +76,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
|||||||
<Builder />
|
<Builder />
|
||||||
<Simulation />
|
<Simulation />
|
||||||
<Visualization />
|
<Visualization />
|
||||||
|
<Stats />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</KeyboardControls>
|
</KeyboardControls>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Sun from '../environment/sky'
|
|||||||
import Shadows from '../environment/shadow'
|
import Shadows from '../environment/shadow'
|
||||||
import PostProcessing from '../postProcessing/postProcessing'
|
import PostProcessing from '../postProcessing/postProcessing'
|
||||||
import Controls from '../controls/controls';
|
import Controls from '../controls/controls';
|
||||||
import { Environment } from '@react-three/drei'
|
import { AdaptiveDpr, AdaptiveEvents, Environment } from '@react-three/drei'
|
||||||
|
|
||||||
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||||
// import { Perf } from 'r3f-perf';
|
// import { Perf } from 'r3f-perf';
|
||||||
@@ -23,6 +23,10 @@ function Setup() {
|
|||||||
{/* <MovingClouds /> */}
|
{/* <MovingClouds /> */}
|
||||||
|
|
||||||
<Environment files={background} environmentIntensity={1.5} />
|
<Environment files={background} environmentIntensity={1.5} />
|
||||||
|
|
||||||
|
<AdaptiveEvents />
|
||||||
|
|
||||||
|
<AdaptiveDpr pixelated />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export default function PolygonGenerator({
|
|||||||
aisle.type.aisleType === "arc-aisle"
|
aisle.type.aisleType === "arc-aisle"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
arcAndCircleResult.forEach((arc) => {
|
arcAndCircleResult.forEach((arc) => {
|
||||||
const arcGroup = scene.getObjectByProperty("uuid", arc.aisleUuid);
|
const arcGroup = scene.getObjectByProperty("uuid", arc.aisleUuid);
|
||||||
if (!arcGroup) return;
|
if (!arcGroup) return;
|
||||||
@@ -60,7 +59,6 @@ export default function PolygonGenerator({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const wallPoints: THREE.Vector3[][] = walls
|
const wallPoints: THREE.Vector3[][] = walls
|
||||||
.map((wall) =>
|
.map((wall) =>
|
||||||
wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
|
wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ export const thirdPersonControls: ThirdPersonControls = {
|
|||||||
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, // 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
|
||||||
|
|||||||
Reference in New Issue
Block a user