feat: Add assembly action handling and UI components
- Implemented `useAssemblyHandler` to manage assembly actions for humans. - Enhanced `useHumanActions` to include assembly action handling. - Updated `HumanInstance` to support assembly processes and animations. - Modified `HumanUi` to allow for assembly point configuration and rotation. - Created `AssemblyAction` component for setting process time and material swap options. - Updated simulation types to include assembly action properties. - Adjusted existing action handlers to accommodate assembly actions alongside worker actions. - Refactored `MaterialAnimator` and `VehicleAnimator` to manage attachment states and visibility based on load. - Updated product store types to include human point actions.
This commit is contained in:
@@ -9,13 +9,16 @@ import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
|
||||
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
|
||||
import assembly from "../../../../../assets/gltf-glb/ui/human-ui-assembly.glb";
|
||||
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
|
||||
function HumanUi() {
|
||||
const { scene: startScene } = useGLTF(startPoint) as any;
|
||||
const { scene: endScene } = useGLTF(startEnd) as any;
|
||||
const { scene: assemblyScene } = useGLTF(assembly) as any;
|
||||
const startMarker = useRef<Group>(null);
|
||||
const endMarker = useRef<Group>(null);
|
||||
const assemblyMarker = useRef<Group>(null);
|
||||
const outerGroup = useRef<Group>(null);
|
||||
const prevMousePos = useRef({ x: 0, y: 0 });
|
||||
const { controls, raycaster, camera } = useThree();
|
||||
@@ -28,6 +31,7 @@ function HumanUi() {
|
||||
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, Math.PI, 0]);
|
||||
const [assemblyRotation, setAssemblyRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const { isDragging, setIsDragging } = useIsDragging();
|
||||
const { isRotating, setIsRotating } = useIsRotating();
|
||||
@@ -44,6 +48,10 @@ function HumanUi() {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const selectedHuman = selectedEventSphere ? getHumanById(selectedEventSphere.userData.modelUuid) : null;
|
||||
const actionType = selectedHuman?.point?.action?.actionType || null;
|
||||
const isAssembly = actionType === 'assembly';
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
@@ -99,6 +107,13 @@ function HumanUi() {
|
||||
setEndPosition([0, 1, 0]);
|
||||
setEndRotation([0, 0, 0]);
|
||||
}
|
||||
|
||||
if (action.assemblyPoint?.rotation) {
|
||||
setAssemblyRotation(action.assemblyPoint.rotation);
|
||||
} else {
|
||||
setAssemblyRotation([0, 0, 0]);
|
||||
}
|
||||
|
||||
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
|
||||
|
||||
const handlePointerDown = (
|
||||
@@ -106,6 +121,7 @@ function HumanUi() {
|
||||
state: "start" | "end",
|
||||
rotation: "start" | "end"
|
||||
) => {
|
||||
if (isAssembly) return;
|
||||
e.stopPropagation();
|
||||
const intersection = new Vector3();
|
||||
const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
|
||||
@@ -144,54 +160,68 @@ function HumanUi() {
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
|
||||
if (selectedEventSphere?.userData.modelUuid && selectedAction.actionId) {
|
||||
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
|
||||
if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return;
|
||||
|
||||
if (selectedHuman && outerGroup.current && startMarker.current && endMarker.current) {
|
||||
const worldPosStart = new Vector3(...startPosition);
|
||||
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
|
||||
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
|
||||
if (!selectedHuman || !outerGroup.current) return;
|
||||
|
||||
const worldPosEnd = new Vector3(...endPosition);
|
||||
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
|
||||
const isAssembly = selectedHuman.point?.action?.actionType === 'assembly';
|
||||
|
||||
const updatedAction = {
|
||||
...selectedHuman.point.action,
|
||||
pickUpPoint: {
|
||||
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
|
||||
rotation: startRotation
|
||||
},
|
||||
dropPoint: {
|
||||
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
|
||||
rotation: endRotation
|
||||
}
|
||||
};
|
||||
let updatedAction;
|
||||
|
||||
const event = updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
selectedEventSphere.userData.modelUuid,
|
||||
{
|
||||
...selectedHuman,
|
||||
point: {
|
||||
...selectedHuman.point,
|
||||
action: updatedAction
|
||||
}
|
||||
}
|
||||
);
|
||||
if (isAssembly) {
|
||||
updatedAction = {
|
||||
...selectedHuman.point.action,
|
||||
assemblyPoint: {
|
||||
rotation: assemblyRotation
|
||||
},
|
||||
};
|
||||
} else {
|
||||
if (!startMarker.current || !endMarker.current) return;
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
const worldPosStart = new Vector3(...startPosition);
|
||||
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
|
||||
|
||||
const worldPosEnd = new Vector3(...endPosition);
|
||||
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
|
||||
|
||||
updatedAction = {
|
||||
...selectedHuman.point.action,
|
||||
pickUpPoint: {
|
||||
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
|
||||
rotation: startRotation,
|
||||
},
|
||||
dropPoint: {
|
||||
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
|
||||
rotation: endRotation,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const event = updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
selectedEventSphere.userData.modelUuid,
|
||||
{
|
||||
...selectedHuman,
|
||||
point: {
|
||||
...selectedHuman.point,
|
||||
action: updatedAction,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
|
||||
if (isAssembly || !isDragging || !plane.current || !raycaster || !outerGroup.current) return;
|
||||
const intersectPoint = new Vector3();
|
||||
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
|
||||
if (!intersects) return;
|
||||
@@ -210,11 +240,17 @@ 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" ? isAssembly ? assemblyMarker.current : startMarker.current : isAssembly ? assemblyMarker.current : endMarker.current;
|
||||
if (marker) {
|
||||
const rotationSpeed = 10;
|
||||
marker.rotation.y += deltaX * rotationSpeed;
|
||||
if (isRotating === "start") {
|
||||
if (isAssembly && isRotating === "start") {
|
||||
setAssemblyRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
marker.rotation.z,
|
||||
]);
|
||||
} else if (isRotating === "start") {
|
||||
setStartRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
@@ -245,7 +281,7 @@ function HumanUi() {
|
||||
return () => {
|
||||
window.removeEventListener("pointerup", handleGlobalPointerUp);
|
||||
};
|
||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation]);
|
||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyRotation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -255,43 +291,69 @@ function HumanUi() {
|
||||
ref={outerGroup}
|
||||
rotation={[0, Math.PI, 0]}
|
||||
>
|
||||
<primitive
|
||||
name="startMarker"
|
||||
object={startScene}
|
||||
ref={startMarker}
|
||||
position={startPosition}
|
||||
rotation={startRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "start", "start");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
(controls as any).enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
{isAssembly ? (
|
||||
<primitive
|
||||
ref={assemblyMarker}
|
||||
object={assemblyScene}
|
||||
position={[0, 1, 0]}
|
||||
rotation={assemblyRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
if (e.object.parent.name === "handle") {
|
||||
e.stopPropagation();
|
||||
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
prevMousePos.current.x = normalizedX;
|
||||
setIsRotating("start");
|
||||
setIsDragging(null);
|
||||
if (controls) (controls as any).enabled = false;
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
if (controls) (controls as any).enabled = true;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<primitive
|
||||
name="startMarker"
|
||||
object={startScene}
|
||||
ref={startMarker}
|
||||
position={startPosition}
|
||||
rotation={startRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "start", "start");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
if (controls) (controls as any).enabled = true;
|
||||
}}
|
||||
/>
|
||||
|
||||
<primitive
|
||||
name="endMarker"
|
||||
object={endScene}
|
||||
ref={endMarker}
|
||||
position={endPosition}
|
||||
rotation={endRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "end", "end");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
(controls as any).enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
<primitive
|
||||
name="endMarker"
|
||||
object={endScene}
|
||||
ref={endMarker}
|
||||
position={endPosition}
|
||||
rotation={endRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "end", "end");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
if (controls) (controls as any).enabled = true;
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default HumanUi
|
||||
Reference in New Issue
Block a user