diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 6c2f8d6..7c6c719 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -6,7 +6,7 @@ import { useSelectedFloorItem, } from "../../../store/builder/store"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useDeletableEventSphere, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { useEffect } from "react"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; @@ -15,6 +15,7 @@ export default function PostProcessing() { const { selectedWallItem } = useSelectedWallItem(); const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventSphere } = useSelectedEventSphere(); + const { deletableEventSphere } = useDeletableEventSphere(); const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore(); function flattenChildren(children: any[]) { @@ -56,6 +57,10 @@ export default function PostProcessing() { // console.log('deletableWallAsset: ', deletableWallAsset); }, [deletableWallAsset]) + useEffect(() => { + // console.log('deletableEventSphere: ', deletableEventSphere); + }, [deletableEventSphere]) + return ( )} + {deletableEventSphere && ( + + )} ); } diff --git a/app/src/modules/simulation/events/arrows/arrows.tsx b/app/src/modules/simulation/events/arrows/arrows.tsx index 5b9d6fd..2053fe5 100644 --- a/app/src/modules/simulation/events/arrows/arrows.tsx +++ b/app/src/modules/simulation/events/arrows/arrows.tsx @@ -16,11 +16,11 @@ interface ConnectionLine { } export function Arrows({ connections }: { readonly connections: ConnectionLine[] }) { - const [hoveredLineKey, setHoveredLineKey] = useState(null); + const [hoveredArrowTrigger, setHoveredArrowTrigger] = useState(null); const groupRef = useRef(null); const { scene } = useThree(); const { toolMode } = useToolMode(); - const { eventStore, productStore } = useSceneContext(); + const { productStore } = useSceneContext(); const { removeTrigger } = productStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -103,15 +103,15 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[] return ( setHoveredLineKey(trigger.triggerUuid)} - onPointerOut={() => setHoveredLineKey(null)} + onPointerOver={() => setHoveredArrowTrigger(trigger.triggerUuid)} + onPointerOut={() => setHoveredArrowTrigger(null)} onClick={() => { removeConnection(trigger) }} > diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index bfdf49d..9fb27a9 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,38 +1,46 @@ import { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; +import { useToolMode } from "../../../../../store/builder/store"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; -import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; +import { useSelectedEventSphere, useSelectedEventData, useDeletableEventSphere } from "../../../../../store/simulation/useSimulationStore"; import { useThree } from "@react-three/fiber"; import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; import { useProductContext } from "../../../products/productContext"; import { useParams } from "react-router-dom"; -import { useToolMode } from "../../../../../store/builder/store"; import { useVersionContext } from "../../../../builder/version/versionContext"; import { useSceneContext } from "../../../../scene/sceneContext"; +import PointInstances from "../instances/pointInstances"; + function PointsCreator() { const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); + const { toolMode } = useToolMode(); + const { activeModule } = useModuleStore(); const { selectedProductStore } = useProductContext(); const { eventStore, productStore } = useSceneContext(); - const { events, getEventByModelUuid } = eventStore(); - const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getEventByModelUuid: getEventByModelUuidFromProduct2, getPointByUuid: getPointByUuidFromProduct, getTriggersByTriggeredPointUuid, removeTrigger, removePoint } = productStore(); + const { getEventByModelUuid } = eventStore(); + const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getPointByUuid: getPointByUuidFromProduct, getTriggersByTriggeredPointUuid, removeTrigger, removePoint } = productStore(); const { selectedProduct } = selectedProductStore(); - const { activeModule } = useModuleStore(); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); - const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); - const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); + const { selectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); + const { clearDeletableEventSphere } = useDeletableEventSphere(); const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); - const { toolMode } = useToolMode(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); + useEffect(() => { + clearSelectedEventSphere(); + clearSelectedEventData(); + clearDeletableEventSphere(); + }, [toolMode, activeModule]) + const updateBackend = ( productName: string, productUuid: string, @@ -208,205 +216,7 @@ function PointsCreator() { {activeModule === "simulation" && ( <> - {events.map((event, index) => { - const updatedEvent = selectedProduct.productUuid !== '' - ? getEventByModelUuidFromProduct2(selectedProduct.productUuid, event.modelUuid) - : null; - - const usedEvent = updatedEvent || event; - - if (usedEvent.type === "transfer") { - return ( - - {usedEvent.points.map((point, j) => ( - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - key={`${index}-${point.uuid}`} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - ))} - - ); - } else if (usedEvent.type === "vehicle") { - const point = usedEvent.point; - return ( - - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - - ); - } else if (usedEvent.type === "roboticArm") { - const point = usedEvent.point; - return ( - - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - - ); - } else if (usedEvent.type === "machine") { - const point = usedEvent.point; - return ( - - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - - ); - } else if (usedEvent.type === "storageUnit") { - const point = usedEvent.point; - return ( - - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - - ); - } else if (usedEvent.type === "human") { - const point = usedEvent.point; - return ( - - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - if (toolMode === 'cursor') { - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - } - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: usedEvent.modelUuid, - pointUuid: point.uuid, - }} - > - - - - - ); - } else { - return null; - } - })} + {selectedEventSphere && transformMode && ( diff --git a/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx b/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx new file mode 100644 index 0000000..cfac4bb --- /dev/null +++ b/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx @@ -0,0 +1,121 @@ +import * as THREE from "three"; +import { MeshProps } from "@react-three/fiber"; +import { useRef } from "react"; +import { useToolMode } from "../../../../../../store/builder/store"; +import { useDeletableEventSphere, useSelectedEventSphere } from "../../../../../../store/simulation/useSimulationStore"; +import { useSceneContext } from "../../../../../scene/sceneContext"; +import { useProductContext } from "../../../../products/productContext"; +import { useVersionContext } from "../../../../../builder/version/versionContext"; +import { useParams } from "react-router-dom"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; + +interface PointInstanceProps extends Omit { + point: { + uuid: string; + position: [number, number, number]; + }; + modelUuid: string; + color: string; +} + +export default function PointInstance({ + point, + modelUuid, + color, + ...meshProps +}: PointInstanceProps) { + const ref = useRef(null); + const { setSelectedEventSphere } = useSelectedEventSphere(); + const { deletableEventSphere, setDeletableEventSphere, clearDeletableEventSphere } = useDeletableEventSphere(); + const { toolMode } = useToolMode(); + const { productStore } = useSceneContext(); + const { getEventByModelUuid, getTriggersByTriggeredPointUuid, removeTrigger, removePoint } = productStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + + const updateBackend = ( + productName: string, + productUuid: string, + projectId: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }) + } + + const handleEventPointDelete = () => { + const eventModel = getEventByModelUuid(selectedProduct.productUuid, modelUuid); + if (!eventModel || eventModel.type !== 'transfer' || eventModel.points.length < 2) return; + + const triggers = getTriggersByTriggeredPointUuid(selectedProduct.productUuid, point.uuid); + + const updatedEvents: EventsSchema[] = []; + if (triggers.length > 0) { + triggers.map((trigger) => { + const event = removeTrigger(selectedProduct.productUuid, trigger.triggerUuid); + if (event) { + updatedEvents.push(event); + } + }) + } + + const event = removePoint(selectedProduct.productUuid, modelUuid, point.uuid) + + if (event) { + updatedEvents.push(event); + } + + if (updatedEvents.length > 0) { + updatedEvents.map((updatedEvent) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + updatedEvent + ); + }) + } + } + + return ( + { + e.stopPropagation(); + if (toolMode === 'cursor') { + setSelectedEventSphere(ref.current); + } else if (toolMode === '3D-Delete') { + handleEventPointDelete() + } + }} + onPointerOver={(e) => { + e.stopPropagation(); + if (toolMode === '3D-Delete' && (!deletableEventSphere || deletableEventSphere.uuid !== point.uuid)) { + setDeletableEventSphere(ref.current); + } + }} + onPointerOut={(e) => { + e.stopPropagation(); + if (toolMode === '3D-Delete' && deletableEventSphere && deletableEventSphere.uuid === point.uuid) { + clearDeletableEventSphere(); + } + }} + userData={{ modelUuid, pointUuid: point.uuid }} + > + + + + ); +} diff --git a/app/src/modules/simulation/events/points/instances/pointInstances.tsx b/app/src/modules/simulation/events/points/instances/pointInstances.tsx new file mode 100644 index 0000000..0b7ca60 --- /dev/null +++ b/app/src/modules/simulation/events/points/instances/pointInstances.tsx @@ -0,0 +1,56 @@ +import { useProductContext } from "../../../products/productContext"; +import { useSceneContext } from "../../../../scene/sceneContext"; + +import PointInstance from "./instance/pointInstance"; + +function PointInstances() { + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + + const { eventStore, productStore } = useSceneContext(); + const { events } = eventStore(); + const { getEventByModelUuid } = productStore(); + + const colorByType: Record = { + transfer: "orange", + vehicle: "blue", + roboticArm: "green", + machine: "purple", + storageUnit: "red", + human: "white", + }; + + return ( + <> + {events.map((event, index) => { + const updatedEvent = selectedProduct.productUuid !== '' ? getEventByModelUuid(selectedProduct.productUuid, event.modelUuid) : null; + + const usedEvent = updatedEvent || event; + const color = colorByType[usedEvent.type]; + + if (!color) return null; + + const points = usedEvent.type === "transfer" ? usedEvent.points : [usedEvent.point]; + + return ( + + {points.map((point) => ( + + ))} + + ); + })} + + ); +} + +export default PointInstances; diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts index b6cd076..9742795 100644 --- a/app/src/store/simulation/useSimulationStore.ts +++ b/app/src/store/simulation/useSimulationStore.ts @@ -24,6 +24,28 @@ export const useSelectedEventSphere = create()( })) ); +interface DeletableEventSphereState { + deletableEventSphere: THREE.Mesh | null; + setDeletableEventSphere: (mesh: THREE.Mesh | null) => void; + clearDeletableEventSphere: () => void; +} + +export const useDeletableEventSphere = create()( + immer((set) => ({ + deletableEventSphere: null, + setDeletableEventSphere: (mesh) => { + set((state) => { + state.deletableEventSphere = mesh; + }); + }, + clearDeletableEventSphere: () => { + set((state) => { + state.deletableEventSphere = null; + }); + }, + })) +); + interface SelectedEventDataState { selectedEventData: { data: EventsSchema; selectedPoint: string } | undefined; setSelectedEventData: (data: EventsSchema, selectedPoint: string) => void;