Merge remote-tracking branch 'origin/simulation-agv-v2' into simulation-agv-v2
This commit is contained in:
commit
4dd0840980
|
@ -30,7 +30,6 @@
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"immer": "^10.1.1",
|
|
||||||
"leva": "^0.10.0",
|
"leva": "^0.10.0",
|
||||||
"mqtt": "^5.10.4",
|
"mqtt": "^5.10.4",
|
||||||
"postprocessing": "^6.36.4",
|
"postprocessing": "^6.36.4",
|
||||||
|
@ -2022,7 +2021,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==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
},
|
},
|
||||||
|
@ -2034,7 +2033,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==",
|
||||||
"devOptional": true,
|
"dev": 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"
|
||||||
|
@ -4137,26 +4136,6 @@
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
|
||||||
"version": "10.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
|
||||||
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
|
||||||
"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",
|
|
||||||
"chalk": "^4.1.0",
|
|
||||||
"dom-accessibility-api": "^0.5.9",
|
|
||||||
"lz-string": "^1.5.0",
|
|
||||||
"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",
|
||||||
|
@ -4268,25 +4247,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==",
|
||||||
"devOptional": true
|
"dev": 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==",
|
||||||
"devOptional": true
|
"dev": 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==",
|
||||||
"devOptional": true
|
"dev": 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==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@turf/along": {
|
"node_modules/@turf/along": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
|
@ -9040,7 +9019,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==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
|
@ -9917,7 +9896,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==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
|
@ -12747,10 +12726,9 @@
|
||||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
},
|
},
|
||||||
"node_modules/immer": {
|
"node_modules/immer": {
|
||||||
"version": "10.1.1",
|
"version": "9.0.21",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/immer"
|
"url": "https://opencollective.com/immer"
|
||||||
|
@ -15281,7 +15259,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==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
|
@ -18012,16 +17990,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dev-utils/node_modules/immer": {
|
|
||||||
"version": "9.0.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
|
||||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/immer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-dev-utils/node_modules/loader-utils": {
|
"node_modules/react-dev-utils/node_modules/loader-utils": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz",
|
||||||
|
@ -20759,7 +20727,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==",
|
||||||
"devOptional": true,
|
"dev": 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",
|
||||||
|
@ -20802,7 +20770,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==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.11.0"
|
"acorn": "^8.11.0"
|
||||||
},
|
},
|
||||||
|
@ -20814,7 +20782,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==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
|
@ -21310,7 +21278,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==",
|
||||||
"devOptional": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul": {
|
"node_modules/v8-to-istanbul": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
|
@ -22369,7 +22337,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==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -9,17 +9,25 @@ import Simulation from "../simulation/simulation";
|
||||||
import Collaboration from "../collaboration/collaboration";
|
import Collaboration from "../collaboration/collaboration";
|
||||||
|
|
||||||
export default function Scene() {
|
export default function Scene() {
|
||||||
const map = useMemo(() => [
|
const map = useMemo(
|
||||||
|
() => [
|
||||||
{ name: "forward", keys: ["ArrowUp", "w", "W"] },
|
{ name: "forward", keys: ["ArrowUp", "w", "W"] },
|
||||||
{ name: "backward", keys: ["ArrowDown", "s", "S"] },
|
{ name: "backward", keys: ["ArrowDown", "s", "S"] },
|
||||||
{ name: "left", keys: ["ArrowLeft", "a", "A"] },
|
{ name: "left", keys: ["ArrowLeft", "a", "A"] },
|
||||||
{ name: "right", keys: ["ArrowRight", "d", "D"] },],
|
{ name: "right", keys: ["ArrowRight", "d", "D"] },
|
||||||
[]);
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardControls map={map}>
|
<KeyboardControls map={map}>
|
||||||
<Canvas eventPrefix="client" gl={{ powerPreference: "high-performance", antialias: true }} onContextMenu={(e) => { e.preventDefault(); }}>
|
<Canvas
|
||||||
|
eventPrefix="client"
|
||||||
|
gl={{ powerPreference: "high-performance", antialias: true }}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Setup />
|
<Setup />
|
||||||
|
|
||||||
<Collaboration />
|
<Collaboration />
|
||||||
|
@ -29,7 +37,6 @@ export default function Scene() {
|
||||||
<Simulation />
|
<Simulation />
|
||||||
|
|
||||||
<Visualization />
|
<Visualization />
|
||||||
|
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</KeyboardControls>
|
</KeyboardControls>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,28 +1,38 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import * as THREE from 'three';
|
import * as THREE from "three";
|
||||||
import { useEventsStore } from '../../../../../store/simulation/useEventsStore';
|
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
|
||||||
import useModuleStore from '../../../../../store/useModuleStore';
|
import useModuleStore from "../../../../../store/useModuleStore";
|
||||||
import { TransformControls } from '@react-three/drei';
|
import { TransformControls } from "@react-three/drei";
|
||||||
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import { useSelectedEventSphere, useSelectedEventData } from '../../../../../store/simulation/useSimulationStore';
|
import {
|
||||||
|
useSelectedEventSphere,
|
||||||
|
useSelectedEventData,
|
||||||
|
} from "../../../../../store/simulation/useSimulationStore";
|
||||||
|
|
||||||
function PointsCreator() {
|
function PointsCreator() {
|
||||||
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
|
const { events, updatePoint, getPointByUuid, getEventByModelUuid } =
|
||||||
|
useEventsStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const transformRef = useRef<any>(null);
|
const transformRef = useRef<any>(null);
|
||||||
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
|
const [transformMode, setTransformMode] = useState<
|
||||||
|
"translate" | "rotate" | null
|
||||||
|
>(null);
|
||||||
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
||||||
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere();
|
const {
|
||||||
const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
|
selectedEventSphere,
|
||||||
|
setSelectedEventSphere,
|
||||||
|
clearSelectedEventSphere,
|
||||||
|
} = useSelectedEventSphere();
|
||||||
|
const { setSelectedEventData, clearSelectedEventData } =
|
||||||
|
useSelectedEventData();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedEventSphere) {
|
if (selectedEventSphere) {
|
||||||
const eventData = getEventByModelUuid(selectedEventSphere.userData.modelUuid);
|
const eventData = getEventByModelUuid(
|
||||||
if (eventData) {
|
selectedEventSphere.userData.modelUuid
|
||||||
setSelectedEventData(
|
|
||||||
eventData,
|
|
||||||
selectedEventSphere.userData.pointUuid
|
|
||||||
);
|
);
|
||||||
|
if (eventData) {
|
||||||
|
setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
|
||||||
} else {
|
} else {
|
||||||
clearSelectedEventData();
|
clearSelectedEventData();
|
||||||
}
|
}
|
||||||
|
@ -48,38 +58,61 @@ function PointsCreator() {
|
||||||
}, [selectedEventSphere]);
|
}, [selectedEventSphere]);
|
||||||
|
|
||||||
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
|
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
|
||||||
let point = JSON.parse(JSON.stringify(getPointByUuid(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)));
|
let point = JSON.parse(
|
||||||
|
JSON.stringify(
|
||||||
|
getPointByUuid(
|
||||||
|
selectedEventSphere.userData.modelUuid,
|
||||||
|
selectedEventSphere.userData.pointUuid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
if (point) {
|
if (point) {
|
||||||
point.position = [selectedEventSphere.position.x, selectedEventSphere.position.y, selectedEventSphere.position.z];
|
point.position = [
|
||||||
updatePoint(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid, point)
|
selectedEventSphere.position.x,
|
||||||
}
|
selectedEventSphere.position.y,
|
||||||
|
selectedEventSphere.position.z,
|
||||||
|
];
|
||||||
|
updatePoint(
|
||||||
|
selectedEventSphere.userData.modelUuid,
|
||||||
|
selectedEventSphere.userData.pointUuid,
|
||||||
|
point
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{activeModule === 'simulation' &&
|
{activeModule === "simulation" && (
|
||||||
<>
|
<>
|
||||||
<group name='EventPointsGroup' >
|
<group name="EventPointsGroup">
|
||||||
{events.map((event, i) => {
|
{events.map((event, i) => {
|
||||||
if (event.type === 'transfer') {
|
if (event.type === "transfer") {
|
||||||
return (
|
return (
|
||||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
<group
|
||||||
|
key={i}
|
||||||
|
position={new THREE.Vector3(...event.position)}
|
||||||
|
>
|
||||||
{event.points.map((point, j) => (
|
{event.points.map((point, j) => (
|
||||||
<mesh
|
<mesh
|
||||||
name='Event-Sphere'
|
name="Event-Sphere"
|
||||||
uuid={point.uuid}
|
uuid={point.uuid}
|
||||||
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedEventSphere(sphereRefs.current[point.uuid]);
|
setSelectedEventSphere(
|
||||||
|
sphereRefs.current[point.uuid]
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
clearSelectedEventSphere();
|
// clearSelectedEventSphere();
|
||||||
setTransformMode(null);
|
setTransformMode(null);
|
||||||
}}
|
}}
|
||||||
key={`${i}-${j}`}
|
key={`${i}-${j}`}
|
||||||
position={new THREE.Vector3(...point.position)}
|
position={new THREE.Vector3(...point.position)}
|
||||||
userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }}
|
userData={{
|
||||||
|
modelUuid: event.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshStandardMaterial color="orange" />
|
<meshStandardMaterial color="orange" />
|
||||||
|
@ -87,69 +120,93 @@ function PointsCreator() {
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
} else if (event.type === 'vehicle') {
|
} else if (event.type === "vehicle") {
|
||||||
return (
|
return (
|
||||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
<group
|
||||||
|
key={i}
|
||||||
|
position={new THREE.Vector3(...event.position)}
|
||||||
|
>
|
||||||
<mesh
|
<mesh
|
||||||
name='Event-Sphere'
|
name="Event-Sphere"
|
||||||
uuid={event.point.uuid}
|
uuid={event.point.uuid}
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
|
setSelectedEventSphere(
|
||||||
|
sphereRefs.current[event.point.uuid]
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
clearSelectedEventSphere();
|
// clearSelectedEventSphere();
|
||||||
setTransformMode(null);
|
setTransformMode(null);
|
||||||
}}
|
}}
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
|
userData={{
|
||||||
|
modelUuid: event.modelUuid,
|
||||||
|
pointUuid: event.point.uuid,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshStandardMaterial color="blue" />
|
<meshStandardMaterial color="blue" />
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
} else if (event.type === 'roboticArm') {
|
} else if (event.type === "roboticArm") {
|
||||||
return (
|
return (
|
||||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
<group
|
||||||
|
key={i}
|
||||||
|
position={new THREE.Vector3(...event.position)}
|
||||||
|
>
|
||||||
<mesh
|
<mesh
|
||||||
name='Event-Sphere'
|
name="Event-Sphere"
|
||||||
uuid={event.point.uuid}
|
uuid={event.point.uuid}
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
|
setSelectedEventSphere(
|
||||||
|
sphereRefs.current[event.point.uuid]
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
clearSelectedEventSphere();
|
// clearSelectedEventSphere();
|
||||||
setTransformMode(null);
|
setTransformMode(null);
|
||||||
}}
|
}}
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
|
userData={{
|
||||||
|
modelUuid: event.modelUuid,
|
||||||
|
pointUuid: event.point.uuid,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshStandardMaterial color="green" />
|
<meshStandardMaterial color="green" />
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
} else if (event.type === 'machine') {
|
} else if (event.type === "machine") {
|
||||||
return (
|
return (
|
||||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
<group
|
||||||
|
key={i}
|
||||||
|
position={new THREE.Vector3(...event.position)}
|
||||||
|
>
|
||||||
<mesh
|
<mesh
|
||||||
name='Event-Sphere'
|
name="Event-Sphere"
|
||||||
uuid={event.point.uuid}
|
uuid={event.point.uuid}
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
|
setSelectedEventSphere(
|
||||||
|
sphereRefs.current[event.point.uuid]
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
clearSelectedEventSphere();
|
// clearSelectedEventSphere();
|
||||||
setTransformMode(null);
|
setTransformMode(null);
|
||||||
}}
|
}}
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
|
userData={{
|
||||||
|
modelUuid: event.modelUuid,
|
||||||
|
pointUuid: event.point.uuid,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshStandardMaterial color="purple" />
|
<meshStandardMaterial color="purple" />
|
||||||
|
@ -161,11 +218,18 @@ function PointsCreator() {
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</group>
|
</group>
|
||||||
{(selectedEventSphere && transformMode) &&
|
{selectedEventSphere && transformMode && (
|
||||||
<TransformControls ref={transformRef} object={selectedEventSphere} mode={transformMode} onMouseUp={(e) => { updatePointToState(selectedEventSphere) }} />
|
<TransformControls
|
||||||
}
|
ref={transformRef}
|
||||||
|
object={selectedEventSphere}
|
||||||
|
mode={transformMode}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
updatePointToState(selectedEventSphere);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
import { useRef } from "react";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { ThreeEvent, useThree } from "@react-three/fiber";
|
||||||
|
|
||||||
|
type OnUpdateCallback = (object: THREE.Object3D) => void;
|
||||||
|
|
||||||
|
export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
|
||||||
|
const { camera, gl, controls, scene } = useThree();
|
||||||
|
const activeObjRef = useRef<THREE.Object3D | null>(null);
|
||||||
|
const planeRef = useRef<THREE.Plane>(
|
||||||
|
new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)
|
||||||
|
);
|
||||||
|
const offsetRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||||
|
const initialPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||||
|
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const pointer = new THREE.Vector2();
|
||||||
|
|
||||||
|
const handlePointerDown = (e: ThreeEvent<PointerEvent>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let obj: THREE.Object3D | null = e.object;
|
||||||
|
|
||||||
|
// Traverse up until we find modelUuid in userData
|
||||||
|
while (obj && !obj.userData?.modelUuid) {
|
||||||
|
obj = obj.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj) return;
|
||||||
|
|
||||||
|
// Disable orbit controls while dragging
|
||||||
|
if (controls) (controls as any).enabled = false;
|
||||||
|
|
||||||
|
activeObjRef.current = obj;
|
||||||
|
initialPositionRef.current.copy(obj.position);
|
||||||
|
|
||||||
|
// Get world position
|
||||||
|
const objectWorldPos = new THREE.Vector3();
|
||||||
|
obj.getWorldPosition(objectWorldPos);
|
||||||
|
|
||||||
|
// Set plane at the object's Y level
|
||||||
|
planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y);
|
||||||
|
|
||||||
|
// Convert pointer to NDC
|
||||||
|
const rect = gl.domElement.getBoundingClientRect();
|
||||||
|
pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
|
// Raycast to intersection
|
||||||
|
raycaster.setFromCamera(pointer, camera);
|
||||||
|
const intersection = new THREE.Vector3();
|
||||||
|
raycaster.ray.intersectPlane(planeRef.current, intersection);
|
||||||
|
|
||||||
|
// Calculate offset
|
||||||
|
offsetRef.current.copy(objectWorldPos).sub(intersection);
|
||||||
|
|
||||||
|
// Start listening for drag
|
||||||
|
gl.domElement.addEventListener("pointermove", handlePointerMove);
|
||||||
|
gl.domElement.addEventListener("pointerup", handlePointerUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerMove = (e: PointerEvent) => {
|
||||||
|
if (!activeObjRef.current) return;
|
||||||
|
|
||||||
|
// Check if Shift key is pressed
|
||||||
|
const isShiftKeyPressed = e.shiftKey;
|
||||||
|
|
||||||
|
// Get the mouse position relative to the canvas
|
||||||
|
const rect = gl.domElement.getBoundingClientRect();
|
||||||
|
pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
|
// Update raycaster to point to the mouse position
|
||||||
|
raycaster.setFromCamera(pointer, camera);
|
||||||
|
|
||||||
|
// Create a vector to store intersection point
|
||||||
|
const intersection = new THREE.Vector3();
|
||||||
|
const intersects = raycaster.ray.intersectPlane(planeRef.current, intersection);
|
||||||
|
if (!intersects) return;
|
||||||
|
|
||||||
|
// Add offset for dragging
|
||||||
|
intersection.add(offsetRef.current);
|
||||||
|
console.log('intersection: ', intersection);
|
||||||
|
|
||||||
|
// Get the parent's world matrix if exists
|
||||||
|
const parent = activeObjRef.current.parent;
|
||||||
|
const targetPosition = new THREE.Vector3();
|
||||||
|
|
||||||
|
if (isShiftKeyPressed) {
|
||||||
|
console.log('isShiftKeyPressed: ', isShiftKeyPressed);
|
||||||
|
// For Y-axis only movement, maintain original X and Z
|
||||||
|
console.log('initialPositionRef: ', initialPositionRef);
|
||||||
|
console.log('intersection.y: ', intersection);
|
||||||
|
targetPosition.set(
|
||||||
|
initialPositionRef.current.x,
|
||||||
|
intersection.y,
|
||||||
|
initialPositionRef.current.z
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// For free movement
|
||||||
|
targetPosition.copy(intersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert world position to local if object is nested inside a parent
|
||||||
|
if (parent) {
|
||||||
|
parent.worldToLocal(targetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update object position
|
||||||
|
activeObjRef.current.position.copy(targetPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerUp = () => {
|
||||||
|
if (controls) (controls as any).enabled = true;
|
||||||
|
|
||||||
|
if (activeObjRef.current) {
|
||||||
|
// Pass the updated position to the onUpdate callback to persist it
|
||||||
|
onUpdate(activeObjRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.domElement.removeEventListener("pointermove", handlePointerMove);
|
||||||
|
gl.domElement.removeEventListener("pointerup", handlePointerUp);
|
||||||
|
|
||||||
|
activeObjRef.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handlePointerDown };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
|
import startPoint from "../../../../assets/gltf-glb/arrow_green.glb";
|
||||||
|
import startEnd from "../../../../assets/gltf-glb/arrow_red.glb";
|
||||||
|
import { useGLTF } from "@react-three/drei";
|
||||||
|
import { useSelectedEventSphere } from "../../../../store/simulation/useSimulationStore";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { useThree } from "@react-three/fiber";
|
||||||
|
|
||||||
|
type VehicleUIProps = {
|
||||||
|
vehicleStatusSample: VehicleEventSchema[];
|
||||||
|
setVehicleStatusSample: React.Dispatch<
|
||||||
|
React.SetStateAction<VehicleEventSchema[]>
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VehicleUI: React.FC<VehicleUIProps> = ({
|
||||||
|
vehicleStatusSample,
|
||||||
|
setVehicleStatusSample,
|
||||||
|
}) => {
|
||||||
|
const { scene: startScene } = useGLTF(startPoint) as any;
|
||||||
|
const { scene: endScene } = useGLTF(startEnd) as any;
|
||||||
|
const { camera, gl, controls } = useThree();
|
||||||
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
|
|
||||||
|
const startMarker = useRef<THREE.Group>(null);
|
||||||
|
const endMarker = useRef<THREE.Group>(null);
|
||||||
|
const hasInitialized = useRef(false);
|
||||||
|
|
||||||
|
const [draggedMarker, setDraggedMarker] = useState<"start" | "end" | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||||
|
const [isRotating, setIsRotating] = useState(false);
|
||||||
|
|
||||||
|
const raycaster = useRef(new THREE.Raycaster());
|
||||||
|
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Y = 0 plane
|
||||||
|
const mouse = useRef(new THREE.Vector2());
|
||||||
|
const prevMousePos = useRef({ x: 0, y: 0 });
|
||||||
|
|
||||||
|
// Initialize start/end markers
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
selectedEventSphere &&
|
||||||
|
startMarker.current &&
|
||||||
|
endMarker.current &&
|
||||||
|
!hasInitialized.current
|
||||||
|
) {
|
||||||
|
startMarker.current.clear();
|
||||||
|
endMarker.current.clear();
|
||||||
|
|
||||||
|
const startClone = startScene.clone();
|
||||||
|
const endClone = endScene.clone();
|
||||||
|
|
||||||
|
startClone.name = "start-marker";
|
||||||
|
endClone.name = "end-marker";
|
||||||
|
|
||||||
|
startClone.traverse((child: any) => {
|
||||||
|
if (child.isMesh && child.name.toLowerCase().includes("handle")) {
|
||||||
|
child.name = "handle";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
endClone.traverse((child: any) => {
|
||||||
|
if (child.isMesh && child.name.toLowerCase().includes("handle")) {
|
||||||
|
child.name = "handle";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
startMarker.current.add(startClone);
|
||||||
|
endMarker.current.add(endClone);
|
||||||
|
|
||||||
|
hasInitialized.current = true;
|
||||||
|
}
|
||||||
|
}, [selectedEventSphere, startScene, endScene]);
|
||||||
|
|
||||||
|
// Position start/end markers
|
||||||
|
useEffect(() => {
|
||||||
|
if (!selectedEventSphere || !startMarker.current || !endMarker.current)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const selectedVehicle = vehicleStatusSample.find(
|
||||||
|
(vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedVehicle?.point?.action) {
|
||||||
|
const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action;
|
||||||
|
|
||||||
|
// Update start marker position
|
||||||
|
if (pickUpPoint) {
|
||||||
|
const localPos = new THREE.Vector3(
|
||||||
|
pickUpPoint.x,
|
||||||
|
pickUpPoint.y,
|
||||||
|
pickUpPoint.z
|
||||||
|
);
|
||||||
|
const worldPos = selectedEventSphere.localToWorld(localPos);
|
||||||
|
worldPos.y = 0; // Force y to 0
|
||||||
|
startMarker.current.position.copy(worldPos);
|
||||||
|
} else {
|
||||||
|
const defaultLocal = new THREE.Vector3(0, 0, 1.5);
|
||||||
|
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
|
||||||
|
defaultWorld.y = 0; // Force y to 0
|
||||||
|
startMarker.current.position.copy(defaultWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update end marker position
|
||||||
|
if (unLoadPoint) {
|
||||||
|
const localPos = new THREE.Vector3(
|
||||||
|
unLoadPoint.x,
|
||||||
|
unLoadPoint.y,
|
||||||
|
unLoadPoint.z
|
||||||
|
);
|
||||||
|
const worldPos = selectedEventSphere.localToWorld(localPos);
|
||||||
|
worldPos.y = 0; // Force y to 0
|
||||||
|
endMarker.current.position.copy(worldPos);
|
||||||
|
} else {
|
||||||
|
const defaultLocal = new THREE.Vector3(0, 0, -1.5);
|
||||||
|
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
|
||||||
|
defaultWorld.y = 0; // Force y to 0
|
||||||
|
endMarker.current.position.copy(defaultWorld);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedEventSphere, vehicleStatusSample]);
|
||||||
|
|
||||||
|
// Handle dragging and rotation
|
||||||
|
const handlePointerDown = (e: any, markerType: "start" | "end") => {
|
||||||
|
if (!selectedEventSphere) return;
|
||||||
|
|
||||||
|
if (e.object.name === "handle") {
|
||||||
|
setIsRotating(true);
|
||||||
|
prevMousePos.current = { x: e.clientX, y: e.clientY };
|
||||||
|
if (controls) (controls as any).enabled = false;
|
||||||
|
e.stopPropagation();
|
||||||
|
setDraggedMarker(markerType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDraggedMarker(markerType);
|
||||||
|
if (controls) (controls as any).enabled = false;
|
||||||
|
|
||||||
|
const marker =
|
||||||
|
markerType === "start" ? startMarker.current : endMarker.current;
|
||||||
|
if (!marker) return;
|
||||||
|
|
||||||
|
mouse.current.x = (e.clientX / gl.domElement.clientWidth) * 2 - 1;
|
||||||
|
mouse.current.y = -(e.clientY / gl.domElement.clientHeight) * 2 + 1;
|
||||||
|
|
||||||
|
raycaster.current.setFromCamera(mouse.current, camera);
|
||||||
|
|
||||||
|
const intersectPoint = new THREE.Vector3();
|
||||||
|
raycaster.current.ray.intersectPlane(plane.current, intersectPoint);
|
||||||
|
|
||||||
|
const offset = new THREE.Vector3().subVectors(
|
||||||
|
marker.position,
|
||||||
|
intersectPoint
|
||||||
|
);
|
||||||
|
setDragOffset(offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerMove = (e: PointerEvent) => {
|
||||||
|
if (!selectedEventSphere) return;
|
||||||
|
|
||||||
|
if (isRotating) {
|
||||||
|
const deltaX = e.clientX - prevMousePos.current.x;
|
||||||
|
prevMousePos.current = { x: e.clientX, y: e.clientY };
|
||||||
|
|
||||||
|
const rotationSpeed = 0.01;
|
||||||
|
const marker =
|
||||||
|
draggedMarker === "start" ? startMarker.current : endMarker.current;
|
||||||
|
|
||||||
|
if (marker) {
|
||||||
|
marker.rotation.y -= deltaX * rotationSpeed;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!draggedMarker || !dragOffset) return;
|
||||||
|
|
||||||
|
mouse.current.x = (e.clientX / gl.domElement.clientWidth) * 2 - 1;
|
||||||
|
mouse.current.y = -(e.clientY / gl.domElement.clientHeight) * 2 + 1;
|
||||||
|
|
||||||
|
raycaster.current.setFromCamera(mouse.current, camera);
|
||||||
|
|
||||||
|
const intersectPoint = new THREE.Vector3();
|
||||||
|
raycaster.current.ray.intersectPlane(plane.current, intersectPoint);
|
||||||
|
|
||||||
|
if (!intersectPoint) return;
|
||||||
|
|
||||||
|
const newPos = {
|
||||||
|
x: intersectPoint.x + dragOffset.x,
|
||||||
|
y: 0,
|
||||||
|
z: intersectPoint.z + dragOffset.z,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (draggedMarker === "start" && startMarker.current) {
|
||||||
|
startMarker.current.position.set(newPos.x, newPos.y, newPos.z);
|
||||||
|
} else if (draggedMarker === "end" && endMarker.current) {
|
||||||
|
endMarker.current.position.set(newPos.x, newPos.y, newPos.z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerUp = () => {
|
||||||
|
if (isRotating) {
|
||||||
|
setIsRotating(false);
|
||||||
|
if (controls) (controls as any).enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedEventSphere || !draggedMarker || !dragOffset) {
|
||||||
|
if (controls) (controls as any).enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controls) (controls as any).enabled = true;
|
||||||
|
|
||||||
|
const marker =
|
||||||
|
draggedMarker === "start" ? startMarker.current : endMarker.current;
|
||||||
|
if (!marker) return;
|
||||||
|
|
||||||
|
const worldPos = marker.position.clone();
|
||||||
|
const localPos = selectedEventSphere.worldToLocal(worldPos);
|
||||||
|
|
||||||
|
// Direct update (no snapping, ground level forced at y = 0)
|
||||||
|
const updatedLocalPos = { x: localPos.x, y: 0, z: localPos.z };
|
||||||
|
|
||||||
|
setVehicleStatusSample((prev) =>
|
||||||
|
prev.map((vehicle) => {
|
||||||
|
if (
|
||||||
|
vehicle.modelUuid === selectedEventSphere.userData.modelUuid &&
|
||||||
|
selectedEventSphere
|
||||||
|
) {
|
||||||
|
const updatedVehicle = {
|
||||||
|
...vehicle,
|
||||||
|
point: {
|
||||||
|
...vehicle.point,
|
||||||
|
action: {
|
||||||
|
...vehicle.point?.action,
|
||||||
|
...(draggedMarker === "start"
|
||||||
|
? { pickUpPoint: updatedLocalPos }
|
||||||
|
: { unLoadPoint: updatedLocalPos }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return updatedVehicle;
|
||||||
|
}
|
||||||
|
return vehicle;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setDraggedMarker(null);
|
||||||
|
setDragOffset(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("pointermove", handlePointerMove);
|
||||||
|
window.addEventListener("pointerup", handlePointerUp);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("pointermove", handlePointerMove);
|
||||||
|
window.removeEventListener("pointerup", handlePointerUp);
|
||||||
|
};
|
||||||
|
}, [draggedMarker, dragOffset, isRotating]);
|
||||||
|
|
||||||
|
if (!selectedEventSphere) {
|
||||||
|
hasInitialized.current = false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group>
|
||||||
|
<group
|
||||||
|
ref={startMarker}
|
||||||
|
scale={draggedMarker === "start" ? [1.1, 1.1, 1.1] : [1, 1, 1]}
|
||||||
|
onPointerDown={(e) => handlePointerDown(e, "start")}
|
||||||
|
/>
|
||||||
|
<group
|
||||||
|
ref={endMarker}
|
||||||
|
scale={draggedMarker === "end" ? [1.1, 1.1, 1.1] : [1, 1, 1]}
|
||||||
|
onPointerDown={(e) => handlePointerDown(e, "end")}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VehicleUI;
|
|
@ -1,61 +1,21 @@
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import VehicleInstances from './instances/vehicleInstances';
|
import VehicleInstances from "./instances/vehicleInstances";
|
||||||
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
|
import { useVehicleStore } from "../../../store/simulation/useVehicleStore";
|
||||||
import { useFloorItems } from '../../../store/store';
|
import { useFloorItems } from "../../../store/store";
|
||||||
import { useSelectedEventSphere } from '../../../store/simulation/useSimulationStore';
|
import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
|
||||||
import * as THREE from "three"
|
import VehicleUI from "../ui/vehicle/vehicleUI";
|
||||||
function Vehicles() {
|
function Vehicles() {
|
||||||
|
|
||||||
const { vehicles, addVehicle } = useVehicleStore();
|
const { vehicles, addVehicle } = useVehicleStore();
|
||||||
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere();
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
|
|
||||||
const { floorItems } = useFloorItems();
|
const { floorItems } = useFloorItems();
|
||||||
|
|
||||||
const vehicleStatusSample: VehicleEventSchema[] = [
|
const [vehicleStatusSample, setVehicleStatusSample] = useState<
|
||||||
|
VehicleEventSchema[]
|
||||||
|
>([
|
||||||
|
|
||||||
{
|
{
|
||||||
modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
|
modelUuid: "68f8dc55-7802-47fe-aa1c-eade54b4320a",
|
||||||
modelName: "AGV",
|
|
||||||
position: [97.9252965204558, 0, 37.96138815638661],
|
|
||||||
rotation: [0, 0, 0],
|
|
||||||
state: "idle",
|
|
||||||
type: "vehicle",
|
|
||||||
speed: 2.5,
|
|
||||||
point: {
|
|
||||||
uuid: "point-789",
|
|
||||||
position: [0, 1, 0],
|
|
||||||
rotation: [0, 0, 0],
|
|
||||||
action: {
|
|
||||||
actionUuid: "action-456",
|
|
||||||
actionName: "Deliver to Zone A",
|
|
||||||
actionType: "travel",
|
|
||||||
unLoadDuration: 10,
|
|
||||||
loadCapacity: 2,
|
|
||||||
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
|
|
||||||
unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
|
|
||||||
triggers: [
|
|
||||||
{
|
|
||||||
triggerUuid: "trig-001",
|
|
||||||
triggerName: "Start Travel",
|
|
||||||
triggerType: "onComplete",
|
|
||||||
delay: 0,
|
|
||||||
triggeredAsset: {
|
|
||||||
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
|
|
||||||
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
|
|
||||||
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
triggerUuid: "trig-002",
|
|
||||||
triggerName: "Complete Travel",
|
|
||||||
triggerType: "onComplete",
|
|
||||||
delay: 2,
|
|
||||||
triggeredAsset: null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
|
|
||||||
modelName: "AGV",
|
modelName: "AGV",
|
||||||
position: [89.61609306554463, 0, 33.634136622267356],
|
position: [89.61609306554463, 0, 33.634136622267356],
|
||||||
rotation: [0, 0, 0],
|
rotation: [0, 0, 0],
|
||||||
|
@ -72,8 +32,8 @@ function Vehicles() {
|
||||||
actionType: "travel",
|
actionType: "travel",
|
||||||
unLoadDuration: 10,
|
unLoadDuration: 10,
|
||||||
loadCapacity: 2,
|
loadCapacity: 2,
|
||||||
pickUpPoint: { x: 90, y: 0, z: 28 },
|
pickUpPoint: null,
|
||||||
unLoadPoint: { x: 20, y: 0, z: 10 },
|
unLoadPoint: null,
|
||||||
triggers: [
|
triggers: [
|
||||||
{
|
{
|
||||||
triggerUuid: "trig-001",
|
triggerUuid: "trig-001",
|
||||||
|
@ -82,23 +42,29 @@ function Vehicles() {
|
||||||
delay: 0,
|
delay: 0,
|
||||||
triggeredAsset: {
|
triggeredAsset: {
|
||||||
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
|
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
|
||||||
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
|
triggeredPoint: {
|
||||||
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
|
pointName: "Pickup Arm Point",
|
||||||
}
|
pointUuid: "arm-point-01",
|
||||||
|
},
|
||||||
|
triggeredAction: {
|
||||||
|
actionName: "Grab Widget",
|
||||||
|
actionUuid: "grab-001",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
triggerUuid: "trig-002",
|
triggerUuid: "trig-002",
|
||||||
triggerName: "Complete Travel",
|
triggerName: "Complete Travel",
|
||||||
triggerType: "onComplete",
|
triggerType: "onComplete",
|
||||||
delay: 2,
|
delay: 2,
|
||||||
triggeredAsset: null
|
triggeredAsset: null,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
|
modelUuid: "3a8f6da6-da57-4ef5-91e3-b8daf89e5753",
|
||||||
modelName: "forklift",
|
modelName: "forklift",
|
||||||
position: [98.85729337188162, 0, 38.36616546567653],
|
position: [98.85729337188162, 0, 38.36616546567653],
|
||||||
rotation: [0, 0, 0],
|
rotation: [0, 0, 0],
|
||||||
|
@ -115,8 +81,8 @@ function Vehicles() {
|
||||||
actionType: "travel",
|
actionType: "travel",
|
||||||
unLoadDuration: 15,
|
unLoadDuration: 15,
|
||||||
loadCapacity: 5,
|
loadCapacity: 5,
|
||||||
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
|
pickUpPoint: null,
|
||||||
unLoadPoint: { x: 20, y: 0, z: 10 },
|
unLoadPoint: null,
|
||||||
triggers: [
|
triggers: [
|
||||||
{
|
{
|
||||||
triggerUuid: "trig-001",
|
triggerUuid: "trig-001",
|
||||||
|
@ -125,40 +91,50 @@ function Vehicles() {
|
||||||
delay: 0,
|
delay: 0,
|
||||||
triggeredAsset: {
|
triggeredAsset: {
|
||||||
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
|
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
|
||||||
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
|
triggeredPoint: {
|
||||||
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
|
pointName: "Pickup Arm Point",
|
||||||
}
|
pointUuid: "arm-point-01",
|
||||||
|
},
|
||||||
|
triggeredAction: {
|
||||||
|
actionName: "Grab Widget",
|
||||||
|
actionUuid: "grab-001",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
triggerUuid: "trig-002",
|
triggerUuid: "trig-002",
|
||||||
triggerName: "Complete Travel",
|
triggerName: "Complete Travel",
|
||||||
triggerType: "onComplete",
|
triggerType: "onComplete",
|
||||||
delay: 2,
|
delay: 2,
|
||||||
triggeredAsset: null
|
triggeredAsset: null,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
]);
|
||||||
|
// useEffect(())
|
||||||
|
console.log("vehicleStatusSample", vehicleStatusSample);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addVehicle('123', vehicleStatusSample[0]);
|
addVehicle("123", vehicleStatusSample[0]);
|
||||||
// addVehicle('123', vehicleStatusSample[1]);
|
// addVehicle('123', vehicleStatusSample[1]);
|
||||||
// addVehicle('123', vehicleStatusSample[2]);
|
// addVehicle('123', vehicleStatusSample[2]);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// console.log('vehicles: ', vehicles);
|
|
||||||
}, [vehicles])
|
|
||||||
|
|
||||||
|
useEffect(() => {}, [vehicles]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<VehicleInstances />
|
<VehicleInstances />
|
||||||
|
<VehicleUI
|
||||||
|
setVehicleStatusSample={setVehicleStatusSample}
|
||||||
|
vehicleStatusSample={vehicleStatusSample}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vehicles;
|
export default Vehicles;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue