added new human ui

This commit is contained in:
2025-07-07 10:11:37 +05:30
parent 2feedc7dcb
commit 36421fa70a
10 changed files with 189 additions and 27 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -17,6 +17,7 @@ import { useSceneContext } from '../../../../scene/sceneContext';
import { useVersionContext } from '../../../version/versionContext'; import { useVersionContext } from '../../../version/versionContext';
import { SkeletonUtils } from 'three-stdlib'; import { SkeletonUtils } from 'three-stdlib';
import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore'; import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore';
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
function Model({ asset }: { readonly asset: Asset }) { function Model({ asset }: { readonly asset: Asset }) {
const { camera, controls, gl } = useThree(); const { camera, controls, gl } = useThree();
@@ -55,6 +56,21 @@ function Model({ asset }: { readonly asset: Asset }) {
const blendFactor = useRef(0); const blendFactor = useRef(0);
const blendDuration = 0.5; 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(() => { useEffect(() => {
setDeletableFloorItem(null); setDeletableFloorItem(null);
if (selectedFloorItem === null || selectedFloorItem.modelUuid !== asset.modelUuid) { 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) const response = socket.emit('v1:model-asset:delete', data)
eventStore.getState().removeEvent(asset.modelUuid); 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) { if (response) {

View File

@@ -1,8 +1,8 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { SkeletonUtils } from "three-stdlib";
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData";
import { useSceneContext } from "../../sceneContext"; import { useSceneContext } from "../../sceneContext";
import { useVersionContext } from "../../../builder/version/versionContext"; import { useVersionContext } from "../../../builder/version/versionContext";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
const CopyPasteControls = ({ const CopyPasteControls = ({
copiedObjects, copiedObjects,
setCopiedObjects, setCopiedObjects,
@@ -109,7 +111,7 @@ const CopyPasteControls = ({
const copySelection = () => { const copySelection = () => {
if (selectedAssets.length > 0) { if (selectedAssets.length > 0) {
const newClones = selectedAssets.map((asset: any) => { const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone(); const clone = SkeletonUtils.clone(asset);
clone.position.copy(asset.position); clone.position.copy(asset.position);
return clone; return clone;
}); });

View File

@@ -1,8 +1,8 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { SkeletonUtils } from "three-stdlib";
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData";
import { useSceneContext } from "../../sceneContext"; import { useSceneContext } from "../../sceneContext";
import { useVersionContext } from "../../../builder/version/versionContext"; import { useVersionContext } from "../../../builder/version/versionContext";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
const DuplicationControls = ({ const DuplicationControls = ({
duplicatedObjects, duplicatedObjects,
setDuplicatedObjects, setDuplicatedObjects,
@@ -104,7 +106,7 @@ const DuplicationControls = ({
const duplicateSelection = () => { const duplicateSelection = () => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const newClones = selectedAssets.map((asset: any) => { const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone(); const clone = SkeletonUtils.clone(asset);
clone.position.copy(asset.position); clone.position.copy(asset.position);
return clone; return clone;
}); });

View File

@@ -17,6 +17,8 @@ import { useParams } from "react-router-dom";
import { getUserData } from "../../../../functions/getUserData"; import { getUserData } from "../../../../functions/getUserData";
import { useSceneContext } from "../../sceneContext"; import { useSceneContext } from "../../sceneContext";
import { useVersionContext } from "../../../builder/version/versionContext"; import { useVersionContext } from "../../../builder/version/versionContext";
import { useProductContext } from "../../../simulation/products/productContext";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
const SelectionControls: React.FC = () => { const SelectionControls: React.FC = () => {
const { camera, controls, gl, scene, raycaster, pointer } = useThree(); const { camera, controls, gl, scene, raycaster, pointer } = useThree();
@@ -37,6 +39,8 @@ const SelectionControls: React.FC = () => {
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { projectId } = useParams(); const { projectId } = useParams();
const isDragging = useRef(false); const isDragging = useRef(false);
@@ -48,6 +52,21 @@ const SelectionControls: React.FC = () => {
const isShiftSelecting = useRef(false); const isShiftSelecting = useRef(false);
const { userId, organization } = getUserData(); 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(() => { useEffect(() => {
if (!camera || !scene || toggleView) return; if (!camera || !scene || toggleView) return;
@@ -284,7 +303,16 @@ const SelectionControls: React.FC = () => {
const response = socket.emit("v1:model-asset:delete", data); const response = socket.emit("v1:model-asset:delete", data);
eventStore.getState().removeEvent(selectedMesh.uuid); 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) { if (response) {

View File

@@ -253,6 +253,9 @@ function HumanInstance({ human }: { human: HumanStatus }) {
function startUnloadingProcess() { function startUnloadingProcess() {
const humanAsset = getAssetById(human.modelUuid); 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 (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
if (human.point.action.triggers.length > 0) { if (human.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid); const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);

View File

@@ -4,11 +4,11 @@ import { useFrame, useThree } from '@react-three/fiber';
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore'; import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../products/productContext'; import { useProductContext } from '../../../products/productContext';
import { useSceneContext } from '../../../../scene/sceneContext'; 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 { useVersionContext } from '../../../../builder/version/versionContext';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import startPoint from "../../../../../assets/gltf-glb/ui/arrow_green.glb"; import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
import startEnd from "../../../../../assets/gltf-glb/ui/arrow_red.glb"; import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
function HumanUi() { function HumanUi() {
@@ -18,7 +18,7 @@ function HumanUi() {
const endMarker = useRef<Group>(null); const endMarker = useRef<Group>(null);
const outerGroup = useRef<Group>(null); const outerGroup = useRef<Group>(null);
const prevMousePos = useRef({ x: 0, y: 0 }); const prevMousePos = useRef({ x: 0, y: 0 });
const { controls, raycaster } = useThree(); const { controls, raycaster, camera } = useThree();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { humanStore, productStore } = useSceneContext(); const { humanStore, productStore } = useSceneContext();
@@ -27,11 +27,12 @@ function HumanUi() {
const { updateEvent } = productStore(); const { updateEvent } = productStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
const [endPosition, setEndPosition] = 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 [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
const { isDragging, setIsDragging } = useIsDragging(); const { isDragging, setIsDragging } = useIsDragging();
const { isRotating, setIsRotating } = useIsRotating(); const { isRotating, setIsRotating } = useIsRotating();
const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
const dragOffset = useRef(new Vector3());
const [selectedHumanData, setSelectedHumanData] = useState<{ const [selectedHumanData, setSelectedHumanData] = useState<{
position: [number, number, number]; position: [number, number, number];
@@ -74,15 +75,15 @@ function HumanUi() {
if (action.pickUpPoint?.position && outerGroup.current) { if (action.pickUpPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.pickUpPoint.position); const worldPos = new Vector3(...action.pickUpPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); 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]); setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
} }
if (action.dropPoint?.position && outerGroup.current) { if (action.dropPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.dropPoint.position); const worldPos = new Vector3(...action.dropPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setEndPosition([localPosition.x, 0.5, localPosition.z]); setEndPosition([localPosition.x, 1, localPosition.z]);
setEndRotation(action.dropPoint.rotation || [0, 0, 0]); setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]);
} }
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]); }, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
@@ -91,20 +92,39 @@ function HumanUi() {
state: "start" | "end", state: "start" | "end",
rotation: "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 normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1; const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
prevMousePos.current = { x: normalizedX, y: normalizedY }; prevMousePos.current = { x: normalizedX, y: normalizedY };
setIsRotating(rotation); setIsRotating(rotation);
if (controls) (controls as any).enabled = false;
setIsDragging(null); setIsDragging(null);
} else { } else {
setIsDragging(state); setIsDragging(state);
setIsRotating(null); 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 = () => { const handlePointerUp = () => {
(controls as any).enabled = true; (controls as any).enabled = true;
setIsDragging(null); setIsDragging(null);
@@ -159,16 +179,15 @@ function HumanUi() {
useFrame(() => { useFrame(() => {
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return; if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
const intersectPoint = new Vector3(); const intersectPoint = new Vector3();
const intersects = raycaster.ray.intersectPlane( const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
plane.current,
intersectPoint
);
if (!intersects) return; if (!intersects) return;
const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone());
const localPoint = outerGroup.current.worldToLocal(intersectPoint.clone()).add(dragOffset.current);
if (isDragging === "start") { if (isDragging === "start") {
setStartPosition([localPoint.x, 0.5, localPoint.z]); setStartPosition([localPoint.x, 1, localPoint.z]);
} else if (isDragging === "end") { } else if (isDragging === "end") {
setEndPosition([localPoint.x, 0.5, localPoint.z]); setEndPosition([localPoint.x, 1, localPoint.z]);
} }
}); });
@@ -220,6 +239,7 @@ function HumanUi() {
<group <group
position={selectedHumanData.position} position={selectedHumanData.position}
ref={outerGroup} ref={outerGroup}
rotation={[0, Math.PI, 0]}
> >
<primitive <primitive
name="startMarker" name="startMarker"

View File

@@ -14,7 +14,7 @@ type ProductsStore = {
// Event-level actions // Event-level actions
addEvent: (productUuid: string, event: EventsSchema) => void; addEvent: (productUuid: string, event: EventsSchema) => void;
removeEvent: (productUuid: string, modelUuid: string) => void; removeEvent: (productUuid: string, modelUuid: string) => void;
deleteEvent: (modelUuid: string) => void; deleteEvent: (modelUuid: string) => EventsSchema[];
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined; updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
// Point-level actions // Point-level actions
@@ -145,11 +145,93 @@ export const createProductStore = () => {
}, },
deleteEvent: (modelUuid) => { deleteEvent: (modelUuid) => {
let updatedEvents: EventsSchema[] = [];
set((state) => { set((state) => {
const actionsToDelete = new Set<string>();
for (const product of state.products) { 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) => { updateEvent: (productUuid, modelUuid, updates) => {