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:
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import InputRange from "../../../../../ui/inputs/InputRange";
|
||||
import SwapAction from "./SwapAction";
|
||||
|
||||
interface AssemblyActionProps {
|
||||
processTime: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
disabled?: boolean,
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
swapOptions: string[];
|
||||
swapDefaultOption: string;
|
||||
onSwapSelect: (value: string) => void;
|
||||
}
|
||||
|
||||
const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
processTime,
|
||||
swapOptions,
|
||||
swapDefaultOption,
|
||||
onSwapSelect,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<InputRange
|
||||
label="Process Time"
|
||||
value={processTime.value}
|
||||
min={processTime.min}
|
||||
max={processTime.max}
|
||||
disabled={processTime.disabled}
|
||||
onClick={() => { }}
|
||||
onChange={processTime.onChange}
|
||||
/>
|
||||
<SwapAction
|
||||
options={swapOptions}
|
||||
defaultOption={swapDefaultOption}
|
||||
onSelect={onSwapSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssemblyAction;
|
||||
@@ -5,18 +5,21 @@ import RenameInput from "../../../../../ui/inputs/RenameInput";
|
||||
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||
import Trigger from "../trigger/Trigger";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
|
||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
import WorkerAction from "../actions/workerAction";
|
||||
import AssemblyAction from "../actions/assemblyAction";
|
||||
|
||||
function HumanMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"worker">("worker");
|
||||
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
|
||||
const [speed, setSpeed] = useState("0.5");
|
||||
const [loadCapacity, setLoadCapacity] = useState("1");
|
||||
const [processTime, setProcessTime] = useState(10);
|
||||
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
|
||||
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
|
||||
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
@@ -48,6 +51,8 @@ function HumanMechanics() {
|
||||
) as HumanEventSchema | undefined
|
||||
)?.speed?.toString() || "1");
|
||||
setLoadCapacity(point.action.loadCapacity.toString());
|
||||
setProcessTime(point.action.processTime || 10);
|
||||
setSwappedMaterial(point.action.swapMaterial || "Default material");
|
||||
}
|
||||
} else {
|
||||
clearSelectedAction();
|
||||
@@ -158,6 +163,52 @@ function HumanMechanics() {
|
||||
setLoadCapacity(value);
|
||||
};
|
||||
|
||||
const handleProcessTimeChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
const updatedAction = { ...currentAction };
|
||||
updatedAction.processTime = value
|
||||
|
||||
const updatedPoint = { ...selectedPointData, action: updatedAction };
|
||||
|
||||
const event = updateAction(
|
||||
selectedProduct.productUuid,
|
||||
selectedAction.actionId,
|
||||
updatedAction
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
|
||||
}
|
||||
|
||||
setCurrentAction(updatedAction);
|
||||
setSelectedPointData(updatedPoint);
|
||||
setProcessTime(value);
|
||||
};
|
||||
|
||||
const handleMaterialChange = (value: string) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
const updatedAction = { ...currentAction };
|
||||
updatedAction.swapMaterial = value
|
||||
|
||||
const updatedPoint = { ...selectedPointData, action: updatedAction };
|
||||
|
||||
const event = updateAction(
|
||||
selectedProduct.productUuid,
|
||||
selectedAction.actionId,
|
||||
updatedAction
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
|
||||
}
|
||||
|
||||
setCurrentAction(updatedAction);
|
||||
setSelectedPointData(updatedPoint);
|
||||
setSwappedMaterial(value);
|
||||
};
|
||||
|
||||
const handleClearPoints = () => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
@@ -262,23 +313,38 @@ function HumanMechanics() {
|
||||
<LabledDropdown
|
||||
label="Action Type"
|
||||
defaultOption={activeOption}
|
||||
options={["worker"]}
|
||||
options={["worker", "assembly"]}
|
||||
onSelect={handleSelectActionType}
|
||||
disabled={true}
|
||||
/>
|
||||
</div>
|
||||
<WorkerAction
|
||||
loadCapacity={{
|
||||
value: loadCapacity,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
disabled: true,
|
||||
onChange: handleLoadCapacityChange,
|
||||
}}
|
||||
clearPoints={handleClearPoints}
|
||||
/>
|
||||
{currentAction.actionType === 'worker' &&
|
||||
<WorkerAction
|
||||
loadCapacity={{
|
||||
value: loadCapacity,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
defaultValue: "10",
|
||||
disabled: true,
|
||||
onChange: handleLoadCapacityChange,
|
||||
}}
|
||||
clearPoints={handleClearPoints}
|
||||
/>
|
||||
}
|
||||
{currentAction.actionType === 'assembly' &&
|
||||
<AssemblyAction
|
||||
processTime={{
|
||||
value: processTime,
|
||||
min: 1,
|
||||
max: 60,
|
||||
onChange: handleProcessTimeChange,
|
||||
}}
|
||||
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
|
||||
swapDefaultOption={swappedMaterial}
|
||||
onSwapSelect={handleMaterialChange}
|
||||
/>
|
||||
}
|
||||
<div className="tirgger">
|
||||
<Trigger selectedPointData={selectedPointData as any} type="Human" />
|
||||
</div>
|
||||
|
||||
@@ -80,7 +80,6 @@ const InputRange: React.FC<InputToggleProps> = ({
|
||||
disabled={disabled}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
console.log("e.key: ", e.key);
|
||||
handlekey(e);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
|
||||
import texturePath from "../../../../assets/textures/floor/white1.png";
|
||||
import texturePathDark from "../../../../assets/textures/floor/black.png";
|
||||
|
||||
// Cache for materials
|
||||
const materialCache = new Map<string, THREE.Material>();
|
||||
|
||||
export default function addFloorToScene(
|
||||
shape: THREE.Shape,
|
||||
layer: number,
|
||||
floorGroup: Types.RefGroup,
|
||||
userData: any,
|
||||
) {
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
const textureScale = CONSTANTS.floorConfig.textureScale;
|
||||
|
||||
const materialKey = `floorMaterial_${textureScale}`;
|
||||
|
||||
let material: THREE.Material;
|
||||
|
||||
if (materialCache.has(materialKey)) {
|
||||
material = materialCache.get(materialKey) as THREE.Material;
|
||||
} else {
|
||||
const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath);
|
||||
// const floorTexture = textureLoader.load(texturePath);
|
||||
|
||||
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
|
||||
floorTexture.repeat.set(textureScale, textureScale);
|
||||
floorTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
material = new THREE.MeshStandardMaterial({
|
||||
map: floorTexture,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
|
||||
materialCache.set(materialKey, material);
|
||||
}
|
||||
|
||||
const extrudeSettings = {
|
||||
depth: CONSTANTS.floorConfig.height,
|
||||
bevelEnabled: false,
|
||||
};
|
||||
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
mesh.receiveShadow = true;
|
||||
mesh.position.y = (layer) * CONSTANTS.wallConfig.height;
|
||||
mesh.rotateX(Math.PI / 2);
|
||||
mesh.name = `Floor_Layer_${layer}`;
|
||||
|
||||
// Store UUIDs for debugging or future processing
|
||||
mesh.userData.uuids = userData;
|
||||
|
||||
floorGroup.current.add(mesh);
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import * as turf from '@turf/turf';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
||||
// temp
|
||||
import blueFloorImage from "../../../../assets/textures/floor/blue.png"
|
||||
|
||||
function loadOnlyFloors(
|
||||
floorGroup: Types.RefGroup,
|
||||
linesByLayer: any,
|
||||
layer: any,
|
||||
): void {
|
||||
|
||||
////////// Creating polygon floor based on the onlyFloorlines.current which does not add roof to it, The lines are still stored in Lines.current as well //////////
|
||||
|
||||
let floorsInLayer = linesByLayer[layer];
|
||||
floorsInLayer = floorsInLayer.filter((line: any) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName);
|
||||
const floorResult = floorsInLayer.map((pair: [THREE.Vector3, string, number, string][]) =>
|
||||
pair.map((point) => ({
|
||||
position: [point[0].x, point[0].z],
|
||||
uuid: point[1]
|
||||
}))
|
||||
);
|
||||
const FloorLineFeatures = floorResult.map((line: any) => turf.lineString(line.map((p: any) => p.position)));
|
||||
|
||||
function identifyPolygonsAndConnectedLines(FloorLineFeatures: any) {
|
||||
const floorpolygons = [];
|
||||
const connectedLines = [];
|
||||
const unprocessedLines = [...FloorLineFeatures]; // Copy the features
|
||||
|
||||
while (unprocessedLines.length > 0) {
|
||||
const currentLine = unprocessedLines.pop();
|
||||
const coordinates = currentLine.geometry.coordinates;
|
||||
|
||||
// Check if the line is closed (forms a polygon)
|
||||
if (
|
||||
coordinates[0][0] === coordinates[coordinates.length - 1][0] &&
|
||||
coordinates[0][1] === coordinates[coordinates.length - 1][1]
|
||||
) {
|
||||
floorpolygons.push(turf.polygon([coordinates])); // Add as a polygon
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the line connects to another line
|
||||
let connected = false;
|
||||
for (let i = unprocessedLines.length - 1; i >= 0; i--) {
|
||||
const otherCoordinates = unprocessedLines[i].geometry.coordinates;
|
||||
|
||||
// Check if lines share a start or end point
|
||||
if (
|
||||
coordinates[0][0] === otherCoordinates[otherCoordinates.length - 1][0] &&
|
||||
coordinates[0][1] === otherCoordinates[otherCoordinates.length - 1][1]
|
||||
) {
|
||||
// Merge lines
|
||||
const mergedCoordinates = [...otherCoordinates, ...coordinates.slice(1)];
|
||||
unprocessedLines[i] = turf.lineString(mergedCoordinates);
|
||||
connected = true;
|
||||
break;
|
||||
} else if (
|
||||
coordinates[coordinates.length - 1][0] === otherCoordinates[0][0] &&
|
||||
coordinates[coordinates.length - 1][1] === otherCoordinates[0][1]
|
||||
) {
|
||||
// Merge lines
|
||||
const mergedCoordinates = [...coordinates, ...otherCoordinates.slice(1)];
|
||||
unprocessedLines[i] = turf.lineString(mergedCoordinates);
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
connectedLines.push(currentLine); // Add unconnected line as-is
|
||||
}
|
||||
}
|
||||
|
||||
return { floorpolygons, connectedLines };
|
||||
}
|
||||
|
||||
const { floorpolygons, connectedLines } = identifyPolygonsAndConnectedLines(FloorLineFeatures);
|
||||
|
||||
function convertConnectedLinesToPolygons(connectedLines: any) {
|
||||
return connectedLines.map((line: any) => {
|
||||
const coordinates = line.geometry.coordinates;
|
||||
|
||||
// If the line has more than two points, close the polygon
|
||||
if (coordinates.length > 2) {
|
||||
const firstPoint = coordinates[0];
|
||||
const lastPoint = coordinates[coordinates.length - 1];
|
||||
|
||||
// Check if already closed; if not, close it
|
||||
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
|
||||
coordinates.push(firstPoint);
|
||||
}
|
||||
|
||||
// Convert the closed line into a polygon
|
||||
return turf.polygon([coordinates]);
|
||||
}
|
||||
|
||||
// If not enough points for a polygon, return the line unchanged
|
||||
return line;
|
||||
});
|
||||
}
|
||||
|
||||
const convertedConnectedPolygons = convertConnectedLinesToPolygons(connectedLines);
|
||||
|
||||
if (convertedConnectedPolygons.length > 0) {
|
||||
const validPolygons = convertedConnectedPolygons.filter(
|
||||
(polygon: any) => polygon.geometry?.type === "Polygon"
|
||||
);
|
||||
|
||||
if (validPolygons.length > 0) {
|
||||
floorpolygons.push(...validPolygons);
|
||||
}
|
||||
}
|
||||
|
||||
function convertPolygonsToOriginalFormat(floorpolygons: any, originalLines: [THREE.Vector3, string, number, string][][]) {
|
||||
return floorpolygons.map((polygon: any) => {
|
||||
const coordinates = polygon.geometry.coordinates[0]; // Extract the coordinates array (assume it's a single polygon)
|
||||
|
||||
// Map each coordinate back to its original structure
|
||||
const mappedPoints = coordinates.map((coord: [number, number]) => {
|
||||
const [x, z] = coord;
|
||||
|
||||
// Find the original point matching this coordinate
|
||||
const originalPoint = originalLines.flat().find(([point]) => point.x === x && point.z === z);
|
||||
|
||||
if (!originalPoint) {
|
||||
throw new Error(`Original point for coordinate [${x}, ${z}] not found.`);
|
||||
}
|
||||
|
||||
return originalPoint;
|
||||
});
|
||||
|
||||
// Create pairs of consecutive points
|
||||
const pairs: typeof originalLines = [];
|
||||
for (let i = 0; i < mappedPoints.length - 1; i++) {
|
||||
pairs.push([mappedPoints[i], mappedPoints[i + 1]]);
|
||||
}
|
||||
|
||||
return pairs;
|
||||
});
|
||||
}
|
||||
|
||||
const convertedFloorPolygons: Types.OnlyFloorLines = convertPolygonsToOriginalFormat(floorpolygons, floorsInLayer);
|
||||
|
||||
convertedFloorPolygons.forEach((floor) => {
|
||||
const points: THREE.Vector3[] = [];
|
||||
|
||||
floor.forEach((lineSegment) => {
|
||||
const startPoint = lineSegment[0][0];
|
||||
points.push(new THREE.Vector3(startPoint.x, startPoint.y, startPoint.z));
|
||||
});
|
||||
|
||||
const lastLine = floor[floor.length - 1];
|
||||
const endPoint = lastLine[1][0];
|
||||
points.push(new THREE.Vector3(endPoint.x, endPoint.y, endPoint.z));
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(points[0].x, points[0].z);
|
||||
|
||||
points.forEach(point => shape.lineTo(point.x, point.z));
|
||||
shape.closePath();
|
||||
|
||||
const extrudeSettings = {
|
||||
depth: 0.3,
|
||||
bevelEnabled: false
|
||||
};
|
||||
|
||||
const texture = new THREE.TextureLoader().load(blueFloorImage);
|
||||
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.floorConfig.defaultColor, side: THREE.DoubleSide, map: texture });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
|
||||
mesh.position.y = (floor[0][0][2] - 0.99) * CONSTANTS.wallConfig.height;
|
||||
mesh.rotateX(Math.PI / 2);
|
||||
mesh.name = `Only_Floor_Line_${floor[0][0][2]}`;
|
||||
|
||||
mesh.userData = floor;
|
||||
floorGroup?.current?.add(mesh);
|
||||
});
|
||||
}
|
||||
|
||||
export default loadOnlyFloors;
|
||||
@@ -1,291 +0,0 @@
|
||||
// import { useEffect } from "react";
|
||||
// import {
|
||||
// useObjectPosition,
|
||||
// useObjectRotation,
|
||||
// useSelectedWallItem,
|
||||
// useSocketStore,
|
||||
// useWallItems,
|
||||
// useSelectedItem,
|
||||
// useToolMode,
|
||||
// } from "../../../store/builder/store";
|
||||
// import { Csg } from "../csg/csg";
|
||||
// import * as Types from "../../../types/world/worldTypes";
|
||||
// import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
// import * as THREE from "three";
|
||||
// import { useThree } from "@react-three/fiber";
|
||||
// import handleMeshMissed from "../eventFunctions/handleMeshMissed";
|
||||
// import DeleteWallItems from "../geomentries/walls/deleteWallItems";
|
||||
// import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
|
||||
// import AddWallItems from "../geomentries/walls/addWallItems";
|
||||
// import useModuleStore from "../../../store/useModuleStore";
|
||||
// import { useParams } from "react-router-dom";
|
||||
// import { getUserData } from "../../../functions/getUserData";
|
||||
// import { useVersionContext } from "../version/versionContext";
|
||||
|
||||
// const WallItemsGroup = ({
|
||||
// currentWallItem,
|
||||
// hoveredDeletableWallItem,
|
||||
// selectedItemsIndex,
|
||||
// setSelectedItemsIndex,
|
||||
// CSGGroup,
|
||||
// }: any) => {
|
||||
// const state = useThree();
|
||||
// const { socket } = useSocketStore();
|
||||
// const { pointer, camera, raycaster } = state;
|
||||
// const { toolMode } = useToolMode();
|
||||
// const { wallItems, setWallItems } = useWallItems();
|
||||
// const { setObjectPosition } = useObjectPosition();
|
||||
// const { setObjectRotation } = useObjectRotation();
|
||||
// const { setSelectedWallItem } = useSelectedWallItem();
|
||||
// const { activeModule } = useModuleStore();
|
||||
// const { selectedItem } = useSelectedItem();
|
||||
// const { selectedVersionStore } = useVersionContext();
|
||||
// const { selectedVersion } = selectedVersionStore();
|
||||
// const { projectId } = useParams();
|
||||
// const { userId, organization } = getUserData();
|
||||
|
||||
// useEffect(() => {
|
||||
// // Load Wall Items from the backend
|
||||
// if (!projectId || !selectedVersion) return;
|
||||
// loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId);
|
||||
// }, [selectedVersion?.versionId]);
|
||||
|
||||
// ////////// Update the Position value changes in the selected item //////////
|
||||
|
||||
// useEffect(() => {
|
||||
// const canvasElement = state.gl.domElement;
|
||||
// function handlePointerMove(e: any) {
|
||||
// if (selectedItemsIndex !== null && toolMode === 'cursor' && e.buttons === 1) {
|
||||
// const Raycaster = state.raycaster;
|
||||
// const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true);
|
||||
// const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference"));
|
||||
|
||||
// if (Object) {
|
||||
// (state.controls as any)!.enabled = false;
|
||||
// setWallItems((prevItems: any) => {
|
||||
// const updatedItems = [...prevItems];
|
||||
// let position: [number, number, number] = [0, 0, 0];
|
||||
|
||||
// if (updatedItems[selectedItemsIndex].type === "fixed-move") {
|
||||
// position = [
|
||||
// Object!.point.x,
|
||||
// Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) *
|
||||
// CONSTANTS.wallConfig.height,
|
||||
// Object!.point.z,
|
||||
// ];
|
||||
// } else if (updatedItems[selectedItemsIndex].type === "free-move") {
|
||||
// position = [Object!.point.x, Object!.point.y, Object!.point.z];
|
||||
// }
|
||||
|
||||
// requestAnimationFrame(() => {
|
||||
// setObjectPosition(new THREE.Vector3(...position));
|
||||
// setObjectRotation({
|
||||
// x: THREE.MathUtils.radToDeg(Object!.object.rotation.x),
|
||||
// y: THREE.MathUtils.radToDeg(Object!.object.rotation.y),
|
||||
// z: THREE.MathUtils.radToDeg(Object!.object.rotation.z),
|
||||
// });
|
||||
// });
|
||||
|
||||
// updatedItems[selectedItemsIndex] = {
|
||||
// ...updatedItems[selectedItemsIndex],
|
||||
// position: position,
|
||||
// quaternion: Object!.object.quaternion.clone() as Types.QuaternionType,
|
||||
// };
|
||||
|
||||
// return updatedItems;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function handlePointerUp() {
|
||||
// const Raycaster = state.raycaster;
|
||||
// const intersects = Raycaster.intersectObjects(
|
||||
// CSGGroup.current?.children[0].children!,
|
||||
// true
|
||||
// );
|
||||
// const Object = intersects.find((child) =>
|
||||
// child.object.name.includes("WallRaycastReference")
|
||||
// );
|
||||
// if (Object) {
|
||||
// if (selectedItemsIndex !== null) {
|
||||
// let currentItem: any = null;
|
||||
// setWallItems((prevItems: any) => {
|
||||
// const updatedItems = [...prevItems];
|
||||
// const WallItemsForStorage = updatedItems.map((item) => {
|
||||
// const { model, ...rest } = item;
|
||||
// return {
|
||||
// ...rest,
|
||||
// modelUuid: model?.uuid,
|
||||
// };
|
||||
// });
|
||||
|
||||
// currentItem = updatedItems[selectedItemsIndex];
|
||||
// localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
|
||||
// return updatedItems;
|
||||
// });
|
||||
|
||||
// setTimeout(async () => {
|
||||
|
||||
// //REST
|
||||
|
||||
// // await setWallItem(
|
||||
// // organization,
|
||||
// // currentItem?.model?.uuid,
|
||||
// // currentItem.modelName,
|
||||
// // currentItem.assetId,
|
||||
// // currentItem.type!,
|
||||
// // currentItem.csgposition!,
|
||||
// // currentItem.csgscale!,
|
||||
// // currentItem.position,
|
||||
// // currentItem.quaternion,
|
||||
// // currentItem.scale!,
|
||||
// // )
|
||||
|
||||
// //SOCKET
|
||||
|
||||
// const data = {
|
||||
// organization,
|
||||
// modelUuid: currentItem.model?.uuid!,
|
||||
// assetId: currentItem.assetId,
|
||||
// modelName: currentItem.modelName!,
|
||||
// type: currentItem.type!,
|
||||
// csgposition: currentItem.csgposition!,
|
||||
// csgscale: currentItem.csgscale!,
|
||||
// position: currentItem.position!,
|
||||
// quaternion: currentItem.quaternion,
|
||||
// scale: currentItem.scale!,
|
||||
// socketId: socket.id,
|
||||
// versionId: selectedVersion?.versionId || '',
|
||||
// projectId,
|
||||
// userId
|
||||
// };
|
||||
|
||||
// // console.log('data: ', data);
|
||||
// socket.emit("v1:wallItems:set", data);
|
||||
// }, 0);
|
||||
// (state.controls as any)!.enabled = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// canvasElement.addEventListener("pointermove", handlePointerMove);
|
||||
// canvasElement.addEventListener("pointerup", handlePointerUp);
|
||||
|
||||
// return () => {
|
||||
// canvasElement.removeEventListener("pointermove", handlePointerMove);
|
||||
// canvasElement.removeEventListener("pointerup", handlePointerUp);
|
||||
// };
|
||||
// }, [selectedItemsIndex, selectedVersion?.versionId]);
|
||||
|
||||
// useEffect(() => {
|
||||
// const canvasElement = state.gl.domElement;
|
||||
// let drag = false;
|
||||
// let isLeftMouseDown = false;
|
||||
|
||||
// const onMouseDown = (evt: any) => {
|
||||
// if (evt.button === 0) {
|
||||
// isLeftMouseDown = true;
|
||||
// drag = false;
|
||||
// }
|
||||
// };
|
||||
|
||||
// const onMouseUp = (evt: any) => {
|
||||
// if (evt.button === 0) {
|
||||
// isLeftMouseDown = false;
|
||||
// if (!drag && toolMode === '3D-Delete' && activeModule === "builder") {
|
||||
// DeleteWallItems(
|
||||
// hoveredDeletableWallItem,
|
||||
// setWallItems,
|
||||
// wallItems,
|
||||
// socket,
|
||||
// projectId,
|
||||
// selectedVersion?.versionId || '',
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// const onMouseMove = () => {
|
||||
// if (isLeftMouseDown) {
|
||||
// drag = true;
|
||||
// }
|
||||
// };
|
||||
|
||||
// const onDrop = (event: any) => {
|
||||
// if (selectedItem.category !== 'Fenestration') return;
|
||||
|
||||
// pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
// pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
// raycaster.setFromCamera(pointer, camera);
|
||||
|
||||
// if (selectedItem.id && selectedVersion && projectId) {
|
||||
// if (selectedItem.subCategory) {
|
||||
// AddWallItems(
|
||||
// selectedItem,
|
||||
// raycaster,
|
||||
// CSGGroup,
|
||||
// setWallItems,
|
||||
// socket,
|
||||
// projectId,
|
||||
// selectedVersion?.versionId || '',
|
||||
// );
|
||||
// }
|
||||
// event.preventDefault();
|
||||
// }
|
||||
// };
|
||||
|
||||
// const onDragOver = (event: any) => {
|
||||
// event.preventDefault();
|
||||
// };
|
||||
|
||||
// canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
// canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
// canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
// canvasElement.addEventListener("drop", onDrop);
|
||||
// canvasElement.addEventListener("dragover", onDragOver);
|
||||
|
||||
// return () => {
|
||||
// canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
// canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
// canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
// canvasElement.removeEventListener("drop", onDrop);
|
||||
// canvasElement.removeEventListener("dragover", onDragOver);
|
||||
// };
|
||||
// }, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (toolMode && activeModule === "builder") {
|
||||
// handleMeshMissed(
|
||||
// currentWallItem,
|
||||
// setSelectedWallItem,
|
||||
// setSelectedItemsIndex
|
||||
// );
|
||||
// setSelectedWallItem(null);
|
||||
// setSelectedItemsIndex(null);
|
||||
// }
|
||||
// }, [toolMode]);
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {wallItems.map((item: Types.WallItem, index: number) => (
|
||||
// <group
|
||||
// key={index}
|
||||
// position={item.position}
|
||||
// quaternion={item.quaternion}
|
||||
// scale={item.scale}
|
||||
// >
|
||||
// <Csg
|
||||
// position={item.csgposition!}
|
||||
// scale={item.csgscale!}
|
||||
// model={item.model!}
|
||||
// hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||
// />
|
||||
// </group>
|
||||
// ))}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default WallItemsGroup;
|
||||
@@ -1,74 +0,0 @@
|
||||
// import { Geometry } from "@react-three/csg";
|
||||
// import {
|
||||
// useSelectedWallItem,
|
||||
// useToggleView,
|
||||
// useToolMode,
|
||||
// useWallItems,
|
||||
// useWalls,
|
||||
// } from "../../../store/builder/store";
|
||||
// import handleMeshDown from "../eventFunctions/handleMeshDown";
|
||||
// import handleMeshMissed from "../eventFunctions/handleMeshMissed";
|
||||
// import WallsMesh from "./wallsMesh";
|
||||
// import WallItemsGroup from "./wallItemsGroup";
|
||||
|
||||
// const WallsAndWallItems = ({
|
||||
// CSGGroup,
|
||||
// setSelectedItemsIndex,
|
||||
// selectedItemsIndex,
|
||||
// currentWallItem,
|
||||
// csg,
|
||||
// lines,
|
||||
// hoveredDeletableWallItem,
|
||||
// }: any) => {
|
||||
// const { walls } = useWalls();
|
||||
// const { wallItems } = useWallItems();
|
||||
// const { toggleView } = useToggleView();
|
||||
// const { toolMode } = useToolMode();
|
||||
// const { setSelectedWallItem } = useSelectedWallItem();
|
||||
|
||||
// return (
|
||||
// <mesh
|
||||
// ref={CSGGroup as any}
|
||||
// name="Walls"
|
||||
// key={walls.length}
|
||||
// receiveShadow
|
||||
// visible={!toggleView}
|
||||
// onClick={(event) => {
|
||||
// if (toolMode === "cursor") {
|
||||
// handleMeshDown(
|
||||
// event,
|
||||
// currentWallItem,
|
||||
// setSelectedWallItem,
|
||||
// setSelectedItemsIndex,
|
||||
// wallItems,
|
||||
// toggleView
|
||||
// );
|
||||
// }
|
||||
// }}
|
||||
// onPointerMissed={() => {
|
||||
// if (toolMode === "cursor") {
|
||||
// handleMeshMissed(
|
||||
// currentWallItem,
|
||||
// setSelectedWallItem,
|
||||
// setSelectedItemsIndex
|
||||
// );
|
||||
// setSelectedWallItem(null);
|
||||
// setSelectedItemsIndex(null);
|
||||
// }
|
||||
// }}
|
||||
// >
|
||||
// <Geometry ref={csg as any} computeVertexNormals useGroups>
|
||||
// <WallsMesh lines={lines} />
|
||||
// <WallItemsGroup
|
||||
// currentWallItem={currentWallItem}
|
||||
// hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||
// selectedItemsIndex={selectedItemsIndex}
|
||||
// setSelectedItemsIndex={setSelectedItemsIndex}
|
||||
// CSGGroup={CSGGroup}
|
||||
// />
|
||||
// </Geometry>
|
||||
// </mesh>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default WallsAndWallItems;
|
||||
@@ -1,82 +0,0 @@
|
||||
// import * as THREE from "three";
|
||||
// import * as Types from "../../../types/world/worldTypes";
|
||||
// import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
// import { Base } from "@react-three/csg";
|
||||
// import { MeshDiscardMaterial } from "@react-three/drei";
|
||||
// import { useUpdateScene, useWalls } from "../../../store/builder/store";
|
||||
// import React, { useEffect } from "react";
|
||||
// import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
|
||||
// import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
|
||||
// import loadWalls from "../geomentries/walls/loadWalls";
|
||||
// import texturePath from "../../../assets/textures/floor/wall-tex.png";
|
||||
// import { useParams } from "react-router-dom";
|
||||
// import { getUserData } from "../../../functions/getUserData";
|
||||
// import { useVersionContext } from "../version/versionContext";
|
||||
|
||||
// const WallsMeshComponent = ({ lines }: any) => {
|
||||
// const { walls, setWalls } = useWalls();
|
||||
// const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
// const { projectId } = useParams();
|
||||
// const { selectedVersionStore } = useVersionContext();
|
||||
// const { selectedVersion } = selectedVersionStore();
|
||||
// const { organization } = getUserData();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (updateScene) {
|
||||
// if (!selectedVersion) {
|
||||
// setUpdateScene(false);
|
||||
// return;
|
||||
// };
|
||||
// getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => {
|
||||
// const Lines: Types.Lines = objectLinesToArray(data);
|
||||
// localStorage.setItem("Lines", JSON.stringify(Lines));
|
||||
|
||||
// if (Lines) {
|
||||
// loadWalls(lines, setWalls);
|
||||
// }
|
||||
// });
|
||||
// setUpdateScene(false);
|
||||
// }
|
||||
// }, [updateScene, selectedVersion?.versionId]);
|
||||
|
||||
// const textureLoader = new THREE.TextureLoader();
|
||||
// const wallTexture = textureLoader.load(texturePath);
|
||||
|
||||
// wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
|
||||
// wallTexture.repeat.set(0.1, 0.1);
|
||||
// wallTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {walls.map((wall: Types.Wall, index: number) => (
|
||||
// <mesh key={index} renderOrder={1}>
|
||||
// <Base
|
||||
// name={`Wall${index + 1}`}
|
||||
// geometry={wall[0]}
|
||||
// rotation={wall[1]}
|
||||
// position={wall[2]}
|
||||
// userData={{ WallType: wall[3], Layer: wall[4] }}
|
||||
// >
|
||||
// <meshStandardMaterial
|
||||
// side={THREE.DoubleSide}
|
||||
// color={CONSTANTS.wallConfig.defaultColor}
|
||||
// map={wallTexture}
|
||||
// />
|
||||
// </Base>
|
||||
// <mesh
|
||||
// castShadow
|
||||
// geometry={wall[0]}
|
||||
// rotation={wall[1]}
|
||||
// position={wall[2]}
|
||||
// name={`WallRaycastReference_${index + 1}`}
|
||||
// >
|
||||
// <MeshDiscardMaterial />
|
||||
// </mesh>
|
||||
// </mesh>
|
||||
// ))}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// const WallsMesh = React.memo(WallsMeshComponent);
|
||||
// export default WallsMesh;
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useCallback } from "react";
|
||||
import { useSceneContext } from "../../../../scene/sceneContext";
|
||||
import { useProductContext } from "../../../products/productContext";
|
||||
|
||||
export function useAssemblyHandler() {
|
||||
const { materialStore, humanStore, productStore } = useSceneContext();
|
||||
const { getMaterialById } = materialStore();
|
||||
const { getModelUuidByActionUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
|
||||
|
||||
const assemblyLogStatus = (materialUuid: string, status: string) => {
|
||||
echo.info(`${materialUuid}, ${status}`);
|
||||
}
|
||||
|
||||
const handleAssembly = useCallback((action: HumanAction, materialId?: string) => {
|
||||
if (!action || action.actionType !== 'assembly' || !materialId) return;
|
||||
|
||||
const material = getMaterialById(materialId);
|
||||
if (!material) return;
|
||||
|
||||
const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
|
||||
if (!modelUuid) return;
|
||||
|
||||
incrementHumanLoad(modelUuid, 1);
|
||||
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
|
||||
|
||||
assemblyLogStatus(material.materialName, `performing assembly action`);
|
||||
|
||||
}, [getMaterialById]);
|
||||
|
||||
return {
|
||||
handleAssembly,
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
|
||||
import { useAssemblyHandler } from './actionHandler/useAssemblyHandler';
|
||||
|
||||
export function useHumanActions() {
|
||||
const { handleWorker } = useWorkerHandler();
|
||||
const { handleAssembly } = useAssemblyHandler();
|
||||
|
||||
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
handleWorker(action, materialId);
|
||||
}, [handleWorker]);
|
||||
|
||||
const handleAssemblyAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
handleAssembly(action, materialId);
|
||||
}, [handleAssembly]);
|
||||
|
||||
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
if (!action) return;
|
||||
|
||||
@@ -15,10 +21,13 @@ export function useHumanActions() {
|
||||
case 'worker':
|
||||
handleWorkerAction(action, materialId);
|
||||
break;
|
||||
case 'assembly':
|
||||
handleAssemblyAction(action, materialId);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown Human action type: ${action.actionType}`);
|
||||
}
|
||||
}, [handleWorkerAction]);
|
||||
}, [handleWorkerAction, handleAssemblyAction]);
|
||||
|
||||
const cleanup = useCallback(() => {
|
||||
}, []);
|
||||
|
||||
@@ -39,7 +39,7 @@ export function useActionHandler() {
|
||||
case 'store': case 'retrieve':
|
||||
handleStorageAction(action as StorageAction, materialId as string);
|
||||
break;
|
||||
case 'worker':
|
||||
case 'worker': case 'assembly':
|
||||
handleHumanAction(action as HumanAction, materialId as string);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -6,6 +6,7 @@ import { MaterialModel } from '../../../materials/instances/material/materialMod
|
||||
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
|
||||
const meshRef = useRef<any>(null!);
|
||||
const [hasLoad, setHasLoad] = useState(false);
|
||||
const [isAttached, setIsAttached] = useState(false);
|
||||
const { scene } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -13,32 +14,45 @@ const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
|
||||
}, [human.currentLoad]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasLoad || !meshRef.current) return;
|
||||
if (!hasLoad || !meshRef.current) {
|
||||
setIsAttached(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D;
|
||||
if (!humanModel) return;
|
||||
|
||||
meshRef.current.visible = false;
|
||||
|
||||
const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone;
|
||||
if (bone) {
|
||||
if (meshRef.current.parent) {
|
||||
meshRef.current.parent.remove(meshRef.current);
|
||||
}
|
||||
|
||||
bone.add(meshRef.current);
|
||||
|
||||
meshRef.current.position.set(0, 0, 0);
|
||||
meshRef.current.rotation.set(0, 0, 0);
|
||||
meshRef.current.scale.set(1, 1, 1);
|
||||
|
||||
meshRef.current.visible = true;
|
||||
setIsAttached(true);
|
||||
}
|
||||
}, [hasLoad, human.modelUuid]);
|
||||
}, [hasLoad, human.modelUuid, scene]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasLoad && human.currentMaterials.length > 0 && (
|
||||
{hasLoad && human.point.action.actionType === 'worker' && human.currentMaterials.length > 0 && (
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={human.currentMaterials[0].materialId || ''}
|
||||
materialType={human.currentMaterials[0].materialType || 'Default material'}
|
||||
visible={isAttached}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MaterialAnimator;
|
||||
export default MaterialAnimator;
|
||||
@@ -16,7 +16,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime } = materialStore();
|
||||
const { removeMaterial, setEndTime, setMaterial } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
@@ -41,6 +41,13 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
const processStartTimeRef = useRef<number | null>(null);
|
||||
const processTimeRef = useRef<number>(0);
|
||||
const processAnimationIdRef = useRef<number | null>(null);
|
||||
const accumulatedPausedTimeRef = useRef<number>(0);
|
||||
const lastPauseTimeRef = useRef<number | null>(null);
|
||||
const hasLoggedHalfway = useRef(false);
|
||||
const hasLoggedCompleted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
@@ -94,6 +101,16 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
cancelAnimationFrame(animationFrameIdRef.current)
|
||||
animationFrameIdRef.current = null
|
||||
}
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
processStartTimeRef.current = null;
|
||||
processTimeRef.current = 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
@@ -103,7 +120,103 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint) return;
|
||||
if (!human.point.action.assemblyPoint || human.point.action.actionType === 'worker') return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase('waiting');
|
||||
setHumanPicking(human.modelUuid, false);
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'waiting') {
|
||||
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentPhase('assembling');
|
||||
setHumanPicking(human.modelUuid, true);
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
processTimeRef.current = human.point.action.processTime || 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if (human.point.action.assemblyPoint && currentPhase === 'assembling') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase('waiting');
|
||||
setHumanPicking(human.modelUuid, false);
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
if (material) {
|
||||
triggerPointActions(human.point.action, material.materialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
const trackAssemblyProcess = useCallback(() => {
|
||||
const now = performance.now();
|
||||
|
||||
if (!processStartTimeRef.current || !human.point.action.processTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPausedRef.current) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
lastPauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
||||
const totalProcessTimeMs = human.point.action.processTime * 1000;
|
||||
|
||||
if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
|
||||
hasLoggedHalfway.current = true;
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setMaterial(human.currentMaterials[0].materialId, human.point.action.swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
hasLoggedCompleted.current = true;
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}, [human.modelUuid, human.point.action.processTime, human.currentMaterials]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint || human.point.action.actionType === 'assembly') return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
|
||||
const toPickupPath = computePath(
|
||||
@@ -145,7 +258,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0) {
|
||||
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
}
|
||||
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
|
||||
|
||||
@@ -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
|
||||
@@ -301,7 +301,6 @@ const VehicleUI = () => {
|
||||
return selectedVehicleData ? (
|
||||
<group
|
||||
position={selectedVehicleData.position}
|
||||
rotation={selectedVehicleData.rotation}
|
||||
ref={outerGroup}
|
||||
>
|
||||
<group
|
||||
|
||||
@@ -299,6 +299,10 @@ export function useTriggerHandler() {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
|
||||
if (human && human.modelUuid === "cc62adae-7000-447b-b845-6d4910de503a") {
|
||||
console.log(human);
|
||||
}
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
pointUuid: material.current.pointUuid,
|
||||
@@ -330,7 +334,9 @@ export function useTriggerHandler() {
|
||||
if (vehicle) {
|
||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||
// Handle current action from vehicle
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
@@ -341,7 +347,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -354,7 +362,9 @@ export function useTriggerHandler() {
|
||||
if (vehicle) {
|
||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||
// Handle current action from vehicle
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
@@ -365,7 +375,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -382,7 +394,9 @@ export function useTriggerHandler() {
|
||||
if (conveyor) {
|
||||
if (!conveyor.isPaused) {
|
||||
// Handle current action from vehicle
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
@@ -393,7 +407,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -406,7 +422,9 @@ export function useTriggerHandler() {
|
||||
if (conveyor) {
|
||||
if (!conveyor.isPaused) {
|
||||
// Handle current action from vehicle
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
@@ -417,7 +435,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -434,7 +454,9 @@ export function useTriggerHandler() {
|
||||
if (machine) {
|
||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||
setIsPaused(materialId, true);
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
@@ -443,7 +465,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addMachineToMonitor(machine.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -456,7 +480,9 @@ export function useTriggerHandler() {
|
||||
if (machine) {
|
||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||
setIsPaused(materialId, true);
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
@@ -465,7 +491,9 @@ export function useTriggerHandler() {
|
||||
|
||||
addMachineToMonitor(machine.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
@@ -480,7 +508,9 @@ export function useTriggerHandler() {
|
||||
|
||||
// Handle current action from arm bot
|
||||
setIsPaused(materialId, true);
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
@@ -489,7 +519,9 @@ export function useTriggerHandler() {
|
||||
setIsPaused(materialId, true);
|
||||
addHumanToMonitor(human.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
);
|
||||
@@ -501,7 +533,9 @@ export function useTriggerHandler() {
|
||||
|
||||
// Handle current action from arm bot
|
||||
setIsPaused(materialId, true);
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
@@ -510,7 +544,9 @@ export function useTriggerHandler() {
|
||||
setIsPaused(materialId, true);
|
||||
addHumanToMonitor(human.modelUuid,
|
||||
() => {
|
||||
setIsVisible(materialId, false);
|
||||
if (action.actionType === 'worker') {
|
||||
setIsVisible(materialId, false);
|
||||
}
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
);
|
||||
@@ -1280,7 +1316,7 @@ export function useTriggerHandler() {
|
||||
|
||||
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
||||
const material = getMaterialById(materialId);
|
||||
if (material) {
|
||||
if (material && action.actionType === 'worker') {
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
@@ -1410,6 +1446,28 @@ export function useTriggerHandler() {
|
||||
handleAction(action, material.materialId);
|
||||
}
|
||||
|
||||
} else if (material && action.actionType === 'assembly') {
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
pointUuid: material.current.pointUuid,
|
||||
actionUuid: material.current.actionUuid,
|
||||
})
|
||||
|
||||
setCurrentLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
pointUuid: material.current.pointUuid,
|
||||
actionUuid: material.current.actionUuid,
|
||||
});
|
||||
|
||||
setNextLocation(material.materialId, {
|
||||
modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid,
|
||||
pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid,
|
||||
})
|
||||
|
||||
setIsPaused(material.materialId, false);
|
||||
setIsVisible(material.materialId, true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,40 +6,53 @@ import { MaterialModel } from '../../../materials/instances/material/materialMod
|
||||
const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
|
||||
const meshRef = useRef<any>(null!);
|
||||
const [hasLoad, setHasLoad] = useState(false);
|
||||
const [isAttached, setIsAttached] = useState(false);
|
||||
const { scene } = useThree();
|
||||
const offset = new THREE.Vector3(0, 0.85, 0);
|
||||
|
||||
useEffect(() => {
|
||||
setHasLoad(agvDetail.currentLoad > 0);
|
||||
const loadState = agvDetail.currentLoad > 0;
|
||||
setHasLoad(loadState);
|
||||
|
||||
if (!loadState) {
|
||||
setIsAttached(false);
|
||||
if (meshRef.current?.parent) {
|
||||
meshRef.current.parent.remove(meshRef.current);
|
||||
}
|
||||
}
|
||||
}, [agvDetail.currentLoad]);
|
||||
|
||||
useFrame(() => {
|
||||
if (!hasLoad || !meshRef.current) return;
|
||||
if (!hasLoad || !meshRef.current || isAttached) return;
|
||||
|
||||
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
|
||||
if (agvModel) {
|
||||
const worldPosition = offset.clone().applyMatrix4(agvModel.matrixWorld);
|
||||
meshRef.current.position.copy(worldPosition);
|
||||
meshRef.current.rotation.copy(agvModel.rotation);
|
||||
if (agvModel && !isAttached) {
|
||||
if (meshRef.current.parent) {
|
||||
meshRef.current.parent.remove(meshRef.current);
|
||||
}
|
||||
|
||||
agvModel.add(meshRef.current);
|
||||
|
||||
meshRef.current.position.copy(offset);
|
||||
meshRef.current.rotation.set(0, 0, 0);
|
||||
meshRef.current.scale.set(1, 1, 1);
|
||||
|
||||
setIsAttached(true);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasLoad && (
|
||||
<>
|
||||
{agvDetail.currentMaterials.length > 0 &&
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={agvDetail.currentMaterials[0].materialId || ''}
|
||||
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
{hasLoad && agvDetail.currentMaterials.length > 0 && (
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={agvDetail.currentMaterials[0].materialId || ''}
|
||||
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}
|
||||
visible={isAttached}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default MaterialAnimator;
|
||||
export default MaterialAnimator;
|
||||
@@ -80,7 +80,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
const distances = [];
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
const rotationSpeed = 1;
|
||||
const rotationSpeed = 0.75;
|
||||
|
||||
for (let i = 0; i < currentPath.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...currentPath[i]);
|
||||
@@ -100,17 +100,25 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
||||
const currentAngle = object.rotation.y;
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
targetQuaternion.multiply(y180);
|
||||
|
||||
let angleDifference = targetAngle - currentAngle;
|
||||
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
|
||||
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * agvDetail.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta;
|
||||
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
|
||||
const isAligned = Math.abs(angleDifference) < 0.01;
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
}
|
||||
|
||||
const isAligned = angle < 0.01;
|
||||
|
||||
if (isAligned) {
|
||||
progressRef.current += delta * (speed * agvDetail.speed);
|
||||
@@ -122,17 +130,25 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(
|
||||
objectRotation.x,
|
||||
objectRotation.y - agvDetail.point.action.steeringAngle,
|
||||
objectRotation.z
|
||||
);
|
||||
const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
|
||||
if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
|
||||
const targetEuler = new THREE.Euler(0, objectRotation.y - agvDetail.point.action.steeringAngle, 0);
|
||||
|
||||
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
object.rotation.copy(targetEuler);
|
||||
setRestingRotation(false);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * agvDetail.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ type ProductsStore = {
|
||||
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
||||
|
||||
// Point-level actions
|
||||
addPoint: (productUuid: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void;
|
||||
addPoint: (productUuid: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema) => void;
|
||||
removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => void;
|
||||
updatePoint: (
|
||||
productUuid: string,
|
||||
modelUuid: string,
|
||||
pointUuid: string,
|
||||
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema>
|
||||
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema>
|
||||
) => EventsSchema | undefined;
|
||||
|
||||
// Action-level actions
|
||||
@@ -65,9 +65,9 @@ type ProductsStore = {
|
||||
getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
|
||||
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
|
||||
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
|
||||
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
|
||||
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema | undefined;
|
||||
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']) | undefined;
|
||||
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']) | undefined;
|
||||
getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||
getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||
|
||||
5
app/src/types/simulationTypes.d.ts
vendored
5
app/src/types/simulationTypes.d.ts
vendored
@@ -72,7 +72,10 @@ interface StorageAction {
|
||||
interface HumanAction {
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
actionType: "worker";
|
||||
actionType: "worker" | "assembly";
|
||||
processTime?: number;
|
||||
swapMaterial?: string;
|
||||
assemblyPoint?: { rotation: [number, number, number] | null; }
|
||||
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
loadCapacity: number;
|
||||
|
||||
Reference in New Issue
Block a user