From 36421fa70a6aa1aa608e742478eb9c1595721d37 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Mon, 7 Jul 2025 10:11:37 +0530 Subject: [PATCH] added new human ui --- .../assets/gltf-glb/ui/human-ui-assembly.glb | Bin 0 -> 18908 bytes app/src/assets/gltf-glb/ui/human-ui-green.glb | Bin 0 -> 18904 bytes .../assets/gltf-glb/ui/human-ui-orange.glb | Bin 0 -> 18904 bytes .../builder/asset/models/model/model.tsx | 27 +++++- .../selectionControls/copyPasteControls.tsx | 6 +- .../selectionControls/duplicationControls.tsx | 6 +- .../selectionControls/selectionControls.tsx | 30 +++++- .../instances/instance/humanInstance.tsx | 3 + .../human/instances/instance/humanUi.tsx | 58 ++++++++---- app/src/store/simulation/useProductStore.ts | 86 +++++++++++++++++- 10 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 app/src/assets/gltf-glb/ui/human-ui-assembly.glb create mode 100644 app/src/assets/gltf-glb/ui/human-ui-green.glb create mode 100644 app/src/assets/gltf-glb/ui/human-ui-orange.glb diff --git a/app/src/assets/gltf-glb/ui/human-ui-assembly.glb b/app/src/assets/gltf-glb/ui/human-ui-assembly.glb new file mode 100644 index 0000000000000000000000000000000000000000..844fc9bc4aa059bd25fa3964aeddcf5171286985 GIT binary patch literal 18908 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 literal 0 HcmV?d00001 diff --git a/app/src/assets/gltf-glb/ui/human-ui-orange.glb b/app/src/assets/gltf-glb/ui/human-ui-orange.glb new file mode 100644 index 0000000000000000000000000000000000000000..9624cb2ba8d4812450415346238c5bc41f89cba3 GIT binary patch literal 18904 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 af*^realkRXiiyxykiI^dJ0RIQ4#uf_z literal 0 HcmV?d00001 diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index c7bca83..75ad55b 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -17,6 +17,7 @@ import { useSceneContext } from '../../../../scene/sceneContext'; import { useVersionContext } from '../../../version/versionContext'; import { SkeletonUtils } from 'three-stdlib'; import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore'; +import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; function Model({ asset }: { readonly asset: Asset }) { const { camera, controls, gl } = useThree(); @@ -55,6 +56,21 @@ function Model({ asset }: { readonly asset: Asset }) { const blendFactor = useRef(0); const blendDuration = 0.5; + const updateBackend = ( + productName: string, + productUuid: string, + projectId: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }); + }; + useEffect(() => { setDeletableFloorItem(null); if (selectedFloorItem === null || selectedFloorItem.modelUuid !== asset.modelUuid) { @@ -217,7 +233,16 @@ function Model({ asset }: { readonly asset: Asset }) { const response = socket.emit('v1:model-asset:delete', data) eventStore.getState().removeEvent(asset.modelUuid); - productStore.getState().deleteEvent(asset.modelUuid); + const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) if (response) { diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index 29a3007..e8c8886 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -1,8 +1,8 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; +import { SkeletonUtils } from "three-stdlib"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; -// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useParams } from "react-router-dom"; @@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData"; import { useSceneContext } from "../../sceneContext"; import { useVersionContext } from "../../../builder/version/versionContext"; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; + const CopyPasteControls = ({ copiedObjects, setCopiedObjects, @@ -109,7 +111,7 @@ const CopyPasteControls = ({ const copySelection = () => { if (selectedAssets.length > 0) { const newClones = selectedAssets.map((asset: any) => { - const clone = asset.clone(); + const clone = SkeletonUtils.clone(asset); clone.position.copy(asset.position); return clone; }); diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index b4a1916..3b52cdf 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -1,8 +1,8 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; +import { SkeletonUtils } from "three-stdlib"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; -// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useParams } from "react-router-dom"; @@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData"; import { useSceneContext } from "../../sceneContext"; import { useVersionContext } from "../../../builder/version/versionContext"; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; + const DuplicationControls = ({ duplicatedObjects, setDuplicatedObjects, @@ -104,7 +106,7 @@ const DuplicationControls = ({ const duplicateSelection = () => { if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { const newClones = selectedAssets.map((asset: any) => { - const clone = asset.clone(); + const clone = SkeletonUtils.clone(asset); clone.position.copy(asset.position); return clone; }); diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 645ed1c..49c94dd 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -17,6 +17,8 @@ import { useParams } from "react-router-dom"; import { getUserData } from "../../../../functions/getUserData"; import { useSceneContext } from "../../sceneContext"; import { useVersionContext } from "../../../builder/version/versionContext"; +import { useProductContext } from "../../../simulation/products/productContext"; +import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, raycaster, pointer } = useThree(); @@ -37,6 +39,8 @@ const SelectionControls: React.FC = () => { const { toolMode } = useToolMode(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { projectId } = useParams(); const isDragging = useRef(false); @@ -48,6 +52,21 @@ const SelectionControls: React.FC = () => { const isShiftSelecting = useRef(false); const { userId, organization } = getUserData(); + const updateBackend = ( + productName: string, + productUuid: string, + projectId: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }); + }; + useEffect(() => { if (!camera || !scene || toggleView) return; @@ -284,7 +303,16 @@ const SelectionControls: React.FC = () => { const response = socket.emit("v1:model-asset:delete", data); eventStore.getState().removeEvent(selectedMesh.uuid); - productStore.getState().deleteEvent(selectedMesh.uuid); + const updatedEvents = productStore.getState().deleteEvent(selectedMesh.uuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) if (response) { diff --git a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx index 12d2d61..5ba757d 100644 --- a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx @@ -253,6 +253,9 @@ function HumanInstance({ human }: { human: HumanStatus }) { function startUnloadingProcess() { const humanAsset = getAssetById(human.modelUuid); + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { if (human.point.action.triggers.length > 0) { const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid); diff --git a/app/src/modules/simulation/human/instances/instance/humanUi.tsx b/app/src/modules/simulation/human/instances/instance/humanUi.tsx index 64b1f84..68d7e5a 100644 --- a/app/src/modules/simulation/human/instances/instance/humanUi.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanUi.tsx @@ -4,11 +4,11 @@ import { useFrame, useThree } from '@react-three/fiber'; import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore'; import { useProductContext } from '../../../products/productContext'; import { useSceneContext } from '../../../../scene/sceneContext'; -import { Group, Plane, Vector3 } from 'three'; +import { Group, Plane, Vector2, Vector3 } from 'three'; import { useVersionContext } from '../../../../builder/version/versionContext'; import { useParams } from 'react-router-dom'; -import startPoint from "../../../../../assets/gltf-glb/ui/arrow_green.glb"; -import startEnd from "../../../../../assets/gltf-glb/ui/arrow_red.glb"; +import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb"; +import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb"; import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; function HumanUi() { @@ -18,7 +18,7 @@ function HumanUi() { const endMarker = useRef(null); const outerGroup = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); - const { controls, raycaster } = useThree(); + const { controls, raycaster, camera } = useThree(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedProductStore } = useProductContext(); const { humanStore, productStore } = useSceneContext(); @@ -27,11 +27,12 @@ function HumanUi() { const { updateEvent } = productStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]); - const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]); + const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]); const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]); const { isDragging, setIsDragging } = useIsDragging(); const { isRotating, setIsRotating } = useIsRotating(); const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); + const dragOffset = useRef(new Vector3()); const [selectedHumanData, setSelectedHumanData] = useState<{ position: [number, number, number]; @@ -74,15 +75,15 @@ function HumanUi() { if (action.pickUpPoint?.position && outerGroup.current) { const worldPos = new Vector3(...action.pickUpPoint.position); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); - setStartPosition([localPosition.x, 0.5, localPosition.z]); + setStartPosition([localPosition.x, 1, localPosition.z]); setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]); } if (action.dropPoint?.position && outerGroup.current) { const worldPos = new Vector3(...action.dropPoint.position); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); - setEndPosition([localPosition.x, 0.5, localPosition.z]); - setEndRotation(action.dropPoint.rotation || [0, 0, 0]); + setEndPosition([localPosition.x, 1, localPosition.z]); + setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]); } }, [selectedEventSphere, outerGroup.current, selectedAction, humans]); @@ -91,20 +92,39 @@ function HumanUi() { state: "start" | "end", rotation: "start" | "end" ) => { - if (e.object.name === "handle") { + e.stopPropagation(); + const intersection = new Vector3(); + const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.ray.intersectPlane(plane.current, intersection); + + if (e.object.parent.name === "handle") { const normalizedX = (e.clientX / window.innerWidth) * 2 - 1; const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1; prevMousePos.current = { x: normalizedX, y: normalizedY }; setIsRotating(rotation); - if (controls) (controls as any).enabled = false; setIsDragging(null); } else { setIsDragging(state); setIsRotating(null); - if (controls) (controls as any).enabled = false; } + + if (intersects) { + let localPoint: Vector3 | null = null; + if (outerGroup.current) { + localPoint = outerGroup.current.worldToLocal(intersection.clone()); + } + const marker = state === "start" ? startMarker.current : endMarker.current; + if (marker && localPoint) { + const markerPos = new Vector3().copy(marker.position); + dragOffset.current.copy(markerPos.sub(localPoint)); + } + } + + if (controls) (controls as any).enabled = false; }; + const handlePointerUp = () => { (controls as any).enabled = true; setIsDragging(null); @@ -159,16 +179,15 @@ function HumanUi() { useFrame(() => { if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return; const intersectPoint = new Vector3(); - const intersects = raycaster.ray.intersectPlane( - plane.current, - intersectPoint - ); + const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint); if (!intersects) return; - const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone()); + + const localPoint = outerGroup.current.worldToLocal(intersectPoint.clone()).add(dragOffset.current); + if (isDragging === "start") { - setStartPosition([localPoint.x, 0.5, localPoint.z]); + setStartPosition([localPoint.x, 1, localPoint.z]); } else if (isDragging === "end") { - setEndPosition([localPoint.x, 0.5, localPoint.z]); + setEndPosition([localPoint.x, 1, localPoint.z]); } }); @@ -177,7 +196,7 @@ function HumanUi() { const currentPointerX = state.pointer.x; const deltaX = currentPointerX - prevMousePos.current.x; prevMousePos.current.x = currentPointerX; - const marker =isRotating === "start" ? startMarker.current : endMarker.current; + const marker = isRotating === "start" ? startMarker.current : endMarker.current; if (marker) { const rotationSpeed = 10; marker.rotation.y += deltaX * rotationSpeed; @@ -220,6 +239,7 @@ function HumanUi() { void; removeEvent: (productUuid: string, modelUuid: string) => void; - deleteEvent: (modelUuid: string) => void; + deleteEvent: (modelUuid: string) => EventsSchema[]; updateEvent: (productUuid: string, modelUuid: string, updates: Partial) => EventsSchema | undefined; // Point-level actions @@ -145,11 +145,93 @@ export const createProductStore = () => { }, deleteEvent: (modelUuid) => { + let updatedEvents: EventsSchema[] = []; set((state) => { + const actionsToDelete = new Set(); + for (const product of state.products) { - product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + const eventIndex = product.eventDatas.findIndex(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (eventIndex !== -1) { + const event = product.eventDatas[eventIndex]; + + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action) { + actionsToDelete.add(point.action.actionUuid); + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action) { + actionsToDelete.add(point.action.actionUuid); + } else if ('actions' in point) { + for (const action of point.actions) { + actionsToDelete.add(action.actionUuid); + } + } + } + + product.eventDatas.splice(eventIndex, 1); + } + } + + for (const product of state.products) { + for (const event of product.eventDatas) { + let eventModified = false; + + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.triggers) { + const originalLength = point.action.triggers.length; + point.action.triggers = point.action.triggers.filter(trigger => { + return !( + (trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) || + (actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || '')) + ); + }); + if (point.action.triggers.length !== originalLength) { + eventModified = true; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.triggers) { + const originalLength = point.action.triggers.length; + point.action.triggers = point.action.triggers.filter((trigger: TriggerSchema) => { + return !( + (trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) || + (actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || '')) + ); + }); + if (point.action.triggers.length !== originalLength) { + eventModified = true; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if (action.triggers) { + const originalLength = action.triggers.length; + action.triggers = action.triggers.filter((trigger: TriggerSchema) => { + return !( + (trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) || + (actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || '')) + ); + }); + if (action.triggers.length !== originalLength) { + eventModified = true; + } + } + } + } + } + + if (eventModified) { + updatedEvents.push(JSON.parse(JSON.stringify(event))); + } + } } }); + return updatedEvents; }, updateEvent: (productUuid, modelUuid, updates) => {