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 LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||||
import Trigger from "../trigger/Trigger";
|
import Trigger from "../trigger/Trigger";
|
||||||
import ActionsList from "../components/ActionsList";
|
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 { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||||
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
|
||||||
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
|
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
|
||||||
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import WorkerAction from "../actions/workerAction";
|
import WorkerAction from "../actions/workerAction";
|
||||||
|
import AssemblyAction from "../actions/assemblyAction";
|
||||||
|
|
||||||
function HumanMechanics() {
|
function HumanMechanics() {
|
||||||
const [activeOption, setActiveOption] = useState<"worker">("worker");
|
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
|
||||||
const [speed, setSpeed] = useState("0.5");
|
const [speed, setSpeed] = useState("0.5");
|
||||||
const [loadCapacity, setLoadCapacity] = useState("1");
|
const [loadCapacity, setLoadCapacity] = useState("1");
|
||||||
|
const [processTime, setProcessTime] = useState(10);
|
||||||
|
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
|
||||||
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
|
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
|
||||||
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
|
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
|
||||||
const { selectedEventData } = useSelectedEventData();
|
const { selectedEventData } = useSelectedEventData();
|
||||||
@@ -48,6 +51,8 @@ function HumanMechanics() {
|
|||||||
) as HumanEventSchema | undefined
|
) as HumanEventSchema | undefined
|
||||||
)?.speed?.toString() || "1");
|
)?.speed?.toString() || "1");
|
||||||
setLoadCapacity(point.action.loadCapacity.toString());
|
setLoadCapacity(point.action.loadCapacity.toString());
|
||||||
|
setProcessTime(point.action.processTime || 10);
|
||||||
|
setSwappedMaterial(point.action.swapMaterial || "Default material");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clearSelectedAction();
|
clearSelectedAction();
|
||||||
@@ -158,6 +163,52 @@ function HumanMechanics() {
|
|||||||
setLoadCapacity(value);
|
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 = () => {
|
const handleClearPoints = () => {
|
||||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||||
|
|
||||||
@@ -262,23 +313,38 @@ function HumanMechanics() {
|
|||||||
<LabledDropdown
|
<LabledDropdown
|
||||||
label="Action Type"
|
label="Action Type"
|
||||||
defaultOption={activeOption}
|
defaultOption={activeOption}
|
||||||
options={["worker"]}
|
options={["worker", "assembly"]}
|
||||||
onSelect={handleSelectActionType}
|
onSelect={handleSelectActionType}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkerAction
|
{currentAction.actionType === 'worker' &&
|
||||||
loadCapacity={{
|
<WorkerAction
|
||||||
value: loadCapacity,
|
loadCapacity={{
|
||||||
min: 1,
|
value: loadCapacity,
|
||||||
max: 5,
|
min: 1,
|
||||||
step: 1,
|
max: 5,
|
||||||
defaultValue: "1",
|
step: 1,
|
||||||
disabled: true,
|
defaultValue: "10",
|
||||||
onChange: handleLoadCapacityChange,
|
disabled: true,
|
||||||
}}
|
onChange: handleLoadCapacityChange,
|
||||||
clearPoints={handleClearPoints}
|
}}
|
||||||
/>
|
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">
|
<div className="tirgger">
|
||||||
<Trigger selectedPointData={selectedPointData as any} type="Human" />
|
<Trigger selectedPointData={selectedPointData as any} type="Human" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ const InputRange: React.FC<InputToggleProps> = ({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyUp={(e) => {
|
onKeyUp={(e) => {
|
||||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||||
console.log("e.key: ", e.key);
|
|
||||||
handlekey(e);
|
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 { useEffect, useCallback } from 'react';
|
||||||
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
|
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
|
||||||
|
import { useAssemblyHandler } from './actionHandler/useAssemblyHandler';
|
||||||
|
|
||||||
export function useHumanActions() {
|
export function useHumanActions() {
|
||||||
const { handleWorker } = useWorkerHandler();
|
const { handleWorker } = useWorkerHandler();
|
||||||
|
const { handleAssembly } = useAssemblyHandler();
|
||||||
|
|
||||||
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
|
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
|
||||||
handleWorker(action, materialId);
|
handleWorker(action, materialId);
|
||||||
}, [handleWorker]);
|
}, [handleWorker]);
|
||||||
|
|
||||||
|
const handleAssemblyAction = useCallback((action: HumanAction, materialId: string) => {
|
||||||
|
handleAssembly(action, materialId);
|
||||||
|
}, [handleAssembly]);
|
||||||
|
|
||||||
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
|
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
|
||||||
if (!action) return;
|
if (!action) return;
|
||||||
|
|
||||||
@@ -15,10 +21,13 @@ export function useHumanActions() {
|
|||||||
case 'worker':
|
case 'worker':
|
||||||
handleWorkerAction(action, materialId);
|
handleWorkerAction(action, materialId);
|
||||||
break;
|
break;
|
||||||
|
case 'assembly':
|
||||||
|
handleAssemblyAction(action, materialId);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Unknown Human action type: ${action.actionType}`);
|
console.warn(`Unknown Human action type: ${action.actionType}`);
|
||||||
}
|
}
|
||||||
}, [handleWorkerAction]);
|
}, [handleWorkerAction, handleAssemblyAction]);
|
||||||
|
|
||||||
const cleanup = useCallback(() => {
|
const cleanup = useCallback(() => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function useActionHandler() {
|
|||||||
case 'store': case 'retrieve':
|
case 'store': case 'retrieve':
|
||||||
handleStorageAction(action as StorageAction, materialId as string);
|
handleStorageAction(action as StorageAction, materialId as string);
|
||||||
break;
|
break;
|
||||||
case 'worker':
|
case 'worker': case 'assembly':
|
||||||
handleHumanAction(action as HumanAction, materialId as string);
|
handleHumanAction(action as HumanAction, materialId as string);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { MaterialModel } from '../../../materials/instances/material/materialMod
|
|||||||
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
|
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
|
||||||
const meshRef = useRef<any>(null!);
|
const meshRef = useRef<any>(null!);
|
||||||
const [hasLoad, setHasLoad] = useState(false);
|
const [hasLoad, setHasLoad] = useState(false);
|
||||||
|
const [isAttached, setIsAttached] = useState(false);
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -13,32 +14,45 @@ const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
|
|||||||
}, [human.currentLoad]);
|
}, [human.currentLoad]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasLoad || !meshRef.current) return;
|
if (!hasLoad || !meshRef.current) {
|
||||||
|
setIsAttached(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D;
|
const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D;
|
||||||
if (!humanModel) return;
|
if (!humanModel) return;
|
||||||
|
|
||||||
|
meshRef.current.visible = false;
|
||||||
|
|
||||||
const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone;
|
const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone;
|
||||||
if (bone) {
|
if (bone) {
|
||||||
|
if (meshRef.current.parent) {
|
||||||
|
meshRef.current.parent.remove(meshRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
bone.add(meshRef.current);
|
bone.add(meshRef.current);
|
||||||
|
|
||||||
meshRef.current.position.set(0, 0, 0);
|
meshRef.current.position.set(0, 0, 0);
|
||||||
meshRef.current.rotation.set(0, 0, 0);
|
meshRef.current.rotation.set(0, 0, 0);
|
||||||
meshRef.current.scale.set(1, 1, 1);
|
meshRef.current.scale.set(1, 1, 1);
|
||||||
|
|
||||||
|
meshRef.current.visible = true;
|
||||||
|
setIsAttached(true);
|
||||||
}
|
}
|
||||||
}, [hasLoad, human.modelUuid]);
|
}, [hasLoad, human.modelUuid, scene]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasLoad && human.currentMaterials.length > 0 && (
|
{hasLoad && human.point.action.actionType === 'worker' && human.currentMaterials.length > 0 && (
|
||||||
<MaterialModel
|
<MaterialModel
|
||||||
matRef={meshRef}
|
matRef={meshRef}
|
||||||
materialId={human.currentMaterials[0].materialId || ''}
|
materialId={human.currentMaterials[0].materialId || ''}
|
||||||
materialType={human.currentMaterials[0].materialType || 'Default material'}
|
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 { isPlaying } = usePlayButtonStore();
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||||
const { removeMaterial, setEndTime } = materialStore();
|
const { removeMaterial, setEndTime, setMaterial } = materialStore();
|
||||||
const { getStorageUnitById } = storageUnitStore();
|
const { getStorageUnitById } = storageUnitStore();
|
||||||
const { getArmBotById } = armBotStore();
|
const { getArmBotById } = armBotStore();
|
||||||
const { getConveyorById } = conveyorStore();
|
const { getConveyorById } = conveyorStore();
|
||||||
@@ -41,6 +41,13 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
|||||||
const previousTimeRef = useRef<number | null>(null);
|
const previousTimeRef = useRef<number | null>(null);
|
||||||
const animationFrameIdRef = useRef<number | null>(null);
|
const animationFrameIdRef = useRef<number | null>(null);
|
||||||
const humanAsset = getAssetById(human.modelUuid);
|
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(() => {
|
useEffect(() => {
|
||||||
isPausedRef.current = isPaused;
|
isPausedRef.current = isPaused;
|
||||||
@@ -94,6 +101,16 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
|||||||
cancelAnimationFrame(animationFrameIdRef.current)
|
cancelAnimationFrame(animationFrameIdRef.current)
|
||||||
animationFrameIdRef.current = null
|
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);
|
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||||
if (object && human) {
|
if (object && human) {
|
||||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||||
@@ -103,7 +120,103 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isPlaying) {
|
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') {
|
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
|
||||||
const toPickupPath = computePath(
|
const toPickupPath = computePath(
|
||||||
@@ -145,7 +258,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
|||||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
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);
|
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||||
}
|
}
|
||||||
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
|
} 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 { useParams } from 'react-router-dom';
|
||||||
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
|
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
|
||||||
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.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';
|
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||||
|
|
||||||
function HumanUi() {
|
function HumanUi() {
|
||||||
const { scene: startScene } = useGLTF(startPoint) as any;
|
const { scene: startScene } = useGLTF(startPoint) as any;
|
||||||
const { scene: endScene } = useGLTF(startEnd) as any;
|
const { scene: endScene } = useGLTF(startEnd) as any;
|
||||||
|
const { scene: assemblyScene } = useGLTF(assembly) as any;
|
||||||
const startMarker = useRef<Group>(null);
|
const startMarker = useRef<Group>(null);
|
||||||
const endMarker = useRef<Group>(null);
|
const endMarker = useRef<Group>(null);
|
||||||
|
const assemblyMarker = useRef<Group>(null);
|
||||||
const outerGroup = useRef<Group>(null);
|
const outerGroup = useRef<Group>(null);
|
||||||
const prevMousePos = useRef({ x: 0, y: 0 });
|
const prevMousePos = useRef({ x: 0, y: 0 });
|
||||||
const { controls, raycaster, camera } = useThree();
|
const { controls, raycaster, camera } = useThree();
|
||||||
@@ -28,6 +31,7 @@ function HumanUi() {
|
|||||||
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
|
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||||
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
|
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||||
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 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 [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||||
const { isDragging, setIsDragging } = useIsDragging();
|
const { isDragging, setIsDragging } = useIsDragging();
|
||||||
const { isRotating, setIsRotating } = useIsRotating();
|
const { isRotating, setIsRotating } = useIsRotating();
|
||||||
@@ -44,6 +48,10 @@ function HumanUi() {
|
|||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
|
||||||
|
const selectedHuman = selectedEventSphere ? getHumanById(selectedEventSphere.userData.modelUuid) : null;
|
||||||
|
const actionType = selectedHuman?.point?.action?.actionType || null;
|
||||||
|
const isAssembly = actionType === 'assembly';
|
||||||
|
|
||||||
const updateBackend = (
|
const updateBackend = (
|
||||||
productName: string,
|
productName: string,
|
||||||
productUuid: string,
|
productUuid: string,
|
||||||
@@ -99,6 +107,13 @@ function HumanUi() {
|
|||||||
setEndPosition([0, 1, 0]);
|
setEndPosition([0, 1, 0]);
|
||||||
setEndRotation([0, 0, 0]);
|
setEndRotation([0, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.assemblyPoint?.rotation) {
|
||||||
|
setAssemblyRotation(action.assemblyPoint.rotation);
|
||||||
|
} else {
|
||||||
|
setAssemblyRotation([0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
|
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
|
||||||
|
|
||||||
const handlePointerDown = (
|
const handlePointerDown = (
|
||||||
@@ -106,6 +121,7 @@ function HumanUi() {
|
|||||||
state: "start" | "end",
|
state: "start" | "end",
|
||||||
rotation: "start" | "end"
|
rotation: "start" | "end"
|
||||||
) => {
|
) => {
|
||||||
|
if (isAssembly) return;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const intersection = new Vector3();
|
const intersection = new Vector3();
|
||||||
const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
|
const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
|
||||||
@@ -144,54 +160,68 @@ function HumanUi() {
|
|||||||
setIsDragging(null);
|
setIsDragging(null);
|
||||||
setIsRotating(null);
|
setIsRotating(null);
|
||||||
|
|
||||||
if (selectedEventSphere?.userData.modelUuid && selectedAction.actionId) {
|
if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return;
|
||||||
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
|
|
||||||
|
|
||||||
if (selectedHuman && outerGroup.current && startMarker.current && endMarker.current) {
|
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
|
||||||
const worldPosStart = new Vector3(...startPosition);
|
if (!selectedHuman || !outerGroup.current) return;
|
||||||
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
|
|
||||||
|
|
||||||
const worldPosEnd = new Vector3(...endPosition);
|
const isAssembly = selectedHuman.point?.action?.actionType === 'assembly';
|
||||||
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
|
|
||||||
|
|
||||||
const updatedAction = {
|
let 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(
|
if (isAssembly) {
|
||||||
selectedProduct.productUuid,
|
updatedAction = {
|
||||||
selectedEventSphere.userData.modelUuid,
|
...selectedHuman.point.action,
|
||||||
{
|
assemblyPoint: {
|
||||||
...selectedHuman,
|
rotation: assemblyRotation
|
||||||
point: {
|
},
|
||||||
...selectedHuman.point,
|
};
|
||||||
action: updatedAction
|
} else {
|
||||||
}
|
if (!startMarker.current || !endMarker.current) return;
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event) {
|
const worldPosStart = new Vector3(...startPosition);
|
||||||
updateBackend(
|
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
|
||||||
selectedProduct.productName,
|
|
||||||
selectedProduct.productUuid,
|
const worldPosEnd = new Vector3(...endPosition);
|
||||||
projectId || '',
|
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
|
||||||
event
|
|
||||||
);
|
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(() => {
|
useFrame(() => {
|
||||||
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
|
if (isAssembly || !isDragging || !plane.current || !raycaster || !outerGroup.current) return;
|
||||||
const intersectPoint = new Vector3();
|
const intersectPoint = new Vector3();
|
||||||
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
|
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
|
||||||
if (!intersects) return;
|
if (!intersects) return;
|
||||||
@@ -210,11 +240,17 @@ function HumanUi() {
|
|||||||
const currentPointerX = state.pointer.x;
|
const currentPointerX = state.pointer.x;
|
||||||
const deltaX = currentPointerX - prevMousePos.current.x;
|
const deltaX = currentPointerX - prevMousePos.current.x;
|
||||||
prevMousePos.current.x = currentPointerX;
|
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) {
|
if (marker) {
|
||||||
const rotationSpeed = 10;
|
const rotationSpeed = 10;
|
||||||
marker.rotation.y += deltaX * rotationSpeed;
|
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([
|
setStartRotation([
|
||||||
marker.rotation.x,
|
marker.rotation.x,
|
||||||
marker.rotation.y,
|
marker.rotation.y,
|
||||||
@@ -245,7 +281,7 @@ function HumanUi() {
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("pointerup", handleGlobalPointerUp);
|
window.removeEventListener("pointerup", handleGlobalPointerUp);
|
||||||
};
|
};
|
||||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation]);
|
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyRotation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -255,43 +291,69 @@ function HumanUi() {
|
|||||||
ref={outerGroup}
|
ref={outerGroup}
|
||||||
rotation={[0, Math.PI, 0]}
|
rotation={[0, Math.PI, 0]}
|
||||||
>
|
>
|
||||||
<primitive
|
{isAssembly ? (
|
||||||
name="startMarker"
|
<primitive
|
||||||
object={startScene}
|
ref={assemblyMarker}
|
||||||
ref={startMarker}
|
object={assemblyScene}
|
||||||
position={startPosition}
|
position={[0, 1, 0]}
|
||||||
rotation={startRotation}
|
rotation={assemblyRotation}
|
||||||
onPointerDown={(e: any) => {
|
onPointerDown={(e: any) => {
|
||||||
e.stopPropagation();
|
if (e.object.parent.name === "handle") {
|
||||||
handlePointerDown(e, "start", "start");
|
e.stopPropagation();
|
||||||
}}
|
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
|
||||||
onPointerMissed={() => {
|
prevMousePos.current.x = normalizedX;
|
||||||
(controls as any).enabled = true;
|
setIsRotating("start");
|
||||||
setIsDragging(null);
|
setIsDragging(null);
|
||||||
setIsRotating(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
|
<primitive
|
||||||
name="endMarker"
|
name="endMarker"
|
||||||
object={endScene}
|
object={endScene}
|
||||||
ref={endMarker}
|
ref={endMarker}
|
||||||
position={endPosition}
|
position={endPosition}
|
||||||
rotation={endRotation}
|
rotation={endRotation}
|
||||||
onPointerDown={(e: any) => {
|
onPointerDown={(e: any) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handlePointerDown(e, "end", "end");
|
handlePointerDown(e, "end", "end");
|
||||||
}}
|
}}
|
||||||
onPointerMissed={() => {
|
onPointerMissed={() => {
|
||||||
(controls as any).enabled = true;
|
setIsDragging(null);
|
||||||
setIsDragging(null);
|
setIsRotating(null);
|
||||||
setIsRotating(null);
|
if (controls) (controls as any).enabled = true;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</group>
|
</group>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HumanUi
|
export default HumanUi
|
||||||
@@ -301,7 +301,6 @@ const VehicleUI = () => {
|
|||||||
return selectedVehicleData ? (
|
return selectedVehicleData ? (
|
||||||
<group
|
<group
|
||||||
position={selectedVehicleData.position}
|
position={selectedVehicleData.position}
|
||||||
rotation={selectedVehicleData.rotation}
|
|
||||||
ref={outerGroup}
|
ref={outerGroup}
|
||||||
>
|
>
|
||||||
<group
|
<group
|
||||||
|
|||||||
@@ -299,6 +299,10 @@ export function useTriggerHandler() {
|
|||||||
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
|
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||||
|
|
||||||
|
if (human && human.modelUuid === "cc62adae-7000-447b-b845-6d4910de503a") {
|
||||||
|
console.log(human);
|
||||||
|
}
|
||||||
|
|
||||||
setPreviousLocation(material.materialId, {
|
setPreviousLocation(material.materialId, {
|
||||||
modelUuid: material.current.modelUuid,
|
modelUuid: material.current.modelUuid,
|
||||||
pointUuid: material.current.pointUuid,
|
pointUuid: material.current.pointUuid,
|
||||||
@@ -330,7 +334,9 @@ export function useTriggerHandler() {
|
|||||||
if (vehicle) {
|
if (vehicle) {
|
||||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||||
// Handle current action from vehicle
|
// Handle current action from vehicle
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
@@ -341,7 +347,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addVehicleToMonitor(vehicle.modelUuid,
|
addVehicleToMonitor(vehicle.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -354,7 +362,9 @@ export function useTriggerHandler() {
|
|||||||
if (vehicle) {
|
if (vehicle) {
|
||||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||||
// Handle current action from vehicle
|
// Handle current action from vehicle
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
@@ -365,7 +375,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addVehicleToMonitor(vehicle.modelUuid,
|
addVehicleToMonitor(vehicle.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -382,7 +394,9 @@ export function useTriggerHandler() {
|
|||||||
if (conveyor) {
|
if (conveyor) {
|
||||||
if (!conveyor.isPaused) {
|
if (!conveyor.isPaused) {
|
||||||
// Handle current action from vehicle
|
// Handle current action from vehicle
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
@@ -393,7 +407,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addConveyorToMonitor(conveyor.modelUuid,
|
addConveyorToMonitor(conveyor.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -406,7 +422,9 @@ export function useTriggerHandler() {
|
|||||||
if (conveyor) {
|
if (conveyor) {
|
||||||
if (!conveyor.isPaused) {
|
if (!conveyor.isPaused) {
|
||||||
// Handle current action from vehicle
|
// Handle current action from vehicle
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
@@ -417,7 +435,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addConveyorToMonitor(conveyor.modelUuid,
|
addConveyorToMonitor(conveyor.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -434,7 +454,9 @@ export function useTriggerHandler() {
|
|||||||
if (machine) {
|
if (machine) {
|
||||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -443,7 +465,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addMachineToMonitor(machine.modelUuid,
|
addMachineToMonitor(machine.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -456,7 +480,9 @@ export function useTriggerHandler() {
|
|||||||
if (machine) {
|
if (machine) {
|
||||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -465,7 +491,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
addMachineToMonitor(machine.modelUuid,
|
addMachineToMonitor(machine.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -480,7 +508,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
// Handle current action from arm bot
|
// Handle current action from arm bot
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -489,7 +519,9 @@ export function useTriggerHandler() {
|
|||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
addHumanToMonitor(human.modelUuid,
|
addHumanToMonitor(human.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId)
|
handleAction(action, materialId)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -501,7 +533,9 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
// Handle current action from arm bot
|
// Handle current action from arm bot
|
||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId);
|
handleAction(action, materialId);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -510,7 +544,9 @@ export function useTriggerHandler() {
|
|||||||
setIsPaused(materialId, true);
|
setIsPaused(materialId, true);
|
||||||
addHumanToMonitor(human.modelUuid,
|
addHumanToMonitor(human.modelUuid,
|
||||||
() => {
|
() => {
|
||||||
setIsVisible(materialId, false);
|
if (action.actionType === 'worker') {
|
||||||
|
setIsVisible(materialId, false);
|
||||||
|
}
|
||||||
handleAction(action, materialId)
|
handleAction(action, materialId)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1280,7 +1316,7 @@ export function useTriggerHandler() {
|
|||||||
|
|
||||||
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
||||||
const material = getMaterialById(materialId);
|
const material = getMaterialById(materialId);
|
||||||
if (material) {
|
if (material && action.actionType === 'worker') {
|
||||||
|
|
||||||
setPreviousLocation(material.materialId, {
|
setPreviousLocation(material.materialId, {
|
||||||
modelUuid: material.current.modelUuid,
|
modelUuid: material.current.modelUuid,
|
||||||
@@ -1410,6 +1446,28 @@ export function useTriggerHandler() {
|
|||||||
handleAction(action, material.materialId);
|
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 MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
|
||||||
const meshRef = useRef<any>(null!);
|
const meshRef = useRef<any>(null!);
|
||||||
const [hasLoad, setHasLoad] = useState(false);
|
const [hasLoad, setHasLoad] = useState(false);
|
||||||
|
const [isAttached, setIsAttached] = useState(false);
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const offset = new THREE.Vector3(0, 0.85, 0);
|
const offset = new THREE.Vector3(0, 0.85, 0);
|
||||||
|
|
||||||
useEffect(() => {
|
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]);
|
}, [agvDetail.currentLoad]);
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!hasLoad || !meshRef.current) return;
|
if (!hasLoad || !meshRef.current || isAttached) return;
|
||||||
|
|
||||||
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
|
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
|
||||||
if (agvModel) {
|
if (agvModel && !isAttached) {
|
||||||
const worldPosition = offset.clone().applyMatrix4(agvModel.matrixWorld);
|
if (meshRef.current.parent) {
|
||||||
meshRef.current.position.copy(worldPosition);
|
meshRef.current.parent.remove(meshRef.current);
|
||||||
meshRef.current.rotation.copy(agvModel.rotation);
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasLoad && (
|
{hasLoad && agvDetail.currentMaterials.length > 0 && (
|
||||||
<>
|
<MaterialModel
|
||||||
{agvDetail.currentMaterials.length > 0 &&
|
matRef={meshRef}
|
||||||
<MaterialModel
|
materialId={agvDetail.currentMaterials[0].materialId || ''}
|
||||||
matRef={meshRef}
|
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}
|
||||||
materialId={agvDetail.currentMaterials[0].materialId || ''}
|
visible={isAttached}
|
||||||
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}
|
/>
|
||||||
/>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default MaterialAnimator;
|
||||||
export default MaterialAnimator;
|
|
||||||
@@ -80,7 +80,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||||||
const distances = [];
|
const distances = [];
|
||||||
let accumulatedDistance = 0;
|
let accumulatedDistance = 0;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const rotationSpeed = 1;
|
const rotationSpeed = 0.75;
|
||||||
|
|
||||||
for (let i = 0; i < currentPath.length - 1; i++) {
|
for (let i = 0; i < currentPath.length - 1; i++) {
|
||||||
const start = new THREE.Vector3(...currentPath[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 end = new THREE.Vector3(...currentPath[index + 1]);
|
||||||
const segmentDistance = distances[index];
|
const segmentDistance = distances[index];
|
||||||
|
|
||||||
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
|
||||||
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||||
const currentAngle = object.rotation.y;
|
targetQuaternion.multiply(y180);
|
||||||
|
|
||||||
let angleDifference = targetAngle - currentAngle;
|
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||||
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
|
if (angle < 0.01) {
|
||||||
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
|
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;
|
if (angle < step) {
|
||||||
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
|
object.quaternion.copy(targetQuaternion);
|
||||||
const isAligned = Math.abs(angleDifference) < 0.01;
|
} else {
|
||||||
|
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAligned = angle < 0.01;
|
||||||
|
|
||||||
if (isAligned) {
|
if (isAligned) {
|
||||||
progressRef.current += delta * (speed * agvDetail.speed);
|
progressRef.current += delta * (speed * agvDetail.speed);
|
||||||
@@ -122,17 +130,25 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||||||
|
|
||||||
if (progressRef.current >= totalDistance) {
|
if (progressRef.current >= totalDistance) {
|
||||||
if (restRotation && objectRotation) {
|
if (restRotation && objectRotation) {
|
||||||
const targetEuler = new THREE.Euler(
|
const targetEuler = new THREE.Euler(0, objectRotation.y - agvDetail.point.action.steeringAngle, 0);
|
||||||
objectRotation.x,
|
|
||||||
objectRotation.y - agvDetail.point.action.steeringAngle,
|
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||||
objectRotation.z
|
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||||
);
|
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||||
const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
|
||||||
object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
|
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||||
if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
|
if (angle < 0.01) {
|
||||||
object.quaternion.copy(targetQuaternion);
|
object.quaternion.copy(targetQuaternion);
|
||||||
object.rotation.copy(targetEuler);
|
|
||||||
setRestingRotation(false);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ type ProductsStore = {
|
|||||||
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
||||||
|
|
||||||
// Point-level actions
|
// Point-level actions
|
||||||
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;
|
removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => void;
|
||||||
updatePoint: (
|
updatePoint: (
|
||||||
productUuid: string,
|
productUuid: string,
|
||||||
modelUuid: string,
|
modelUuid: string,
|
||||||
pointUuid: string,
|
pointUuid: string,
|
||||||
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema>
|
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema>
|
||||||
) => EventsSchema | undefined;
|
) => EventsSchema | undefined;
|
||||||
|
|
||||||
// Action-level actions
|
// Action-level actions
|
||||||
@@ -65,9 +65,9 @@ type ProductsStore = {
|
|||||||
getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
|
getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
|
||||||
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
|
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
|
||||||
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
|
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
|
||||||
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | 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']) | 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']) | 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;
|
getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||||
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
|
||||||
getPointUuidByActionUuid: (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 {
|
interface HumanAction {
|
||||||
actionUuid: string;
|
actionUuid: string;
|
||||||
actionName: 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; }
|
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||||
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||||
loadCapacity: number;
|
loadCapacity: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user