Merge pull request 'v3-Demo' (#101) from v3-Demo into main
Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/101
This commit is contained in:
commit
29de49443a
|
@ -1,6 +1,5 @@
|
||||||
import { useProductContext } from '../../../modules/simulation/products/productContext'
|
import { useProductContext } from '../../../modules/simulation/products/productContext'
|
||||||
import RegularDropDown from '../../ui/inputs/RegularDropDown';
|
import RegularDropDown from '../../ui/inputs/RegularDropDown';
|
||||||
import { useProductStore } from '../../../store/simulation/useProductStore';
|
|
||||||
import { useCompareProductDataStore, useLoadingProgress, useSaveVersion } from '../../../store/builder/store';
|
import { useCompareProductDataStore, useLoadingProgress, useSaveVersion } from '../../../store/builder/store';
|
||||||
import useModuleStore from '../../../store/useModuleStore';
|
import useModuleStore from '../../../store/useModuleStore';
|
||||||
import CompareLayOut from '../../ui/compareVersion/CompareLayOut';
|
import CompareLayOut from '../../ui/compareVersion/CompareLayOut';
|
||||||
|
@ -8,10 +7,12 @@ import ComparisonResult from '../../ui/compareVersion/ComparisonResult';
|
||||||
import { useComparisonProduct, useMainProduct } from '../../../store/simulation/useSimulationStore';
|
import { useComparisonProduct, useMainProduct } from '../../../store/simulation/useSimulationStore';
|
||||||
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
|
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useDuplicateProductStore } from '../../../modules/duplicate/duplicateStores/duplicateProductStore';
|
||||||
|
import ProductsDuplicate from '../../../modules/duplicate/duplicateSimulation/duplicateProduct/duplicateProduct';
|
||||||
|
|
||||||
function ComparisonScene() {
|
function ComparisonScene() {
|
||||||
const { isPlaying, setIsPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { products } = useProductStore();
|
const { products, getProductById } = useDuplicateProductStore();
|
||||||
const { isVersionSaved } = useSaveVersion();
|
const { isVersionSaved } = useSaveVersion();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
|
@ -28,41 +29,69 @@ function ComparisonScene() {
|
||||||
setComparisonProduct(product.productUuid, product.productName);
|
setComparisonProduct(product.productUuid, product.productName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// setCompareProductsData([
|
if (compareProductsData.length === 2) return;
|
||||||
// {
|
|
||||||
// "productUuid": "15193386-ec58-4ec6-8a92-e665a39eebf1",
|
|
||||||
// "productName": "Product 1",
|
|
||||||
// "simulationData": {
|
|
||||||
// "roiPercentage": 273.9428571428571,
|
|
||||||
// "paybackPeriod": 1.8251981643721318,
|
|
||||||
// "netProfit": 9588000,
|
|
||||||
// "productionCapacity": 4508.5,
|
|
||||||
// "machineIdleTime": 1450,
|
|
||||||
// "machineActiveTime": 430,
|
|
||||||
// "throughputData": 180.34
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "productUuid": "f614bf50-f61d-41c5-acc0-3783fb4da6b8",
|
|
||||||
// "productName": "Product 2",
|
|
||||||
// "simulationData": {
|
|
||||||
// "roiPercentage": 281.7214285714286,
|
|
||||||
// "paybackPeriod": 1.7748028701097842,
|
|
||||||
// "netProfit": 9860250,
|
|
||||||
// "productionCapacity": 4599.25,
|
|
||||||
// "machineIdleTime": 1885,
|
|
||||||
// "machineActiveTime": 646,
|
|
||||||
// "throughputData": 183.97
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ])
|
|
||||||
// }, []); // ✅ Runs only once on mount
|
|
||||||
|
|
||||||
|
// const productData = getProductById(selectedProduct.productUuid);
|
||||||
|
|
||||||
|
// const newData = {
|
||||||
|
// "productUuid": "15193386-ec58-4ec6-8a92-e665a39eebf2",
|
||||||
|
// "productName": "Product 2",
|
||||||
|
// "simulationData": {
|
||||||
|
// "roiPercentage": 281.7214285714286,
|
||||||
|
// "paybackPeriod": 1.7748028701097842,
|
||||||
|
// "netProfit": 9860250,
|
||||||
|
// "productionCapacity": 4599.25,
|
||||||
|
// "machineIdleTime": 1885,
|
||||||
|
// "machineActiveTime": 646,
|
||||||
|
// "throughputData": 183.97
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const existingIndex = compareProductsData.findIndex((item) =>
|
||||||
|
// item.productUuid === productData?.productUuid
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (existingIndex !== -1) {
|
||||||
|
// const updated = [...compareProductsData];
|
||||||
|
// updated[existingIndex] = newData;
|
||||||
|
// setCompareProductsData(updated);
|
||||||
|
// } else {
|
||||||
|
// setCompareProductsData([...compareProductsData, newData]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
setCompareProductsData([
|
||||||
|
{
|
||||||
|
"productUuid": "15193386-ec58-4ec6-8a92-e665a39eebf1",
|
||||||
|
"productName": "Product 1",
|
||||||
|
"simulationData": {
|
||||||
|
"roiPercentage": 273.9428571428571,
|
||||||
|
"paybackPeriod": 1.8251981643721318,
|
||||||
|
"netProfit": 9588000,
|
||||||
|
"productionCapacity": 4508.5,
|
||||||
|
"machineIdleTime": 1450,
|
||||||
|
"machineActiveTime": 430,
|
||||||
|
"throughputData": 180.34
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productUuid": "15193386-ec58-4ec6-8a92-e665a39eebf2",
|
||||||
|
"productName": "Product 2",
|
||||||
|
"simulationData": {
|
||||||
|
"roiPercentage": 281.7214285714286,
|
||||||
|
"paybackPeriod": 1.7748028701097842,
|
||||||
|
"netProfit": 9860250,
|
||||||
|
"productionCapacity": 4599.25,
|
||||||
|
"machineIdleTime": 1885,
|
||||||
|
"machineActiveTime": 646,
|
||||||
|
"throughputData": 183.97
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}, [compareProductsData]); // ✅ Runs only once on mount
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
|
if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
|
||||||
// console.log('compareProductsData: ', compareProductsData);
|
|
||||||
const hasMain = compareProductsData.some(val => val.productUuid === mainProduct.productUuid);
|
const hasMain = compareProductsData.some(val => val.productUuid === mainProduct.productUuid);
|
||||||
const hasComparison = compareProductsData.some(val => val.productUuid === comparisonProduct.productUuid);
|
const hasComparison = compareProductsData.some(val => val.productUuid === comparisonProduct.productUuid);
|
||||||
if (hasMain && hasComparison && mainProduct.productUuid !== comparisonProduct.productUuid) {
|
if (hasMain && hasComparison && mainProduct.productUuid !== comparisonProduct.productUuid) {
|
||||||
|
@ -91,6 +120,8 @@ function ComparisonScene() {
|
||||||
}
|
}
|
||||||
<CompareLayOut />
|
<CompareLayOut />
|
||||||
|
|
||||||
|
<ProductsDuplicate projectId={"684bcd620a64bc2a815a88d6"} />
|
||||||
|
|
||||||
{(shouldShowComparisonResult && !loadingProgress) && <ComparisonResult />}
|
{(shouldShowComparisonResult && !loadingProgress) && <ComparisonResult />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -53,7 +53,7 @@ function MainScene() {
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
const { selectedZone } = useSelectedZoneStore();
|
const { selectedZone } = useSelectedZoneStore();
|
||||||
const { setFloatingWidget } = useFloatingWidget();
|
const { setFloatingWidget } = useFloatingWidget();
|
||||||
const { clearComparisonProduct } = useComparisonProduct();
|
const { comparisonProduct, clearComparisonProduct } = useComparisonProduct();
|
||||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||||
const { setName } = useAssetsStore();
|
const { setName } = useAssetsStore();
|
||||||
const { projectId } = useParams()
|
const { projectId } = useParams()
|
||||||
|
@ -120,7 +120,9 @@ function MainScene() {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="scene-container"
|
className={`scene-container${
|
||||||
|
comparisonProduct?.productUuid ? " half-view" : ""
|
||||||
|
}`}
|
||||||
id="work-space-three-d-canvas"
|
id="work-space-three-d-canvas"
|
||||||
style={{
|
style={{
|
||||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||||
|
|
|
@ -10,15 +10,15 @@ import {
|
||||||
} from "../../../store/builder/store";
|
} from "../../../store/builder/store";
|
||||||
import Search from "../inputs/Search";
|
import Search from "../inputs/Search";
|
||||||
import OuterClick from "../../../utils/outerClick";
|
import OuterClick from "../../../utils/outerClick";
|
||||||
import { useProductStore } from "../../../store/simulation/useProductStore";
|
|
||||||
import Scene from "../../../modules/scene/scene";
|
import Scene from "../../../modules/scene/scene";
|
||||||
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
|
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
|
||||||
import { usePauseButtonStore, usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import { usePauseButtonStore, usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
|
import DuplicateScene from "../../../modules/duplicate/sceneDuplicate";
|
||||||
|
import { useDuplicateProductStore } from "../../../modules/duplicate/duplicateStores/duplicateProductStore";
|
||||||
|
|
||||||
const CompareLayOut = () => {
|
const CompareLayOut = () => {
|
||||||
const { comparisonProduct, setComparisonProduct, clearComparisonProduct } =
|
const { comparisonProduct, setComparisonProduct, clearComparisonProduct } = useComparisonProduct();
|
||||||
useComparisonProduct();
|
const { products } = useDuplicateProductStore();
|
||||||
const { products } = useProductStore();
|
|
||||||
const { setLoadingProgress } = useLoadingProgress();
|
const { setLoadingProgress } = useLoadingProgress();
|
||||||
const [width, setWidth] = useState("50vw");
|
const [width, setWidth] = useState("50vw");
|
||||||
const [isResizing, setIsResizing] = useState(false);
|
const [isResizing, setIsResizing] = useState(false);
|
||||||
|
@ -141,7 +141,8 @@ const CompareLayOut = () => {
|
||||||
{comparisonProduct && (
|
{comparisonProduct && (
|
||||||
<div className="compare-layout-canvas-container">
|
<div className="compare-layout-canvas-container">
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<Scene layout="Comparison Layout" />
|
{/* <Scene layout="Comparison Layout" /> */}
|
||||||
|
<DuplicateScene />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -26,8 +26,6 @@ function AisleInstances() {
|
||||||
return points;
|
return points;
|
||||||
}, [aisles]);
|
}, [aisles]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{toggleView &&
|
{toggleView &&
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
////////// Three and React Three Fiber Imports //////////
|
||||||
|
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
|
import * as Types from "../../../types/world/worldTypes";
|
||||||
|
import Ground from "../../scene/environment/ground";
|
||||||
|
import { Bvh } from "@react-three/drei";
|
||||||
|
import AssetsGroupDuplicate from "./duplicateAsset/assetsGroupDuplicate";
|
||||||
|
import FloorGroupDuplicate from "./duplicateFloor/floorGroupDuplicate";
|
||||||
|
import WallsDuplicate from "./duplicateWall/wallsDuplicate";
|
||||||
|
import ZoneDuplicate from "./duplicateZone/zoneDuplicate";
|
||||||
|
import AislesDuplicate from "./duplicateAisle/aislesDuplicate";
|
||||||
|
|
||||||
|
export default function BuilderDuplicate({ projectId }: { projectId: string }) {
|
||||||
|
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
|
||||||
|
const grid = useRef() as any; // Reference for a grid object for raycaster reference.
|
||||||
|
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
|
||||||
|
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
|
||||||
|
|
||||||
|
////////// Return //////////
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Ground grid={grid} plane={plane} />
|
||||||
|
|
||||||
|
<WallsDuplicate
|
||||||
|
lines={lines}
|
||||||
|
projectId={projectId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Bvh firstHitOnly>
|
||||||
|
|
||||||
|
<FloorGroupDuplicate
|
||||||
|
floorGroup={floorGroup}
|
||||||
|
lines={lines}
|
||||||
|
projectId={projectId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ZoneDuplicate projectId={projectId} />
|
||||||
|
|
||||||
|
<AssetsGroupDuplicate projectId={projectId} />
|
||||||
|
|
||||||
|
<AislesDuplicate projectId={projectId} />
|
||||||
|
</Bvh>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { getAisleApi } from '../../../../services/factoryBuilder/aisle/getAisleApi';
|
||||||
|
import AisleInstance from '../../../builder/aisle/Instances/instance/aisleInstance';
|
||||||
|
|
||||||
|
function AislesDuplicate({ projectId }: { projectId: string }) {
|
||||||
|
const [aisles, setAisles] = useState<Aisles>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAisle = async () => {
|
||||||
|
if (projectId) {
|
||||||
|
const aisles = await getAisleApi(projectId);
|
||||||
|
setAisles(aisles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchAisle()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group name='Aisles-Group'>
|
||||||
|
{aisles.map((aisle) => (
|
||||||
|
<AisleInstance aisle={aisle} key={aisle.aisleUuid} />
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AislesDuplicate
|
|
@ -0,0 +1,253 @@
|
||||||
|
import * as THREE from "three"
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||||
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||||
|
import ModelDuplicate from "./modelDuplicate";
|
||||||
|
import { useLoadingProgress } from "../../../../store/builder/store";
|
||||||
|
import { getFloorAssets } from "../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
|
||||||
|
import { FloorItems } from "../../../../types/world/worldTypes";
|
||||||
|
import { useDuplicateEventsStore } from "../../duplicateStores/duplicateEventStore";
|
||||||
|
|
||||||
|
const gltfLoaderWorker = new Worker(
|
||||||
|
new URL(
|
||||||
|
"../../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
||||||
|
import.meta.url
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function AssetsGroupDuplicate({ projectId }: { projectId: string }) {
|
||||||
|
const { setLoadingProgress } = useLoadingProgress();
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
const [assetsDuplicates, setAssetsDuplicates] = useState<Assets>([]);
|
||||||
|
const { addEvent } = useDuplicateEventsStore();
|
||||||
|
|
||||||
|
const loader = new GLTFLoader();
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
|
||||||
|
dracoLoader.setDecoderPath(
|
||||||
|
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||||
|
);
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let totalAssets = 0;
|
||||||
|
let loadedAssets = 0;
|
||||||
|
|
||||||
|
const updateLoadingProgress = (progress: number) => {
|
||||||
|
if (progress < 100) {
|
||||||
|
setLoadingProgress(progress);
|
||||||
|
} else if (progress === 100) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoadingProgress(100);
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoadingProgress(0);
|
||||||
|
}, 1500);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getFloorAssets(organization, projectId).then((data) => {
|
||||||
|
if (data.length > 0) {
|
||||||
|
const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.assetId === item.assetId));
|
||||||
|
totalAssets = uniqueItems.length;
|
||||||
|
if (totalAssets === 0) {
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gltfLoaderWorker.postMessage({ floorItems: uniqueItems });
|
||||||
|
} else {
|
||||||
|
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gltfLoaderWorker.onmessage = async (event) => {
|
||||||
|
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||||
|
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||||
|
loader.load(blobUrl, (gltf) => {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
THREE.Cache.remove(blobUrl);
|
||||||
|
THREE.Cache.add(event.data.modelID, gltf);
|
||||||
|
|
||||||
|
loadedAssets++;
|
||||||
|
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||||
|
updateLoadingProgress(progress);
|
||||||
|
|
||||||
|
if (loadedAssets === totalAssets) {
|
||||||
|
const assets: Asset[] = [];
|
||||||
|
getFloorAssets(organization, projectId).then((data: FloorItems) => {
|
||||||
|
data.map((item) => {
|
||||||
|
if (item.eventData) {
|
||||||
|
assets.push({
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
assetId: item.assetId,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
isLocked: item.isLocked,
|
||||||
|
isCollidable: false,
|
||||||
|
isVisible: item.isVisible,
|
||||||
|
opacity: 1,
|
||||||
|
eventData: item.eventData
|
||||||
|
})
|
||||||
|
|
||||||
|
if (item.eventData.type === "Vehicle") {
|
||||||
|
const vehicleEvent: VehicleEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
|
type: "vehicle",
|
||||||
|
speed: 1,
|
||||||
|
point: {
|
||||||
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
|
||||||
|
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
|
||||||
|
action: {
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: "Action 1",
|
||||||
|
actionType: "travel",
|
||||||
|
unLoadDuration: 5,
|
||||||
|
loadCapacity: 1,
|
||||||
|
steeringAngle: 0,
|
||||||
|
pickUpPoint: null,
|
||||||
|
unLoadPoint: null,
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEvent(vehicleEvent);
|
||||||
|
} else if (item.eventData.type === "Conveyor") {
|
||||||
|
const ConveyorEvent: ConveyorEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
|
type: "transfer",
|
||||||
|
speed: 1,
|
||||||
|
points: item.eventData.points?.map((point: any, index: number) => ({
|
||||||
|
uuid: point.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [point.position[0], point.position[1], point.position[2]],
|
||||||
|
rotation: [point.rotation[0], point.rotation[1], point.rotation[2]],
|
||||||
|
action: {
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: `Action 1`,
|
||||||
|
actionType: 'default',
|
||||||
|
material: 'Default material',
|
||||||
|
delay: 0,
|
||||||
|
spawnInterval: 5,
|
||||||
|
spawnCount: 1,
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
})) || [],
|
||||||
|
};
|
||||||
|
addEvent(ConveyorEvent);
|
||||||
|
} else if (item.eventData.type === "StaticMachine") {
|
||||||
|
const machineEvent: MachineEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
|
type: "machine",
|
||||||
|
point: {
|
||||||
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
|
||||||
|
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
|
||||||
|
action: {
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: "Action 1",
|
||||||
|
actionType: "process",
|
||||||
|
processTime: 10,
|
||||||
|
swapMaterial: "Default Material",
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEvent(machineEvent);
|
||||||
|
} else if (item.eventData.type === "ArmBot") {
|
||||||
|
const roboticArmEvent: RoboticArmEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
|
type: "roboticArm",
|
||||||
|
speed: 1,
|
||||||
|
point: {
|
||||||
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
|
||||||
|
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: "Action 1",
|
||||||
|
actionType: "pickAndPlace",
|
||||||
|
process: {
|
||||||
|
startPoint: null,
|
||||||
|
endPoint: null
|
||||||
|
},
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEvent(roboticArmEvent);
|
||||||
|
} else if (item.eventData.type === 'Storage') {
|
||||||
|
const storageEvent: StorageEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle",
|
||||||
|
type: "storageUnit",
|
||||||
|
point: {
|
||||||
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
|
||||||
|
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
|
||||||
|
action: {
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: "Action 1",
|
||||||
|
actionType: "store",
|
||||||
|
storageCapacity: 10,
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEvent(storageEvent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assets.push({
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
assetId: item.assetId,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
isLocked: item.isLocked,
|
||||||
|
isCollidable: false,
|
||||||
|
isVisible: item.isVisible,
|
||||||
|
opacity: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setAssetsDuplicates(assets);
|
||||||
|
});
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{assetsDuplicates.map((asset) =>
|
||||||
|
<ModelDuplicate key={`${asset.modelUuid}_Duplicate`} asset={asset} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssetsGroupDuplicate;
|
|
@ -0,0 +1,123 @@
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||||
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
|
import { useFrame, useThree } from '@react-three/fiber';
|
||||||
|
import { useRenderDistance } from '../../../../store/builder/store';
|
||||||
|
import { AssetBoundingBox } from '../../../builder/asset/functions/assetBoundingBox';
|
||||||
|
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
|
||||||
|
|
||||||
|
function ModelDuplicate({ asset }: { readonly asset: Asset }) {
|
||||||
|
const { camera } = useThree();
|
||||||
|
const { renderDistance } = useRenderDistance();
|
||||||
|
const [isRendered, setIsRendered] = useState(false);
|
||||||
|
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||||
|
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
|
||||||
|
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
|
||||||
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loader = new GLTFLoader();
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
|
||||||
|
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
const loadModel = async () => {
|
||||||
|
try {
|
||||||
|
// Check Cache
|
||||||
|
const assetId = asset.assetId;
|
||||||
|
const cachedModel = THREE.Cache.get(assetId);
|
||||||
|
if (cachedModel) {
|
||||||
|
setGltfScene(cachedModel.scene.clone());
|
||||||
|
calculateBoundingBox(cachedModel.scene);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check IndexedDB
|
||||||
|
const indexedDBModel = await retrieveGLTF(assetId);
|
||||||
|
if (indexedDBModel) {
|
||||||
|
const blobUrl = URL.createObjectURL(indexedDBModel);
|
||||||
|
loader.load(blobUrl, (gltf) => {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
THREE.Cache.remove(blobUrl);
|
||||||
|
THREE.Cache.add(assetId, gltf);
|
||||||
|
setGltfScene(gltf.scene.clone());
|
||||||
|
calculateBoundingBox(gltf.scene);
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(error) => {
|
||||||
|
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from Backend
|
||||||
|
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
|
||||||
|
const handleBackendLoad = async (gltf: GLTF) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(modelUrl);
|
||||||
|
const modelBlob = await response.blob();
|
||||||
|
await storeGLTF(assetId, modelBlob);
|
||||||
|
THREE.Cache.add(assetId, gltf);
|
||||||
|
setGltfScene(gltf.scene.clone());
|
||||||
|
calculateBoundingBox(gltf.scene);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loader.load(
|
||||||
|
modelUrl,
|
||||||
|
handleBackendLoad,
|
||||||
|
undefined,
|
||||||
|
(error) => {
|
||||||
|
echo.error(`[Backend] Error loading ${asset.modelName}:`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to load model:", asset.assetId, err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateBoundingBox = (scene: THREE.Object3D) => {
|
||||||
|
const box = new THREE.Box3().setFromObject(scene);
|
||||||
|
setBoundingBox(box);
|
||||||
|
};
|
||||||
|
|
||||||
|
loadModel();
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
const assetPosition = new THREE.Vector3(...asset.position);
|
||||||
|
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
|
||||||
|
setIsRendered(true);
|
||||||
|
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
|
||||||
|
setIsRendered(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={asset.modelUuid}
|
||||||
|
name='Asset Model'
|
||||||
|
ref={groupRef}
|
||||||
|
uuid={asset.modelUuid}
|
||||||
|
position={asset.position}
|
||||||
|
rotation={asset.rotation}
|
||||||
|
visible={asset.isVisible}
|
||||||
|
userData={asset}
|
||||||
|
>
|
||||||
|
{gltfScene && (
|
||||||
|
isRendered ? (
|
||||||
|
<primitive object={gltfScene} />
|
||||||
|
) : (
|
||||||
|
<AssetBoundingBox boundingBox={boundingBox} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModelDuplicate;
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {
|
||||||
|
useRoofVisibility,
|
||||||
|
useToggleView,
|
||||||
|
useWallVisibility,
|
||||||
|
} from "../../../../store/builder/store";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import loadFloor from "../../../builder/geomentries/floors/loadFloor";
|
||||||
|
import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi";
|
||||||
|
import objectLinesToArray from "../../../builder/geomentries/lines/lineConvertions/objectLinesToArray";
|
||||||
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
|
import hideRoof from "../../../builder/geomentries/roofs/hideRoof";
|
||||||
|
import hideWalls from "../../../builder/geomentries/walls/hideWalls";
|
||||||
|
|
||||||
|
const FloorGroupDuplicate = ({
|
||||||
|
floorGroup,
|
||||||
|
lines,
|
||||||
|
projectId
|
||||||
|
}: any) => {
|
||||||
|
const { scene, camera } = useThree();
|
||||||
|
const { toggleView } = useToggleView();
|
||||||
|
const { roofVisibility } = useRoofVisibility();
|
||||||
|
const { wallVisibility } = useWallVisibility();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const email = localStorage.getItem('email')
|
||||||
|
const organization = (email!.split("@")[1]).split(".")[0];
|
||||||
|
getLines(organization, projectId).then((data) => {
|
||||||
|
const Lines = objectLinesToArray(data);
|
||||||
|
|
||||||
|
if (Lines) {
|
||||||
|
lines.current = Lines;
|
||||||
|
loadFloor(lines, floorGroup);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
hideRoof(roofVisibility, floorGroup, camera);
|
||||||
|
hideWalls(wallVisibility, scene, camera);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group ref={floorGroup} visible={!toggleView} name="floorGroup"></group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FloorGroupDuplicate;
|
|
@ -0,0 +1,68 @@
|
||||||
|
import * as THREE from "three";
|
||||||
|
import * as Types from "../../../../types/world/worldTypes";
|
||||||
|
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||||
|
import { Base } from "@react-three/csg";
|
||||||
|
import { MeshDiscardMaterial } from "@react-three/drei";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import objectLinesToArray from "../../../builder/geomentries/lines/lineConvertions/objectLinesToArray";
|
||||||
|
import loadWalls from "../../../builder/geomentries/walls/loadWalls";
|
||||||
|
import texturePath from "../../../../assets/textures/floor/wall-tex.png";
|
||||||
|
import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi";
|
||||||
|
|
||||||
|
const WallsMeshDuplicate = ({ projectId, walls, setWalls, lines }: any) => {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
|
getLines(organization, projectId).then((data) => {
|
||||||
|
const Lines: Types.Lines = objectLinesToArray(data);
|
||||||
|
if (Lines) {
|
||||||
|
lines.current = Lines;
|
||||||
|
loadWalls(lines, setWalls);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const wallTexture = React.useMemo(() => {
|
||||||
|
const textureLoader = new THREE.TextureLoader();
|
||||||
|
const texture = textureLoader.load(texturePath);
|
||||||
|
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
|
||||||
|
texture.repeat.set(0.1, 0.1);
|
||||||
|
texture.colorSpace = THREE.SRGBColorSpace;
|
||||||
|
return texture;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{walls.map((wall: Types.Wall, index: number) => (
|
||||||
|
<mesh key={index} renderOrder={1}>
|
||||||
|
<Base
|
||||||
|
name={`Wall${index + 1}`}
|
||||||
|
geometry={wall[0]}
|
||||||
|
rotation={wall[1]}
|
||||||
|
position={wall[2]}
|
||||||
|
userData={{ WallType: wall[3], Layer: wall[4] }}
|
||||||
|
>
|
||||||
|
<meshStandardMaterial
|
||||||
|
side={THREE.DoubleSide}
|
||||||
|
color={CONSTANTS.wallConfig.defaultColor}
|
||||||
|
map={wallTexture}
|
||||||
|
/>
|
||||||
|
</Base>
|
||||||
|
<mesh
|
||||||
|
castShadow
|
||||||
|
geometry={wall[0]}
|
||||||
|
rotation={wall[1]}
|
||||||
|
position={wall[2]}
|
||||||
|
name={`WallRaycastReference_${index + 1}`}
|
||||||
|
>
|
||||||
|
<MeshDiscardMaterial />
|
||||||
|
</mesh>
|
||||||
|
</mesh>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WallsMeshDuplicate;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Geometry } from "@react-three/csg";
|
||||||
|
import {
|
||||||
|
useToggleView,
|
||||||
|
} from "../../../../store/builder/store";
|
||||||
|
import WallsMeshDuplicate from "./wallMeshDuplicate";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const WallsDuplicate = ({
|
||||||
|
lines,
|
||||||
|
projectId
|
||||||
|
}: any) => {
|
||||||
|
const { toggleView } = useToggleView();
|
||||||
|
const [walls, setWalls] = useState([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
name="Walls"
|
||||||
|
key={walls.length}
|
||||||
|
receiveShadow
|
||||||
|
visible={!toggleView}
|
||||||
|
>
|
||||||
|
<Geometry computeVertexNormals useGroups>
|
||||||
|
<WallsMeshDuplicate projectId={projectId} setWalls={setWalls} walls={walls} lines={lines} />
|
||||||
|
</Geometry>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WallsDuplicate;
|
|
@ -0,0 +1,182 @@
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { Html } from "@react-three/drei";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import {
|
||||||
|
useToggleView,
|
||||||
|
useZonePoints,
|
||||||
|
} from "../../../../store/builder/store";
|
||||||
|
import { getZonesApi } from "../../../../services/factoryBuilder/zones/getZonesApi";
|
||||||
|
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||||
|
import * as turf from "@turf/turf";
|
||||||
|
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
|
||||||
|
|
||||||
|
const ZoneDuplicate = ({ projectId }: { projectId: string }) => {
|
||||||
|
const [zones, setZones] = useState([]);
|
||||||
|
const { setZonePoints } = useZonePoints();
|
||||||
|
const { selectedZone } = useSelectedZoneStore();
|
||||||
|
const { toggleView } = useToggleView();
|
||||||
|
|
||||||
|
const groupsRef = useRef<any>();
|
||||||
|
|
||||||
|
const zoneMaterial = useMemo(
|
||||||
|
() =>
|
||||||
|
new THREE.ShaderMaterial({
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
vertexShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main(){
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
vUv = uv;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform vec3 uOuterColor;
|
||||||
|
void main(){
|
||||||
|
float alpha = 1.0 - vUv.y;
|
||||||
|
gl_FragColor = vec4(uOuterColor, alpha);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
uniforms: {
|
||||||
|
uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) },
|
||||||
|
},
|
||||||
|
transparent: true,
|
||||||
|
depthWrite: false,
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchZones = async () => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
if (!email) return;
|
||||||
|
|
||||||
|
const organization = email.split("@")[1].split(".")[0];
|
||||||
|
const data = await getZonesApi(organization, projectId);
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
const fetchedZones = data.map((zone: any) => ({
|
||||||
|
zoneUuid: zone.zoneUuid,
|
||||||
|
zoneName: zone.zoneName,
|
||||||
|
points: zone.points,
|
||||||
|
viewPortCenter: zone.viewPortCenter,
|
||||||
|
viewPortposition: zone.viewPortposition,
|
||||||
|
layer: zone.layer,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setZones(fetchedZones);
|
||||||
|
|
||||||
|
const fetchedPoints = data.flatMap((zone: any) =>
|
||||||
|
zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
|
||||||
|
);
|
||||||
|
|
||||||
|
setZonePoints(fetchedPoints);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchZones();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group ref={groupsRef} name="zoneGroup">
|
||||||
|
<group name="zones" visible={!toggleView}>
|
||||||
|
{zones.map((zone: any) => (
|
||||||
|
<group
|
||||||
|
key={zone.zoneUuid}
|
||||||
|
name={zone.zoneName}
|
||||||
|
visible={zone.zoneUuid === selectedZone.zoneUuid}
|
||||||
|
>
|
||||||
|
{zone.points
|
||||||
|
.slice(0, -1)
|
||||||
|
.map((point: [number, number, number], index: number) => {
|
||||||
|
const nextPoint = zone.points[index + 1];
|
||||||
|
|
||||||
|
const point1 = new THREE.Vector3(point[0], point[1], point[2]);
|
||||||
|
const point2 = new THREE.Vector3(
|
||||||
|
nextPoint[0],
|
||||||
|
nextPoint[1],
|
||||||
|
nextPoint[2]
|
||||||
|
);
|
||||||
|
|
||||||
|
const planeWidth = point1.distanceTo(point2);
|
||||||
|
const planeHeight = CONSTANTS.zoneConfig.height;
|
||||||
|
|
||||||
|
const midpoint = new THREE.Vector3(
|
||||||
|
(point1.x + point2.x) / 2,
|
||||||
|
CONSTANTS.zoneConfig.height / 2 +
|
||||||
|
(zone.layer - 1) * CONSTANTS.zoneConfig.height,
|
||||||
|
(point1.z + point2.z) / 2
|
||||||
|
);
|
||||||
|
|
||||||
|
const angle = Math.atan2(
|
||||||
|
point2.z - point1.z,
|
||||||
|
point2.x - point1.x
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
key={index}
|
||||||
|
position={midpoint}
|
||||||
|
rotation={[0, -angle, 0]}
|
||||||
|
>
|
||||||
|
<planeGeometry args={[planeWidth, planeHeight]} />
|
||||||
|
<primitive
|
||||||
|
object={zoneMaterial.clone()}
|
||||||
|
attach="material"
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!toggleView &&
|
||||||
|
(() => {
|
||||||
|
const points3D = zone.points || [];
|
||||||
|
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
|
||||||
|
|
||||||
|
// Ensure the polygon is closed
|
||||||
|
if (
|
||||||
|
coords2D.length >= 4 &&
|
||||||
|
(coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
|
||||||
|
coords2D[0][1] !== coords2D[coords2D.length - 1][1])
|
||||||
|
) {
|
||||||
|
coords2D.push(coords2D[0]);
|
||||||
|
}
|
||||||
|
if (coords2D.length < 4) return null;
|
||||||
|
|
||||||
|
const polygon = turf.polygon([coords2D]);
|
||||||
|
const center2D = turf.center(polygon).geometry.coordinates;
|
||||||
|
|
||||||
|
// Calculate the average Y value
|
||||||
|
const sumY = points3D.reduce(
|
||||||
|
(sum: number, p: any) => sum + p[1],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const avgY = points3D.length > 0 ? sumY / points3D.length : 0;
|
||||||
|
|
||||||
|
const htmlPosition: [number, number, number] = [
|
||||||
|
center2D[0],
|
||||||
|
avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5,
|
||||||
|
center2D[1],
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Html
|
||||||
|
// data
|
||||||
|
key={zone.zoneUuid}
|
||||||
|
position={htmlPosition}
|
||||||
|
// class
|
||||||
|
className="zone-name-wrapper"
|
||||||
|
// others
|
||||||
|
center
|
||||||
|
>
|
||||||
|
<div className="zone-name">{zone.zoneName}</div>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</group>
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ZoneDuplicate;
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { CameraControls } from "@react-three/drei";
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||||
|
|
||||||
|
import { useDuplicatedCamData, useToggleView } from "../../../store/builder/store";
|
||||||
|
|
||||||
|
export default function ControlsDuplicate() {
|
||||||
|
const controlsRef = useRef<CameraControls>(null);
|
||||||
|
const { toggleView } = useToggleView();
|
||||||
|
const state = useThree();
|
||||||
|
const { duplicatedCamData } = useDuplicatedCamData();
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
controlsRef.current?.setPosition(...duplicatedCamData.camPosition, true);
|
||||||
|
controlsRef.current?.setTarget(...duplicatedCamData.camTarget, true);
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CameraControls
|
||||||
|
makeDefault
|
||||||
|
ref={controlsRef}
|
||||||
|
minDistance={toggleView ? CONSTANTS.twoDimension.minDistance : CONSTANTS.threeDimension.minDistance}
|
||||||
|
maxDistance={CONSTANTS.thirdPersonControls.maxDistance}
|
||||||
|
minZoom={CONSTANTS.thirdPersonControls.minZoom}
|
||||||
|
maxZoom={CONSTANTS.thirdPersonControls.maxZoom}
|
||||||
|
maxPolarAngle={CONSTANTS.thirdPersonControls.maxPolarAngle}
|
||||||
|
camera={state.camera}
|
||||||
|
verticalDragToForward={true}
|
||||||
|
boundaryEnclosesCamera={true}
|
||||||
|
dollyToCursor={toggleView}
|
||||||
|
>
|
||||||
|
</CameraControls>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { EffectComposer, N8AO } from "@react-three/postprocessing";
|
||||||
|
|
||||||
|
export default function PostProcessingDuplicate() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EffectComposer autoClear={false}>
|
||||||
|
<N8AO
|
||||||
|
color="black"
|
||||||
|
aoRadius={20}
|
||||||
|
intensity={7}
|
||||||
|
distanceFalloff={4}
|
||||||
|
aoSamples={32}
|
||||||
|
denoiseRadius={6}
|
||||||
|
denoiseSamples={16}
|
||||||
|
/>
|
||||||
|
</EffectComposer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
||||||
|
import useModuleStore from "../../../../store/useModuleStore";
|
||||||
|
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
|
function PointsDuplicate() {
|
||||||
|
const { events } = useEventsStore();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeModule === "simulation" && (
|
||||||
|
<>
|
||||||
|
<group name="EventPointsGroup" visible={!isPlaying}>
|
||||||
|
{events.map((event, index) => {
|
||||||
|
const usedEvent = event;
|
||||||
|
|
||||||
|
if (usedEvent.type === "transfer") {
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={`${index}-${usedEvent.modelUuid}`}
|
||||||
|
position={usedEvent.position}
|
||||||
|
rotation={usedEvent.rotation}
|
||||||
|
>
|
||||||
|
{usedEvent.points.map((point, j) => (
|
||||||
|
<mesh
|
||||||
|
name="Event-Sphere"
|
||||||
|
uuid={point.uuid}
|
||||||
|
key={`${index}-${point.uuid}`}
|
||||||
|
position={new THREE.Vector3(...point.position)}
|
||||||
|
userData={{
|
||||||
|
modelUuid: usedEvent.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshStandardMaterial color="orange" />
|
||||||
|
</mesh>
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
} else if (usedEvent.type === "vehicle") {
|
||||||
|
const point = usedEvent.point;
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={`${index}-${usedEvent.modelUuid}`}
|
||||||
|
position={usedEvent.position}
|
||||||
|
rotation={usedEvent.rotation}
|
||||||
|
>
|
||||||
|
<mesh
|
||||||
|
name="Event-Sphere"
|
||||||
|
uuid={point.uuid}
|
||||||
|
position={new THREE.Vector3(...point.position)}
|
||||||
|
userData={{
|
||||||
|
modelUuid: usedEvent.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshStandardMaterial color="blue" />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
} else if (usedEvent.type === "roboticArm") {
|
||||||
|
const point = usedEvent.point;
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={`${index}-${usedEvent.modelUuid}`}
|
||||||
|
position={usedEvent.position}
|
||||||
|
rotation={usedEvent.rotation}
|
||||||
|
>
|
||||||
|
<mesh
|
||||||
|
name="Event-Sphere"
|
||||||
|
uuid={point.uuid}
|
||||||
|
position={new THREE.Vector3(...point.position)}
|
||||||
|
userData={{
|
||||||
|
modelUuid: usedEvent.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshStandardMaterial color="green" />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
} else if (usedEvent.type === "machine") {
|
||||||
|
const point = usedEvent.point;
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={`${index}-${usedEvent.modelUuid}`}
|
||||||
|
position={usedEvent.position}
|
||||||
|
rotation={usedEvent.rotation}
|
||||||
|
>
|
||||||
|
<mesh
|
||||||
|
name="Event-Sphere"
|
||||||
|
uuid={point.uuid}
|
||||||
|
position={new THREE.Vector3(...point.position)}
|
||||||
|
userData={{
|
||||||
|
modelUuid: usedEvent.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshStandardMaterial color="purple" />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
} else if (usedEvent.type === "storageUnit") {
|
||||||
|
const point = usedEvent.point;
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
key={`${index}-${usedEvent.modelUuid}`}
|
||||||
|
position={usedEvent.position}
|
||||||
|
rotation={usedEvent.rotation}
|
||||||
|
>
|
||||||
|
<mesh
|
||||||
|
name="Event-Sphere"
|
||||||
|
uuid={point.uuid}
|
||||||
|
position={new THREE.Vector3(...point.position)}
|
||||||
|
userData={{
|
||||||
|
modelUuid: usedEvent.modelUuid,
|
||||||
|
pointUuid: point.uuid,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshStandardMaterial color="red" />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</group>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PointsDuplicate;
|
|
@ -0,0 +1,130 @@
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { upsertProductOrEventApi } from '../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||||
|
import { getAllProductsApi } from '../../../../services/simulation/products/getallProductsApi';
|
||||||
|
import { usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
|
||||||
|
import { useSceneContext } from '../../../scene/sceneContext';
|
||||||
|
import { useProductContext } from '../../../simulation/products/productContext';
|
||||||
|
import { useComparisonProduct } from '../../../../store/simulation/useSimulationStore';
|
||||||
|
import { useDuplicateProductStore } from '../../duplicateStores/duplicateProductStore';
|
||||||
|
|
||||||
|
function ProductsDuplicate({ projectId }: { projectId: string }) {
|
||||||
|
const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout } = useSceneContext();
|
||||||
|
const { products, getProductById, setProducts } = useDuplicateProductStore();
|
||||||
|
const { selectedProductStore } = useProductContext();
|
||||||
|
const { selectedProduct, setSelectedProduct } = selectedProductStore();
|
||||||
|
const { addVehicle, clearvehicles } = vehicleStore();
|
||||||
|
const { addArmBot, clearArmBots } = armBotStore();
|
||||||
|
const { addMachine, clearMachines } = machineStore();
|
||||||
|
const { addConveyor, clearConveyors } = conveyorStore();
|
||||||
|
const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
const { comparisonProduct } = useComparisonProduct();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (layout === 'Comparison Layout' && comparisonProduct) {
|
||||||
|
setSelectedProduct(comparisonProduct.productUuid, comparisonProduct.productName);
|
||||||
|
}
|
||||||
|
}, [comparisonProduct])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getAllProductsApi(projectId || '').then((data) => {
|
||||||
|
if (data.length > 0) {
|
||||||
|
setProducts(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct.productUuid) {
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (product) {
|
||||||
|
clearvehicles();
|
||||||
|
product.eventDatas.forEach(events => {
|
||||||
|
if (events.type === 'vehicle') {
|
||||||
|
addVehicle(selectedProduct.productUuid, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products, isReset, isPlaying]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct.productUuid) {
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (product) {
|
||||||
|
clearArmBots();
|
||||||
|
product.eventDatas.forEach(events => {
|
||||||
|
if (events.type === 'roboticArm') {
|
||||||
|
addArmBot(selectedProduct.productUuid, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products, isReset, isPlaying]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct.productUuid) {
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (product) {
|
||||||
|
clearConveyors();
|
||||||
|
product.eventDatas.forEach(events => {
|
||||||
|
if (events.type === 'transfer') {
|
||||||
|
addConveyor(selectedProduct.productUuid, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products, isReset, isPlaying]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct.productUuid) {
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (product) {
|
||||||
|
clearMachines();
|
||||||
|
product.eventDatas.forEach(events => {
|
||||||
|
if (events.type === 'machine') {
|
||||||
|
addMachine(selectedProduct.productUuid, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products, isReset, isPlaying]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct.productUuid) {
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (product) {
|
||||||
|
clearStorageUnits();
|
||||||
|
product.eventDatas.forEach(event => {
|
||||||
|
if (event.type === 'storageUnit') {
|
||||||
|
addStorageUnit(selectedProduct.productUuid, event);
|
||||||
|
|
||||||
|
if (event.point.action.actionType === 'retrieve') {
|
||||||
|
const storageAction = event.point.action;
|
||||||
|
const materials = Array.from({ length: storageAction.storageCapacity }, () => ({
|
||||||
|
materialType: storageAction.materialType || 'Default material',
|
||||||
|
materialId: THREE.MathUtils.generateUUID()
|
||||||
|
}));
|
||||||
|
|
||||||
|
setCurrentMaterials(event.modelUuid, materials);
|
||||||
|
updateCurrentLoad(event.modelUuid, storageAction.storageCapacity);
|
||||||
|
} else {
|
||||||
|
setCurrentMaterials(event.modelUuid, []);
|
||||||
|
updateCurrentLoad(event.modelUuid, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products, isReset, isPlaying]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductsDuplicate
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ProductsDuplicate from './duplicateProduct/duplicateProduct'
|
||||||
|
import PointsDuplicate from './duplicatePoint/duplicatePoint'
|
||||||
|
import TriggerDuplicate from './duplicateTrigger/duplicateTrigger'
|
||||||
|
|
||||||
|
function SimulationDuplicate() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<PointsDuplicate />
|
||||||
|
|
||||||
|
<TriggerDuplicate />
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SimulationDuplicate
|
|
@ -0,0 +1,165 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useThree } from "@react-three/fiber";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { QuadraticBezierLine } from "@react-three/drei";
|
||||||
|
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||||
|
import { Arrows } from "../../../simulation/events/arrows/arrows";
|
||||||
|
import { useProductContext } from "../../../simulation/products/productContext";
|
||||||
|
import { useDuplicateProductStore } from "../../duplicateStores/duplicateProductStore";
|
||||||
|
|
||||||
|
interface ConnectionLine {
|
||||||
|
id: string;
|
||||||
|
startPointUuid: string;
|
||||||
|
endPointUuid: string;
|
||||||
|
trigger: TriggerSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TriggerDuplicate() {
|
||||||
|
const { scene } = useThree();
|
||||||
|
const { selectedProductStore } = useProductContext();
|
||||||
|
const { products, getProductById } = useDuplicateProductStore();
|
||||||
|
const { selectedProduct } = selectedProductStore();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
|
||||||
|
const [connections, setConnections] = useState<ConnectionLine[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const newConnections: ConnectionLine[] = [];
|
||||||
|
|
||||||
|
const product = getProductById(selectedProduct.productUuid);
|
||||||
|
if (!product || products.length === 0) return;
|
||||||
|
|
||||||
|
product.eventDatas.forEach(event => {
|
||||||
|
// Handle Conveyor points
|
||||||
|
if (event.type === "transfer" && 'points' in event) {
|
||||||
|
event.points.forEach(point => {
|
||||||
|
if (point.action?.triggers) {
|
||||||
|
point.action.triggers.forEach(trigger => {
|
||||||
|
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||||
|
newConnections.push({
|
||||||
|
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||||
|
startPointUuid: point.uuid,
|
||||||
|
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||||
|
trigger
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle Vehicle point
|
||||||
|
else if (event.type === "vehicle" && 'point' in event) {
|
||||||
|
const point = event.point;
|
||||||
|
if (point.action?.triggers) {
|
||||||
|
point.action.triggers.forEach(trigger => {
|
||||||
|
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||||
|
newConnections.push({
|
||||||
|
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||||
|
startPointUuid: point.uuid,
|
||||||
|
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||||
|
trigger
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle Robotic Arm points
|
||||||
|
else if (event.type === "roboticArm" && 'point' in event) {
|
||||||
|
const point = event.point;
|
||||||
|
point.actions?.forEach(action => {
|
||||||
|
action.triggers?.forEach(trigger => {
|
||||||
|
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||||
|
newConnections.push({
|
||||||
|
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||||
|
startPointUuid: point.uuid,
|
||||||
|
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||||
|
trigger
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle Machine point
|
||||||
|
else if (event.type === "machine" && 'point' in event) {
|
||||||
|
const point = event.point;
|
||||||
|
if (point.action?.triggers) {
|
||||||
|
point.action.triggers.forEach(trigger => {
|
||||||
|
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||||
|
newConnections.push({
|
||||||
|
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||||
|
startPointUuid: point.uuid,
|
||||||
|
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||||
|
trigger
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle StorageUnit point
|
||||||
|
else if (event.type === "storageUnit" && 'point' in event) {
|
||||||
|
const point = event.point;
|
||||||
|
if (point.action?.triggers) {
|
||||||
|
point.action.triggers.forEach(trigger => {
|
||||||
|
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||||
|
newConnections.push({
|
||||||
|
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||||
|
startPointUuid: point.uuid,
|
||||||
|
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||||
|
trigger
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setConnections(newConnections);
|
||||||
|
}, [products, selectedProduct.productUuid]);
|
||||||
|
|
||||||
|
const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => {
|
||||||
|
const pointObj = scene.getObjectByProperty("uuid", pointUuid);
|
||||||
|
if (!pointObj) return null;
|
||||||
|
|
||||||
|
const worldPosition = new THREE.Vector3();
|
||||||
|
pointObj.getWorldPosition(worldPosition);
|
||||||
|
return worldPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group name="simulationConnectionGroup" visible={!isPlaying}>
|
||||||
|
{connections.map((connection) => {
|
||||||
|
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
|
||||||
|
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
|
||||||
|
|
||||||
|
if (!startPoint || !endPoint) return null;
|
||||||
|
|
||||||
|
const distance = startPoint.distanceTo(endPoint);
|
||||||
|
const heightFactor = Math.max(0.5, distance * 0.2);
|
||||||
|
const midPoint = new THREE.Vector3(
|
||||||
|
(startPoint.x + endPoint.x) / 2,
|
||||||
|
Math.max(startPoint.y, endPoint.y) + heightFactor,
|
||||||
|
(startPoint.z + endPoint.z) / 2
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QuadraticBezierLine
|
||||||
|
key={connection.id}
|
||||||
|
start={startPoint.toArray()}
|
||||||
|
end={endPoint.toArray()}
|
||||||
|
mid={midPoint.toArray()}
|
||||||
|
color={"#c4abf1"}
|
||||||
|
lineWidth={4}
|
||||||
|
dashed={true}
|
||||||
|
dashSize={0.75}
|
||||||
|
dashScale={20}
|
||||||
|
userData={connection.trigger}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<Arrows connections={connections} color={'#c4abf1'} />
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TriggerDuplicate;
|
|
@ -0,0 +1,357 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
type EventsStore = {
|
||||||
|
events: EventsSchema[];
|
||||||
|
|
||||||
|
// Event-level actions
|
||||||
|
addEvent: (event: EventsSchema) => void;
|
||||||
|
removeEvent: (modelUuid: string) => void;
|
||||||
|
clearEvents: () => void;
|
||||||
|
updateEvent: (modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Point-level actions
|
||||||
|
addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void;
|
||||||
|
removePoint: (modelUuid: string, pointUuid: string) => void;
|
||||||
|
updatePoint: (
|
||||||
|
modelUuid: string,
|
||||||
|
pointUuid: string,
|
||||||
|
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema>
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Action-level actions
|
||||||
|
addAction: (
|
||||||
|
modelUuid: string,
|
||||||
|
pointUuid: string,
|
||||||
|
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']
|
||||||
|
) => void;
|
||||||
|
removeAction: (actionUuid: string) => void;
|
||||||
|
updateAction: (
|
||||||
|
actionUuid: string,
|
||||||
|
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']>
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
// Trigger-level actions
|
||||||
|
addTrigger: (actionUuid: string, trigger: TriggerSchema) => void;
|
||||||
|
removeTrigger: (triggerUuid: string) => void;
|
||||||
|
updateTrigger: (triggerUuid: string, updates: Partial<TriggerSchema>) => void;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined;
|
||||||
|
getPointByUuid: (modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
|
||||||
|
getActionByUuid: (actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||||
|
getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDuplicateEventsStore = create<EventsStore>()(
|
||||||
|
immer((set, get) => ({
|
||||||
|
events: [],
|
||||||
|
|
||||||
|
// Event-level actions
|
||||||
|
addEvent: (event) => {
|
||||||
|
set((state) => {
|
||||||
|
if (!state.events.some(e => 'modelUuid' in e && e.modelUuid === event.modelUuid)) {
|
||||||
|
state.events.push(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeEvent: (modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
state.events = state.events.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearEvents: () => {
|
||||||
|
set((state) => {
|
||||||
|
state.events = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateEvent: (modelUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event) {
|
||||||
|
Object.assign(event, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Point-level actions
|
||||||
|
addPoint: (modelUuid, point) => {
|
||||||
|
set((state) => {
|
||||||
|
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid);
|
||||||
|
if (!existingPoint) {
|
||||||
|
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event) {
|
||||||
|
if (!(event as any).point || (event as any).point.uuid !== point.uuid) {
|
||||||
|
(event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removePoint: (modelUuid, pointUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
(event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid);
|
||||||
|
}
|
||||||
|
// For single-point events, you might want to handle differently
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePoint: (modelUuid, pointUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
if (point) {
|
||||||
|
Object.assign(point, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
Object.assign((event as any).point, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Action-level actions
|
||||||
|
addAction: (modelUuid, pointUuid, action) => {
|
||||||
|
set((state) => {
|
||||||
|
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
|
||||||
|
point.action = action as any;
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
|
||||||
|
point.action = action;
|
||||||
|
} else if ('actions' in point && !point.actions.some((a: any) => a.actionUuid === action.actionUuid)) {
|
||||||
|
point.actions.push(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAction: (actionUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
// Handle removal for single action points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if (event.type === "roboticArm") {
|
||||||
|
if ('actions' in point) {
|
||||||
|
point.actions = point.actions.filter((a: any) => a.actionUuid !== actionUuid);
|
||||||
|
}
|
||||||
|
} else if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
// Handle single action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAction: (actionUuid, updates) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
Object.assign(point.action, updates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
Object.assign(point.action, updates);
|
||||||
|
return;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) {
|
||||||
|
Object.assign(action, updates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Trigger-level actions
|
||||||
|
addTrigger: (actionUuid, trigger) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
|
||||||
|
point.action.triggers.push(trigger);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point: MachinePointSchema | VehiclePointSchema = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
|
||||||
|
point.action.triggers.push(trigger);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = (point as RoboticArmPointSchema).actions.find((a) => a.actionUuid === actionUuid);
|
||||||
|
if (action && !action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
|
||||||
|
action.triggers.push(trigger);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTrigger: (triggerUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTrigger: (triggerUuid, updates) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getEventByModelUuid: (modelUuid) => {
|
||||||
|
return get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
},
|
||||||
|
|
||||||
|
getPointByUuid: (modelUuid, pointUuid) => {
|
||||||
|
const event = get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
return (event as any).point;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getActionByUuid: (actionUuid) => {
|
||||||
|
const state = get();
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
return point.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
return point.action;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTriggerByUuid: (triggerUuid) => {
|
||||||
|
const state = get();
|
||||||
|
for (const event of state.events) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) return trigger;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
|
@ -0,0 +1,798 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
type ProductsStore = {
|
||||||
|
products: productsSchema;
|
||||||
|
|
||||||
|
// Product-level actions
|
||||||
|
addProduct: (productName: string, productUuid: string) => void;
|
||||||
|
setProducts: (products: productsSchema) => void;
|
||||||
|
clearProducts: () => void;
|
||||||
|
removeProduct: (productUuid: string) => void;
|
||||||
|
updateProduct: (productUuid: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void;
|
||||||
|
|
||||||
|
// Event-level actions
|
||||||
|
addEvent: (productUuid: string, event: EventsSchema) => void;
|
||||||
|
removeEvent: (productUuid: string, modelUuid: string) => void;
|
||||||
|
deleteEvent: (modelUuid: string) => void;
|
||||||
|
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Point-level actions
|
||||||
|
addPoint: (productUuid: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void;
|
||||||
|
removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => void;
|
||||||
|
updatePoint: (
|
||||||
|
productUuid: string,
|
||||||
|
modelUuid: string,
|
||||||
|
pointUuid: string,
|
||||||
|
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema>
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Action-level actions
|
||||||
|
addAction: (
|
||||||
|
productUuid: string,
|
||||||
|
modelUuid: string,
|
||||||
|
pointUuid: string,
|
||||||
|
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
|
||||||
|
updateAction: (
|
||||||
|
productUuid: string,
|
||||||
|
actionUuid: string,
|
||||||
|
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']>
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Trigger-level actionss
|
||||||
|
addTrigger: (
|
||||||
|
productUuid: string,
|
||||||
|
actionUuid: string,
|
||||||
|
trigger: TriggerSchema
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
removeTrigger: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
|
||||||
|
updateTrigger: (
|
||||||
|
productUuid: string,
|
||||||
|
triggerUuid: string,
|
||||||
|
updates: Partial<TriggerSchema>
|
||||||
|
) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Renaming functions
|
||||||
|
renameProduct: (productUuid: string, newName: string) => void;
|
||||||
|
renameAction: (productUuid: string, actionUuid: string, newName: string) => EventsSchema | undefined;
|
||||||
|
renameTrigger: (productUuid: string, triggerUuid: string, newName: string) => EventsSchema | undefined;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getProductById: (productUuid: string) => { productName: string; productUuid: string; eventDatas: EventsSchema[] } | undefined;
|
||||||
|
getEventByModelUuid: (productUuid: string, modelUuid: string) => EventsSchema | undefined;
|
||||||
|
getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
|
||||||
|
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
|
||||||
|
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
|
||||||
|
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
|
||||||
|
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||||
|
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||||
|
getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||||
|
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||||
|
getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||||
|
getTriggerByUuid: (productUuid: string, triggerUuid: string) => TriggerSchema | undefined;
|
||||||
|
getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDuplicateProductStore = create<ProductsStore>()(
|
||||||
|
immer((set, get) => ({
|
||||||
|
products: [],
|
||||||
|
|
||||||
|
// Product-level actions
|
||||||
|
addProduct: (productName, productUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const existingProduct = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!existingProduct) {
|
||||||
|
const newProduct = {
|
||||||
|
productName,
|
||||||
|
productUuid: productUuid,
|
||||||
|
eventDatas: []
|
||||||
|
};
|
||||||
|
state.products.push(newProduct);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setProducts: (products) => {
|
||||||
|
set((state) => {
|
||||||
|
state.products = products;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearProducts: () => {
|
||||||
|
set((state) => {
|
||||||
|
state.products = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeProduct: (productUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
state.products = state.products.filter(p => p.productUuid !== productUuid);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateProduct: (productUuid, updates) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
Object.assign(product, updates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Event-level actions
|
||||||
|
addEvent: (productUuid, event) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const existingEvent = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === event.modelUuid);
|
||||||
|
if (!existingEvent) {
|
||||||
|
product.eventDatas.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeEvent: (productUuid, modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteEvent: (modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
for (const product of state.products) {
|
||||||
|
product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateEvent: (productUuid, modelUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event) {
|
||||||
|
Object.assign(event, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Point-level actions
|
||||||
|
addPoint: (productUuid, modelUuid, point) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid);
|
||||||
|
if (!existingPoint) {
|
||||||
|
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event) {
|
||||||
|
const existingPoint = (event as any).point?.uuid === point.uuid;
|
||||||
|
if (!existingPoint) {
|
||||||
|
(event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removePoint: (productUuid, modelUuid, pointUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
(event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid);
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
// For events with single point, we can't remove it, only reset to empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePoint: (productUuid, modelUuid, pointUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
if (point) {
|
||||||
|
Object.assign(point, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
Object.assign((event as any).point, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Action-level actions
|
||||||
|
addAction: (productUuid, modelUuid, pointUuid, action) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (event && 'points' in event) {
|
||||||
|
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
|
||||||
|
point.action = action as any;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
if ('action' in (event as any).point) {
|
||||||
|
if (!(event as any).point.action || (event as any).point.action.actionUuid !== action.actionUuid) {
|
||||||
|
(event as any).point.action = action;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
} else if ('actions' in (event as any).point) {
|
||||||
|
const existingAction = (event as any).point.actions.find((a: any) => a.actionUuid === action.actionUuid);
|
||||||
|
if (!existingAction) {
|
||||||
|
(event as any).point.actions.push(action);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAction: (productUuid, actionUuid) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
// Handle ConveyorEventSchema
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if (event.type === "roboticArm") {
|
||||||
|
if ('actions' in point) {
|
||||||
|
const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (index !== -1) {
|
||||||
|
point.actions.splice(index, 1);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
point.action = undefined;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAction: (productUuid, actionUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
Object.assign(point.action, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
Object.assign(point.action, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) {
|
||||||
|
Object.assign(action, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Trigger-level actions
|
||||||
|
addTrigger: (productUuid, actionUuid, trigger) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
const existingTrigger = point.action.triggers.find(t => t.triggerUuid === trigger.triggerUuid);
|
||||||
|
if (!existingTrigger) {
|
||||||
|
point.action.triggers.push(trigger);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
const existingTrigger = point.action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid);
|
||||||
|
if (!existingTrigger) {
|
||||||
|
point.action.triggers.push(trigger);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) {
|
||||||
|
const existingTrigger = action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid);
|
||||||
|
if (!existingTrigger) {
|
||||||
|
action.triggers.push(trigger);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTrigger: (productUuid, triggerUuid) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
const Trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
|
||||||
|
if (Trigger) {
|
||||||
|
point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
const Trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (Trigger) {
|
||||||
|
point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
const Trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (Trigger) {
|
||||||
|
action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTrigger: (productUuid, triggerUuid, updates) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
Object.assign(trigger, updates);
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Renaming functions
|
||||||
|
renameProduct: (productUuid, newName) => {
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
product.productName = newName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
renameAction: (productUuid, actionUuid, newName) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && point.action.actionUuid === actionUuid) {
|
||||||
|
point.action.actionName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action.actionUuid === actionUuid) {
|
||||||
|
point.action.actionName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) {
|
||||||
|
action.actionName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
renameTrigger: (productUuid, triggerUuid, newName) => {
|
||||||
|
let updatedEvent: EventsSchema | undefined;
|
||||||
|
set((state) => {
|
||||||
|
const product = state.products.find(p => p.productUuid === productUuid);
|
||||||
|
if (product) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
trigger.triggerName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && 'triggers' in point.action) {
|
||||||
|
const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
trigger.triggerName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if ('triggers' in action) {
|
||||||
|
const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
|
||||||
|
if (trigger) {
|
||||||
|
trigger.triggerName = newName;
|
||||||
|
updatedEvent = JSON.parse(JSON.stringify(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return updatedEvent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getProductById: (productUuid) => {
|
||||||
|
return get().products.find(p => p.productUuid === productUuid);
|
||||||
|
},
|
||||||
|
|
||||||
|
getEventByModelUuid: (productUuid, modelUuid) => {
|
||||||
|
const product = get().getProductById(productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
},
|
||||||
|
|
||||||
|
getEventByActionUuid: (productUuid, actionUuid) => {
|
||||||
|
const product = get().getProductById(productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.actionUuid === actionUuid) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
return event;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEventByTriggerUuid: (productUuid, triggerUuid) => {
|
||||||
|
const product = get().getProductById(productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.triggers?.some(t => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point) {
|
||||||
|
if (point.action?.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if (action.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEventByPointUuid: (productUuid, pointUuid) => {
|
||||||
|
const product = get().getProductById(productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
if ((event as ConveyorEventSchema).points.some(p => p.uuid === pointUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
if ((event as any).point?.uuid === pointUuid) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPointByUuid: (productUuid, modelUuid, pointUuid) => {
|
||||||
|
const event = get().getEventByModelUuid(productUuid, modelUuid);
|
||||||
|
if (!event) return undefined;
|
||||||
|
|
||||||
|
if ('points' in event) {
|
||||||
|
return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
|
||||||
|
} else if ('point' in event && (event as any).point.uuid === pointUuid) {
|
||||||
|
return (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getActionByUuid: (productUuid, actionUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.actionUuid === actionUuid) {
|
||||||
|
return point.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
return point.action;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getActionByPointUuid: (productUuid, pointUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.uuid === pointUuid) {
|
||||||
|
return point.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if (point.uuid === pointUuid) {
|
||||||
|
return point.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getModelUuidByPointUuid: (productUuid, pointUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.uuid === pointUuid) {
|
||||||
|
return event.modelUuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if (point.uuid === pointUuid) {
|
||||||
|
return event.modelUuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getModelUuidByActionUuid: (productUuid, actionUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.actionUuid === actionUuid) {
|
||||||
|
return event.modelUuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
return event.modelUuid;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) return event.modelUuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPointUuidByActionUuid: (productUuid, actionUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.actionUuid === actionUuid) {
|
||||||
|
return point.uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||||
|
return point.uuid;
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||||
|
if (action) return point.uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTriggerByUuid: (productUuid, triggerUuid) => {
|
||||||
|
const product = get().products.find(p => p.productUuid === productUuid);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
for (const trigger of point.action?.triggers || []) {
|
||||||
|
if (trigger.triggerUuid === triggerUuid) {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point) {
|
||||||
|
for (const trigger of point.action?.triggers || []) {
|
||||||
|
if (trigger.triggerUuid === triggerUuid) {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
for (const trigger of action.triggers || []) {
|
||||||
|
if (trigger.triggerUuid === triggerUuid) {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getIsEventInProduct: (productUuid, modelUuid) => {
|
||||||
|
const product = get().getProductById(productUuid);
|
||||||
|
if (!product) return false;
|
||||||
|
return product.eventDatas.some(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Canvas } from "@react-three/fiber";
|
||||||
|
|
||||||
|
import BuilderDuplicate from "./duplicateBuilder/builderDuplicate";
|
||||||
|
import { Environment } from "@react-three/drei";
|
||||||
|
import Shadows from "../scene/environment/shadow";
|
||||||
|
import Sun from "../scene/environment/sky";
|
||||||
|
|
||||||
|
import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||||
|
import ControlsDuplicate from "./duplicateSetup/controlsDuplicate";
|
||||||
|
import PostProcessingDuplicate from "./duplicateSetup/postProcessingDuplicate";
|
||||||
|
import { Color } from "three";
|
||||||
|
import SimulationDuplicate from "./duplicateSimulation/duplicateSimulation";
|
||||||
|
|
||||||
|
export default function DuplicateScene() {
|
||||||
|
const projectId = "684bcd620a64bc2a815a88d6";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Canvas
|
||||||
|
id="sceneCanvas"
|
||||||
|
shadows
|
||||||
|
color="#aaaa"
|
||||||
|
eventPrefix="client"
|
||||||
|
style={{
|
||||||
|
pointerEvents: 'none'
|
||||||
|
}}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onCreated={(e) => {
|
||||||
|
e.scene.background = new Color(0x19191d);
|
||||||
|
}}
|
||||||
|
gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
|
||||||
|
>
|
||||||
|
|
||||||
|
<Sun />
|
||||||
|
|
||||||
|
<Shadows />
|
||||||
|
|
||||||
|
<ControlsDuplicate />
|
||||||
|
|
||||||
|
<PostProcessingDuplicate />
|
||||||
|
|
||||||
|
<Environment files={background} environmentIntensity={1.5} />
|
||||||
|
|
||||||
|
<BuilderDuplicate projectId={projectId} />
|
||||||
|
|
||||||
|
<SimulationDuplicate />
|
||||||
|
</Canvas>
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||||
|
|
||||||
import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store";
|
import { useSocketStore, useToggleView, useResetCamera, useDuplicatedCamData } from "../../../store/builder/store";
|
||||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||||
import updateCamPosition from "../camera/updateCameraPosition";
|
import updateCamPosition from "../camera/updateCameraPosition";
|
||||||
import CamMode from "../camera/camMode";
|
import CamMode from "../camera/camMode";
|
||||||
|
@ -22,6 +22,7 @@ export default function Controls() {
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const state = useThree();
|
const state = useThree();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
const { setDuplicatedCamData } = useDuplicatedCamData();
|
||||||
const { userId, organization, email } = getUserData();
|
const { userId, organization, email } = getUserData();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -103,10 +104,23 @@ export default function Controls() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const camUpdate = () => {
|
||||||
|
if (!controlsRef.current) return;
|
||||||
|
const position = controlsRef.current.getPosition(new THREE.Vector3(0, 0, 0));
|
||||||
|
const target = controlsRef.current.getTarget(new THREE.Vector3(0, 0, 0));
|
||||||
|
const rotation = state.camera.rotation;
|
||||||
|
setDuplicatedCamData(
|
||||||
|
[position.x, position.y, position.z],
|
||||||
|
[target.x, target.y, target.z],
|
||||||
|
[rotation.x, rotation.y, rotation.z]
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
const controls = controlsRef.current;
|
const controls = controlsRef.current;
|
||||||
if (controls) {
|
if (controls) {
|
||||||
controls.addEventListener("sleep", handleRest);
|
controls.addEventListener("sleep", handleRest);
|
||||||
controls.addEventListener("control", startInterval);
|
controls.addEventListener("control", startInterval);
|
||||||
|
controls.addEventListener("control", camUpdate);
|
||||||
controls.addEventListener("controlend", stopInterval);
|
controls.addEventListener("controlend", stopInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +128,7 @@ export default function Controls() {
|
||||||
if (controls) {
|
if (controls) {
|
||||||
controls.removeEventListener("sleep", handleRest);
|
controls.removeEventListener("sleep", handleRest);
|
||||||
controls.removeEventListener("control", startInterval);
|
controls.removeEventListener("control", startInterval);
|
||||||
|
controls.removeEventListener("control", camUpdate);
|
||||||
controls.removeEventListener("controlend", stopInterval);
|
controls.removeEventListener("controlend", stopInterval);
|
||||||
}
|
}
|
||||||
stopInterval();
|
stopInterval();
|
||||||
|
|
|
@ -29,6 +29,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
||||||
const { projectSocket } = useSocketStore();
|
const { projectSocket } = useSocketStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { loadingProgress } = useLoadingProgress();
|
const { loadingProgress } = useLoadingProgress();
|
||||||
|
|
||||||
const handleUpdatingProject = async () => {
|
const handleUpdatingProject = async () => {
|
||||||
if (!projectId && loadingProgress > 1) return;
|
if (!projectId && loadingProgress > 1) return;
|
||||||
try {
|
try {
|
||||||
|
@ -36,18 +37,37 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
||||||
let projectUuid = projects.Projects.find(
|
let projectUuid = projects.Projects.find(
|
||||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||||
);
|
);
|
||||||
const canvas =
|
|
||||||
document.getElementById("sceneCanvas")?.children[0]?.children[0];
|
if (activeModule === "builder" && loadingProgress !== 1) {
|
||||||
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
const canvas =
|
||||||
const updateProjects = {
|
document.getElementById("sceneCanvas")?.children[0]?.children[0];
|
||||||
projectId: projectUuid,
|
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
||||||
organization,
|
setTimeout(() => {
|
||||||
userId,
|
const updateProjects = {
|
||||||
projectName: projectUuid.projectName,
|
projectId: projectUuid,
|
||||||
thumbnail: screenshotDataUrl,
|
organization,
|
||||||
};
|
userId,
|
||||||
if (projectSocket) {
|
projectName: projectUuid.projectName,
|
||||||
projectSocket.emit("v1:project:update", updateProjects);
|
thumbnail: screenshotDataUrl,
|
||||||
|
};
|
||||||
|
if (projectSocket) {
|
||||||
|
projectSocket.emit("v1:project:update", updateProjects);
|
||||||
|
}
|
||||||
|
}, 8000);
|
||||||
|
} else {
|
||||||
|
const canvas =
|
||||||
|
document.getElementById("sceneCanvas")?.children[0]?.children[0];
|
||||||
|
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
||||||
|
const updateProjects = {
|
||||||
|
projectId: projectUuid,
|
||||||
|
organization,
|
||||||
|
userId,
|
||||||
|
projectName: projectUuid.projectName,
|
||||||
|
thumbnail: screenshotDataUrl,
|
||||||
|
};
|
||||||
|
if (projectSocket) {
|
||||||
|
projectSocket.emit("v1:project:update", updateProjects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface ConnectionLine {
|
||||||
trigger: TriggerSchema;
|
trigger: TriggerSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Arrows({ connections }: { readonly connections: ConnectionLine[] }) {
|
export function Arrows({ connections, color = "#42a5f5" }: { readonly connections: ConnectionLine[], color?: string }) {
|
||||||
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
|
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
|
@ -61,7 +61,7 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
|
||||||
onPointerOut={() => setHoveredLineKey(null)}
|
onPointerOut={() => setHoveredLineKey(null)}
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={toolMode === '2D-Delete' && hoveredLineKey === key ? "red" : "#42a5f5"}
|
color={toolMode === '2D-Delete' && hoveredLineKey === key ? "red" : color}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
<mesh
|
<mesh
|
||||||
|
@ -72,7 +72,7 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
|
||||||
onPointerOut={() => setHoveredLineKey(null)}
|
onPointerOut={() => setHoveredLineKey(null)}
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={toolMode === '2D-Delete' && hoveredLineKey === key ? "red" : "#42a5f5"}
|
color={toolMode === '2D-Delete' && hoveredLineKey === key ? "red" : color}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
</group>
|
||||||
|
|
|
@ -477,7 +477,7 @@ function TriggerConnector() {
|
||||||
start={startPoint.toArray()}
|
start={startPoint.toArray()}
|
||||||
end={endPoint.toArray()}
|
end={endPoint.toArray()}
|
||||||
mid={midPoint.toArray()}
|
mid={midPoint.toArray()}
|
||||||
color={toolMode === '3D-Delete' && hoveredLineKey === connection.id ? "red" : "#42a5f5"}
|
color={toolMode === '3D-Delete' && hoveredLineKey === connection.id ? "red" : "#6f42c1"}
|
||||||
lineWidth={4}
|
lineWidth={4}
|
||||||
dashed={toolMode === '3D-Delete' && hoveredLineKey === connection.id ? false : true}
|
dashed={toolMode === '3D-Delete' && hoveredLineKey === connection.id ? false : true}
|
||||||
dashSize={0.75}
|
dashSize={0.75}
|
||||||
|
@ -496,7 +496,7 @@ function TriggerConnector() {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<Arrows connections={connections} />
|
<Arrows connections={connections} color="#6f42c1"/>
|
||||||
|
|
||||||
{currentLine && (
|
{currentLine && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { setFloorItemApi } from "../services/factoryBuilder/assest/floorAsset/se
|
||||||
import { useAssetsStore } from "../store/builder/useAssetStore";
|
import { useAssetsStore } from "../store/builder/useAssetStore";
|
||||||
import ComparisonSceneProvider from "../components/layout/scenes/ComparisonSceneProvider";
|
import ComparisonSceneProvider from "../components/layout/scenes/ComparisonSceneProvider";
|
||||||
import MainSceneProvider from "../components/layout/scenes/MainSceneProvider";
|
import MainSceneProvider from "../components/layout/scenes/MainSceneProvider";
|
||||||
|
import { SceneProvider } from "../modules/scene/sceneContext";
|
||||||
import { getUserData } from "../functions/getUserData";
|
import { getUserData } from "../functions/getUserData";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
|
@ -116,7 +117,9 @@ const Project: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="project-main">
|
<div className="project-main">
|
||||||
<ComparisonSceneProvider />
|
<SceneProvider layout='Comparison Layout'>
|
||||||
|
<ComparisonSceneProvider />
|
||||||
|
</SceneProvider>
|
||||||
<MainSceneProvider />
|
<MainSceneProvider />
|
||||||
{selectedUser && <FollowPerson />}
|
{selectedUser && <FollowPerson />}
|
||||||
{isLogListVisible && (
|
{isLogListVisible && (
|
||||||
|
|
|
@ -741,4 +741,36 @@ export const useCompareProductDataStore = create<{
|
||||||
}>((set) => ({
|
}>((set) => ({
|
||||||
compareProductsData: [],
|
compareProductsData: [],
|
||||||
setCompareProductsData: (x) => set({ compareProductsData: x }),
|
setCompareProductsData: (x) => set({ compareProductsData: x }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const useDuplicatedCamData = create<{
|
||||||
|
duplicatedCamData: {
|
||||||
|
camPosition: [number, number, number];
|
||||||
|
camTarget: [number, number, number];
|
||||||
|
camRotation: [number, number, number];
|
||||||
|
};
|
||||||
|
setDuplicatedCamData: (
|
||||||
|
camPosition: [number, number, number],
|
||||||
|
camTarget: [number, number, number],
|
||||||
|
camRotation: [number, number, number],
|
||||||
|
) =>
|
||||||
|
void;
|
||||||
|
}>((set) => ({
|
||||||
|
duplicatedCamData: {
|
||||||
|
camPosition: [0, 0, 0],
|
||||||
|
camTarget: [0, 0, 0],
|
||||||
|
camRotation: [0, 0, 0],
|
||||||
|
},
|
||||||
|
setDuplicatedCamData: (
|
||||||
|
camPosition: [number, number, number],
|
||||||
|
camTarget: [number, number, number],
|
||||||
|
camRotation: [number, number, number],
|
||||||
|
) => set({
|
||||||
|
duplicatedCamData: {
|
||||||
|
camPosition: camPosition,
|
||||||
|
camTarget: camTarget,
|
||||||
|
camRotation: camRotation,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
|
@ -28,6 +28,9 @@ section,
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.scene-container.half-view{
|
||||||
|
width: 50vw !important;
|
||||||
|
}
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -242,6 +242,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
hidePlayer,
|
hidePlayer,
|
||||||
selectedFloorItem,
|
selectedFloorItem,
|
||||||
isRenameMode,
|
isRenameMode,
|
||||||
|
comparisonProduct
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Reference in New Issue