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 { 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) {

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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<Group>(null);
const outerGroup = useRef<Group>(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() {
<group
position={selectedHumanData.position}
ref={outerGroup}
rotation={[0, Math.PI, 0]}
>
<primitive
name="startMarker"

View File

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