Merge branch 'dev-simulation/human' into main-dev
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/src/assets/gltf-glb/ui/human-ui-assembly.glb
Normal file
BIN
app/src/assets/gltf-glb/ui/human-ui-assembly.glb
Normal file
Binary file not shown.
BIN
app/src/assets/gltf-glb/ui/human-ui-green.glb
Normal file
BIN
app/src/assets/gltf-glb/ui/human-ui-green.glb
Normal file
Binary file not shown.
BIN
app/src/assets/gltf-glb/ui/human-ui-orange.glb
Normal file
BIN
app/src/assets/gltf-glb/ui/human-ui-orange.glb
Normal file
Binary file not shown.
@@ -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);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsse
|
|||||||
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
||||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||||
import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes";
|
import { FloorItems, RefMesh } from "../../../types/world/worldTypes";
|
||||||
import Models from "./models/models";
|
import Models from "./models/models";
|
||||||
import useModuleStore from "../../../store/useModuleStore";
|
import useModuleStore from "../../../store/useModuleStore";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { useSceneContext } from '../../../../scene/sceneContext';
|
|||||||
import { useVersionContext } from '../../../version/versionContext';
|
import { useVersionContext } from '../../../version/versionContext';
|
||||||
import { SkeletonUtils } from 'three-stdlib';
|
import { SkeletonUtils } from 'three-stdlib';
|
||||||
import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore';
|
import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore';
|
||||||
|
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||||
|
|
||||||
function Model({ asset }: { readonly asset: Asset }) {
|
function Model({ asset }: { readonly asset: Asset }) {
|
||||||
const { camera, controls, gl } = useThree();
|
const { camera, controls, gl } = useThree();
|
||||||
@@ -55,9 +56,24 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const blendFactor = useRef(0);
|
const blendFactor = useRef(0);
|
||||||
const blendDuration = 0.5;
|
const blendDuration = 0.5;
|
||||||
|
|
||||||
|
const updateBackend = (
|
||||||
|
productName: string,
|
||||||
|
productUuid: string,
|
||||||
|
projectId: string,
|
||||||
|
eventData: EventsSchema
|
||||||
|
) => {
|
||||||
|
upsertProductOrEventApi({
|
||||||
|
productName: productName,
|
||||||
|
productUuid: productUuid,
|
||||||
|
projectId: projectId,
|
||||||
|
eventDatas: eventData,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeletableFloorItem(null);
|
setDeletableFloorItem(null);
|
||||||
if (selectedFloorItem === null) {
|
if (selectedFloorItem === null || selectedFloorItem.modelUuid !== asset.modelUuid) {
|
||||||
resetAnimation(asset.modelUuid);
|
resetAnimation(asset.modelUuid);
|
||||||
}
|
}
|
||||||
}, [activeModule, toolMode, selectedFloorItem])
|
}, [activeModule, toolMode, selectedFloorItem])
|
||||||
@@ -217,7 +233,16 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const response = socket.emit('v1:model-asset:delete', data)
|
const response = socket.emit('v1:model-asset:delete', data)
|
||||||
|
|
||||||
eventStore.getState().removeEvent(asset.modelUuid);
|
eventStore.getState().removeEvent(asset.modelUuid);
|
||||||
productStore.getState().deleteEvent(asset.modelUuid);
|
const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid);
|
||||||
|
|
||||||
|
updatedEvents.forEach((event) => {
|
||||||
|
updateBackend(
|
||||||
|
selectedProduct.productName,
|
||||||
|
selectedProduct.productUuid,
|
||||||
|
projectId || '',
|
||||||
|
event
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|
||||||
@@ -291,7 +316,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
|
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
if (mixerRef.current) {
|
if (mixerRef.current) {
|
||||||
mixerRef.current.update(delta * speed);
|
mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
|
import { SkeletonUtils } from "three-stdlib";
|
||||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
|
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
|
||||||
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
|
||||||
import * as Types from "../../../../types/world/worldTypes";
|
import * as Types from "../../../../types/world/worldTypes";
|
||||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
@@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData";
|
|||||||
import { useSceneContext } from "../../sceneContext";
|
import { useSceneContext } from "../../sceneContext";
|
||||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||||
|
|
||||||
|
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||||
|
|
||||||
const CopyPasteControls = ({
|
const CopyPasteControls = ({
|
||||||
copiedObjects,
|
copiedObjects,
|
||||||
setCopiedObjects,
|
setCopiedObjects,
|
||||||
@@ -109,7 +111,7 @@ const CopyPasteControls = ({
|
|||||||
const copySelection = () => {
|
const copySelection = () => {
|
||||||
if (selectedAssets.length > 0) {
|
if (selectedAssets.length > 0) {
|
||||||
const newClones = selectedAssets.map((asset: any) => {
|
const newClones = selectedAssets.map((asset: any) => {
|
||||||
const clone = asset.clone();
|
const clone = SkeletonUtils.clone(asset);
|
||||||
clone.position.copy(asset.position);
|
clone.position.copy(asset.position);
|
||||||
return clone;
|
return clone;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
|
import { SkeletonUtils } from "three-stdlib";
|
||||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
|
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store";
|
||||||
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
|
||||||
import * as Types from "../../../../types/world/worldTypes";
|
import * as Types from "../../../../types/world/worldTypes";
|
||||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
@@ -10,6 +10,8 @@ import { getUserData } from "../../../../functions/getUserData";
|
|||||||
import { useSceneContext } from "../../sceneContext";
|
import { useSceneContext } from "../../sceneContext";
|
||||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||||
|
|
||||||
|
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||||
|
|
||||||
const DuplicationControls = ({
|
const DuplicationControls = ({
|
||||||
duplicatedObjects,
|
duplicatedObjects,
|
||||||
setDuplicatedObjects,
|
setDuplicatedObjects,
|
||||||
@@ -104,7 +106,7 @@ const DuplicationControls = ({
|
|||||||
const duplicateSelection = () => {
|
const duplicateSelection = () => {
|
||||||
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
|
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
|
||||||
const newClones = selectedAssets.map((asset: any) => {
|
const newClones = selectedAssets.map((asset: any) => {
|
||||||
const clone = asset.clone();
|
const clone = SkeletonUtils.clone(asset);
|
||||||
clone.position.copy(asset.position);
|
clone.position.copy(asset.position);
|
||||||
return clone;
|
return clone;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import { useParams } from "react-router-dom";
|
|||||||
import { getUserData } from "../../../../functions/getUserData";
|
import { getUserData } from "../../../../functions/getUserData";
|
||||||
import { useSceneContext } from "../../sceneContext";
|
import { useSceneContext } from "../../sceneContext";
|
||||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||||
|
import { useProductContext } from "../../../simulation/products/productContext";
|
||||||
|
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||||
|
|
||||||
const SelectionControls: React.FC = () => {
|
const SelectionControls: React.FC = () => {
|
||||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||||
@@ -37,6 +39,8 @@ const SelectionControls: React.FC = () => {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
|
const { selectedProductStore } = useProductContext();
|
||||||
|
const { selectedProduct } = selectedProductStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
|
||||||
const isDragging = useRef(false);
|
const isDragging = useRef(false);
|
||||||
@@ -48,6 +52,21 @@ const SelectionControls: React.FC = () => {
|
|||||||
const isShiftSelecting = useRef(false);
|
const isShiftSelecting = useRef(false);
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
|
const updateBackend = (
|
||||||
|
productName: string,
|
||||||
|
productUuid: string,
|
||||||
|
projectId: string,
|
||||||
|
eventData: EventsSchema
|
||||||
|
) => {
|
||||||
|
upsertProductOrEventApi({
|
||||||
|
productName: productName,
|
||||||
|
productUuid: productUuid,
|
||||||
|
projectId: projectId,
|
||||||
|
eventDatas: eventData,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!camera || !scene || toggleView) return;
|
if (!camera || !scene || toggleView) return;
|
||||||
|
|
||||||
@@ -284,7 +303,16 @@ const SelectionControls: React.FC = () => {
|
|||||||
const response = socket.emit("v1:model-asset:delete", data);
|
const response = socket.emit("v1:model-asset:delete", data);
|
||||||
|
|
||||||
eventStore.getState().removeEvent(selectedMesh.uuid);
|
eventStore.getState().removeEvent(selectedMesh.uuid);
|
||||||
productStore.getState().deleteEvent(selectedMesh.uuid);
|
const updatedEvents = productStore.getState().deleteEvent(selectedMesh.uuid);
|
||||||
|
|
||||||
|
updatedEvents.forEach((event) => {
|
||||||
|
updateBackend(
|
||||||
|
selectedProduct.productName,
|
||||||
|
selectedProduct.productUuid,
|
||||||
|
projectId || '',
|
||||||
|
event
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
|
|||||||
const distances = [];
|
const distances = [];
|
||||||
let accumulatedDistance = 0;
|
let accumulatedDistance = 0;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const rotationSpeed = 1;
|
const rotationSpeed = 1.5;
|
||||||
|
|
||||||
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]);
|
||||||
@@ -108,7 +108,14 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
|
|||||||
if (angle < 0.01) {
|
if (angle < 0.01) {
|
||||||
object.quaternion.copy(targetQuaternion);
|
object.quaternion.copy(targetQuaternion);
|
||||||
} else {
|
} else {
|
||||||
object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 5);
|
const step = rotationSpeed * delta * speed * human.speed;
|
||||||
|
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||||
|
|
||||||
|
if (angle < step) {
|
||||||
|
object.quaternion.copy(targetQuaternion);
|
||||||
|
} else {
|
||||||
|
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAligned = angle < 0.01;
|
const isAligned = angle < 0.01;
|
||||||
@@ -145,7 +152,14 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
|
|||||||
object.quaternion.copy(targetQuaternion);
|
object.quaternion.copy(targetQuaternion);
|
||||||
setRestingRotation(false);
|
setRestingRotation(false);
|
||||||
} else {
|
} else {
|
||||||
object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 4);
|
const step = rotationSpeed * delta * speed * human.speed;
|
||||||
|
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||||
|
|
||||||
|
if (angle < step) {
|
||||||
|
object.quaternion.copy(targetQuaternion);
|
||||||
|
} else {
|
||||||
|
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (human.currentMaterials.length > 0) {
|
if (human.currentMaterials.length > 0) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -253,6 +366,9 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
|||||||
|
|
||||||
function startUnloadingProcess() {
|
function startUnloadingProcess() {
|
||||||
const humanAsset = getAssetById(human.modelUuid);
|
const humanAsset = getAssetById(human.modelUuid);
|
||||||
|
if (humanAsset?.animationState?.current !== 'drop') {
|
||||||
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||||
|
}
|
||||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||||
if (human.point.action.triggers.length > 0) {
|
if (human.point.action.triggers.length > 0) {
|
||||||
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
|
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
|
||||||
|
|||||||
@@ -4,21 +4,24 @@ import { useFrame, useThree } from '@react-three/fiber';
|
|||||||
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
|
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
|
||||||
import { useProductContext } from '../../../products/productContext';
|
import { useProductContext } from '../../../products/productContext';
|
||||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||||
import { Group, Plane, Vector3 } from 'three';
|
import { Group, Plane, Vector2, Vector3 } from 'three';
|
||||||
import { useVersionContext } from '../../../../builder/version/versionContext';
|
import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import startPoint from "../../../../../assets/gltf-glb/ui/arrow_green.glb";
|
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
|
||||||
import startEnd from "../../../../../assets/gltf-glb/ui/arrow_red.glb";
|
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
|
||||||
|
import 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 } = useThree();
|
const { controls, raycaster, camera } = useThree();
|
||||||
const { selectedEventSphere } = useSelectedEventSphere();
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
const { humanStore, productStore } = useSceneContext();
|
const { humanStore, productStore } = useSceneContext();
|
||||||
@@ -27,11 +30,13 @@ function HumanUi() {
|
|||||||
const { updateEvent } = productStore();
|
const { updateEvent } = productStore();
|
||||||
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
|
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||||
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
|
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||||
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]);
|
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]);
|
||||||
|
const [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();
|
||||||
const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
|
const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
|
||||||
|
const dragOffset = useRef(new Vector3());
|
||||||
|
|
||||||
const [selectedHumanData, setSelectedHumanData] = useState<{
|
const [selectedHumanData, setSelectedHumanData] = useState<{
|
||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
@@ -43,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,
|
||||||
@@ -69,21 +78,42 @@ function HumanUi() {
|
|||||||
rotation: selectedHuman.rotation,
|
rotation: selectedHuman.rotation,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (outerGroup.current) {
|
||||||
|
outerGroup.current.position.set(
|
||||||
|
selectedHuman.position[0],
|
||||||
|
selectedHuman.position[1],
|
||||||
|
selectedHuman.position[2]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const action = selectedHuman.point.action;
|
const action = selectedHuman.point.action;
|
||||||
|
|
||||||
if (action.pickUpPoint?.position && outerGroup.current) {
|
if (action.pickUpPoint?.position && outerGroup.current) {
|
||||||
const worldPos = new Vector3(...action.pickUpPoint.position);
|
const worldPos = new Vector3(...action.pickUpPoint.position);
|
||||||
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
||||||
setStartPosition([localPosition.x, 0.5, localPosition.z]);
|
setStartPosition([localPosition.x, 1, localPosition.z]);
|
||||||
setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
|
setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
|
||||||
|
} else {
|
||||||
|
setStartPosition([0, 1, 0]);
|
||||||
|
setStartRotation([0, Math.PI, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.dropPoint?.position && outerGroup.current) {
|
if (action.dropPoint?.position && outerGroup.current) {
|
||||||
const worldPos = new Vector3(...action.dropPoint.position);
|
const worldPos = new Vector3(...action.dropPoint.position);
|
||||||
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
||||||
setEndPosition([localPosition.x, 0.5, localPosition.z]);
|
setEndPosition([localPosition.x, 1, localPosition.z]);
|
||||||
setEndRotation(action.dropPoint.rotation || [0, 0, 0]);
|
setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]);
|
||||||
|
} else {
|
||||||
|
setEndPosition([0, 1, 0]);
|
||||||
|
setEndRotation([0, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.assemblyPoint?.rotation) {
|
||||||
|
setAssemblyRotation(action.assemblyPoint.rotation);
|
||||||
|
} else {
|
||||||
|
setAssemblyRotation([0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
|
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
|
||||||
|
|
||||||
const handlePointerDown = (
|
const handlePointerDown = (
|
||||||
@@ -91,84 +121,117 @@ function HumanUi() {
|
|||||||
state: "start" | "end",
|
state: "start" | "end",
|
||||||
rotation: "start" | "end"
|
rotation: "start" | "end"
|
||||||
) => {
|
) => {
|
||||||
if (e.object.name === "handle") {
|
if (isAssembly) return;
|
||||||
|
e.stopPropagation();
|
||||||
|
const intersection = new Vector3();
|
||||||
|
const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
|
||||||
|
raycaster.setFromCamera(pointer, camera);
|
||||||
|
const intersects = raycaster.ray.intersectPlane(plane.current, intersection);
|
||||||
|
|
||||||
|
if (e.object.parent.name === "handle") {
|
||||||
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
|
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
|
||||||
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
|
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||||
prevMousePos.current = { x: normalizedX, y: normalizedY };
|
prevMousePos.current = { x: normalizedX, y: normalizedY };
|
||||||
setIsRotating(rotation);
|
setIsRotating(rotation);
|
||||||
if (controls) (controls as any).enabled = false;
|
|
||||||
setIsDragging(null);
|
setIsDragging(null);
|
||||||
} else {
|
} else {
|
||||||
setIsDragging(state);
|
setIsDragging(state);
|
||||||
setIsRotating(null);
|
setIsRotating(null);
|
||||||
if (controls) (controls as any).enabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (intersects) {
|
||||||
|
let localPoint: Vector3 | null = null;
|
||||||
|
if (outerGroup.current) {
|
||||||
|
localPoint = outerGroup.current.worldToLocal(intersection.clone());
|
||||||
|
}
|
||||||
|
const marker = state === "start" ? startMarker.current : endMarker.current;
|
||||||
|
if (marker && localPoint) {
|
||||||
|
const markerPos = new Vector3().copy(marker.position);
|
||||||
|
dragOffset.current.copy(markerPos.sub(localPoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controls) (controls as any).enabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handlePointerUp = () => {
|
const handlePointerUp = () => {
|
||||||
(controls as any).enabled = true;
|
(controls as any).enabled = true;
|
||||||
setIsDragging(null);
|
setIsDragging(null);
|
||||||
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(
|
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
|
||||||
plane.current,
|
|
||||||
intersectPoint
|
|
||||||
);
|
|
||||||
if (!intersects) return;
|
if (!intersects) return;
|
||||||
const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone());
|
|
||||||
|
const localPoint = outerGroup.current.worldToLocal(intersectPoint.clone()).add(dragOffset.current);
|
||||||
|
|
||||||
if (isDragging === "start") {
|
if (isDragging === "start") {
|
||||||
setStartPosition([localPoint.x, 0.5, localPoint.z]);
|
setStartPosition([localPoint.x, 1, localPoint.z]);
|
||||||
} else if (isDragging === "end") {
|
} else if (isDragging === "end") {
|
||||||
setEndPosition([localPoint.x, 0.5, localPoint.z]);
|
setEndPosition([localPoint.x, 1, localPoint.z]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -177,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,
|
||||||
@@ -212,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 (
|
||||||
<>
|
<>
|
||||||
@@ -220,44 +289,71 @@ function HumanUi() {
|
|||||||
<group
|
<group
|
||||||
position={selectedHumanData.position}
|
position={selectedHumanData.position}
|
||||||
ref={outerGroup}
|
ref={outerGroup}
|
||||||
|
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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useFrame, useThree } from '@react-three/fiber';
|
import { useFrame } from '@react-three/fiber';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { Line, Text } from '@react-three/drei';
|
import { Line, Text } from '@react-three/drei';
|
||||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
@@ -10,7 +10,6 @@ type PointWithDegree = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) {
|
function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) {
|
||||||
const { scene } = useThree();
|
|
||||||
const progressRef = useRef(0);
|
const progressRef = useRef(0);
|
||||||
const curveRef = useRef<THREE.Vector3[] | null>(null);
|
const curveRef = useRef<THREE.Vector3[] | null>(null);
|
||||||
const totalDistanceRef = useRef(0);
|
const totalDistanceRef = useRef(0);
|
||||||
@@ -202,11 +201,7 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone
|
|||||||
}, [circlePoints, currentPath]);
|
}, [circlePoints, currentPath]);
|
||||||
|
|
||||||
// Frame update for animation
|
// Frame update for animation
|
||||||
useFrame((state, delta) => {
|
useFrame((_, delta) => {
|
||||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
|
||||||
if (targetMesh) {
|
|
||||||
targetMesh.visible = (!isPlaying)
|
|
||||||
}
|
|
||||||
if (!ikSolver) return;
|
if (!ikSolver) return;
|
||||||
|
|
||||||
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useThree } from "@react-three/fiber";
|
|
||||||
import IKInstance from '../ikInstance/ikInstance';
|
import IKInstance from '../ikInstance/ikInstance';
|
||||||
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
||||||
import MaterialAnimator from '../animator/materialAnimator';
|
import MaterialAnimator from '../animator/materialAnimator';
|
||||||
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
|
||||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||||
@@ -14,17 +12,16 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
|
|||||||
const [currentPhase, setCurrentPhase] = useState<(string)>("init");
|
const [currentPhase, setCurrentPhase] = useState<(string)>("init");
|
||||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||||
const [ikSolver, setIkSolver] = useState<any>(null);
|
const [ikSolver, setIkSolver] = useState<any>(null);
|
||||||
const { scene } = useThree();
|
|
||||||
const restPosition = new THREE.Vector3(0, 1.75, -1.6);
|
const restPosition = new THREE.Vector3(0, 1.75, -1.6);
|
||||||
const targetBone = "Target";
|
const targetBone = "Target";
|
||||||
const groupRef = useRef<any>(null);
|
|
||||||
const pauseTimeRef = useRef<number | null>(null);
|
const pauseTimeRef = useRef<number | null>(null);
|
||||||
const isPausedRef = useRef<boolean>(false);
|
const isPausedRef = useRef<boolean>(false);
|
||||||
const isSpeedRef = useRef<any>(null);
|
const isSpeedRef = useRef<any>(null);
|
||||||
let startTime: number;
|
let startTime: number;
|
||||||
|
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
const { materialStore, armBotStore, vehicleStore, storageUnitStore, productStore } = useSceneContext();
|
const { materialStore, armBotStore, vehicleStore, storageUnitStore, productStore, assetStore } = useSceneContext();
|
||||||
|
const { resetAsset } = assetStore();
|
||||||
const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = armBotStore();
|
const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = armBotStore();
|
||||||
const { decrementVehicleLoad, removeLastMaterial } = vehicleStore();
|
const { decrementVehicleLoad, removeLastMaterial } = vehicleStore();
|
||||||
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = storageUnitStore();
|
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = storageUnitStore();
|
||||||
@@ -190,7 +187,8 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
|
|||||||
setCurrentPhase("init");
|
setCurrentPhase("init");
|
||||||
setPath([])
|
setPath([])
|
||||||
setIkSolver(null);
|
setIkSolver(null);
|
||||||
removeCurrentAction(armBot.modelUuid)
|
removeCurrentAction(armBot.modelUuid);
|
||||||
|
resetAsset(armBot.modelUuid);
|
||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
startTime = 0
|
startTime = 0
|
||||||
@@ -266,14 +264,9 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}, [armBot.isActive, armBot.state, currentPhase])
|
}, [armBot, currentPhase, isPlaying])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
|
||||||
if (targetMesh) {
|
|
||||||
targetMesh.visible = (!isPlaying)
|
|
||||||
}
|
|
||||||
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
||||||
if (!isReset && isPlaying) {
|
if (!isReset && isPlaying) {
|
||||||
//Moving armBot from initial point to rest position.
|
//Moving armBot from initial point to rest position.
|
||||||
@@ -390,7 +383,7 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
|
|||||||
<>
|
<>
|
||||||
{!isReset && isPlaying && (
|
{!isReset && isPlaying && (
|
||||||
<>
|
<>
|
||||||
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} armBot={armBot} groupRef={groupRef} />
|
<IKInstance setIkSolver={setIkSolver} armBot={armBot} />
|
||||||
<RoboticArmAnimator
|
<RoboticArmAnimator
|
||||||
HandleCallback={HandleCallback}
|
HandleCallback={HandleCallback}
|
||||||
restPosition={restPosition}
|
restPosition={restPosition}
|
||||||
|
|||||||
@@ -1,83 +1,84 @@
|
|||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect } from 'react'
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
||||||
import { clone } from "three/examples/jsm/utils/SkeletonUtils";
|
|
||||||
import { useLoader } from "@react-three/fiber";
|
|
||||||
import { CCDIKSolver, CCDIKHelper } from "three/examples/jsm/animation/CCDIKSolver";
|
import { CCDIKSolver, CCDIKHelper } from "three/examples/jsm/animation/CCDIKSolver";
|
||||||
|
import { usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
|
|
||||||
type IKInstanceProps = {
|
type IKInstanceProps = {
|
||||||
modelUrl: string;
|
|
||||||
setIkSolver: any
|
setIkSolver: any
|
||||||
armBot: ArmBotStatus;
|
armBot: ArmBotStatus;
|
||||||
groupRef: any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function IKInstance({ modelUrl, setIkSolver, armBot, groupRef }: IKInstanceProps) {
|
function IKInstance({ setIkSolver, armBot }: IKInstanceProps) {
|
||||||
const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
|
const { scene } = useThree();
|
||||||
const draco = new DRACOLoader();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/");
|
const { isReset } = useResetButtonStore();
|
||||||
loader.setDRACOLoader(draco);
|
|
||||||
});
|
|
||||||
const cloned = useMemo(() => clone(gltf?.scene), [gltf]);
|
|
||||||
const targetBoneName = "Target";
|
const targetBoneName = "Target";
|
||||||
const skinnedMeshName = "link_0";
|
const skinnedMeshName = "link_0";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gltf) return;
|
let retryId: NodeJS.Timeout | null = null;
|
||||||
const OOI: any = {};
|
|
||||||
cloned.traverse((n: any) => {
|
|
||||||
if (n.name === targetBoneName) OOI.Target_Bone = n;
|
|
||||||
if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
|
|
||||||
});
|
|
||||||
if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
|
|
||||||
const iks = [
|
|
||||||
{
|
|
||||||
target: 7,
|
|
||||||
effector: 6,
|
|
||||||
links: [
|
|
||||||
{
|
|
||||||
index: 5,
|
|
||||||
enabled: true,
|
|
||||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
|
||||||
rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 4,
|
|
||||||
enabled: true,
|
|
||||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
|
||||||
rotationMax: new THREE.Vector3(0, 0, 0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 3,
|
|
||||||
enabled: true,
|
|
||||||
rotationMin: new THREE.Vector3(0, 0, 0),
|
|
||||||
rotationMax: new THREE.Vector3(2, 0, 0),
|
|
||||||
},
|
|
||||||
{ index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
|
|
||||||
{ index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
|
const trySetup = () => {
|
||||||
setIkSolver(solver);
|
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
||||||
|
|
||||||
// const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05)
|
if (!targetMesh) {
|
||||||
|
retryId = setTimeout(trySetup, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// scene.add(helper);
|
const OOI: any = {};
|
||||||
|
targetMesh.traverse((n: any) => {
|
||||||
|
if (n.name === targetBoneName) OOI.Target_Bone = n;
|
||||||
|
if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
|
||||||
|
});
|
||||||
|
if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
|
||||||
|
const iks = [
|
||||||
|
{
|
||||||
|
target: 7,
|
||||||
|
effector: 6,
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
index: 5,
|
||||||
|
enabled: true,
|
||||||
|
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||||
|
rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
enabled: true,
|
||||||
|
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||||
|
rotationMax: new THREE.Vector3(0, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
enabled: true,
|
||||||
|
rotationMin: new THREE.Vector3(0, 0, 0),
|
||||||
|
rotationMax: new THREE.Vector3(2, 0, 0),
|
||||||
|
},
|
||||||
|
{ index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
|
||||||
|
{ index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
}, [cloned, gltf]);
|
const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
|
||||||
|
setIkSolver(solver);
|
||||||
|
|
||||||
|
// const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05)
|
||||||
|
// scene.add(helper);
|
||||||
|
};
|
||||||
|
|
||||||
|
trySetup();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (retryId) clearTimeout(retryId);
|
||||||
|
};
|
||||||
|
}, [isPlaying, isReset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group ref={groupRef} position={armBot.position} rotation={armBot.rotation}>
|
<>
|
||||||
<primitive
|
</>
|
||||||
uuid={`${armBot.modelUuid}_IK`}
|
|
||||||
object={cloned}
|
|
||||||
scale={[1, 1, 1]}
|
|
||||||
name={armBot.modelName}
|
|
||||||
/>
|
|
||||||
</group>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface AssetsStore {
|
|||||||
removeAsset: (modelUuid: string) => void;
|
removeAsset: (modelUuid: string) => void;
|
||||||
updateAsset: (modelUuid: string, updates: Partial<Asset>) => void;
|
updateAsset: (modelUuid: string, updates: Partial<Asset>) => void;
|
||||||
clearAssets: () => void;
|
clearAssets: () => void;
|
||||||
|
resetAsset: (modelUuid: string) => void;
|
||||||
setAssets: (assets: Assets) => void;
|
setAssets: (assets: Assets) => void;
|
||||||
|
|
||||||
// Asset properties
|
// Asset properties
|
||||||
@@ -74,6 +75,19 @@ export const createAssetStore = () => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resetAsset: (modelUuid) => {
|
||||||
|
const asset = get().assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
const clonedAsset = JSON.parse(JSON.stringify(asset));
|
||||||
|
setTimeout(() => {
|
||||||
|
get().removeAsset(asset.modelUuid);
|
||||||
|
setTimeout(() => {
|
||||||
|
get().addAsset(clonedAsset);
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setAssets: (assets) => {
|
setAssets: (assets) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.assets = assets;
|
state.assets = assets;
|
||||||
@@ -185,7 +199,8 @@ export const createAssetStore = () => {
|
|||||||
asset.animationState.current = '';
|
asset.animationState.current = '';
|
||||||
asset.animationState.isPlaying = true;
|
asset.animationState.isPlaying = true;
|
||||||
asset.animationState.loopAnimation = true;
|
asset.animationState.loopAnimation = true;
|
||||||
asset.animationState.isCompleted = true; }
|
asset.animationState.isCompleted = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ type ProductsStore = {
|
|||||||
// Event-level actions
|
// Event-level actions
|
||||||
addEvent: (productUuid: string, event: EventsSchema) => void;
|
addEvent: (productUuid: string, event: EventsSchema) => void;
|
||||||
removeEvent: (productUuid: string, modelUuid: string) => void;
|
removeEvent: (productUuid: string, modelUuid: string) => void;
|
||||||
deleteEvent: (modelUuid: string) => void;
|
deleteEvent: (modelUuid: string) => EventsSchema[];
|
||||||
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
|
||||||
|
|
||||||
// Point-level actions
|
// Point-level actions
|
||||||
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;
|
||||||
@@ -145,11 +145,93 @@ export const createProductStore = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteEvent: (modelUuid) => {
|
deleteEvent: (modelUuid) => {
|
||||||
|
let updatedEvents: EventsSchema[] = [];
|
||||||
set((state) => {
|
set((state) => {
|
||||||
|
const actionsToDelete = new Set<string>();
|
||||||
|
|
||||||
for (const product of state.products) {
|
for (const product of state.products) {
|
||||||
product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
|
const eventIndex = product.eventDatas.findIndex(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
|
if (eventIndex !== -1) {
|
||||||
|
const event = product.eventDatas[eventIndex];
|
||||||
|
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action) {
|
||||||
|
actionsToDelete.add(point.action.actionUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action) {
|
||||||
|
actionsToDelete.add(point.action.actionUuid);
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
actionsToDelete.add(action.actionUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
product.eventDatas.splice(eventIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const product of state.products) {
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
let eventModified = false;
|
||||||
|
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.triggers) {
|
||||||
|
const originalLength = point.action.triggers.length;
|
||||||
|
point.action.triggers = point.action.triggers.filter(trigger => {
|
||||||
|
return !(
|
||||||
|
(trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) ||
|
||||||
|
(actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || ''))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (point.action.triggers.length !== originalLength) {
|
||||||
|
eventModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point && point.action?.triggers) {
|
||||||
|
const originalLength = point.action.triggers.length;
|
||||||
|
point.action.triggers = point.action.triggers.filter((trigger: TriggerSchema) => {
|
||||||
|
return !(
|
||||||
|
(trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) ||
|
||||||
|
(actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || ''))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (point.action.triggers.length !== originalLength) {
|
||||||
|
eventModified = true;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if (action.triggers) {
|
||||||
|
const originalLength = action.triggers.length;
|
||||||
|
action.triggers = action.triggers.filter((trigger: TriggerSchema) => {
|
||||||
|
return !(
|
||||||
|
(trigger.triggeredAsset?.triggeredModel?.modelUuid === modelUuid) ||
|
||||||
|
(actionsToDelete.has(trigger.triggeredAsset?.triggeredAction?.actionUuid || ''))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (action.triggers.length !== originalLength) {
|
||||||
|
eventModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventModified) {
|
||||||
|
updatedEvents.push(JSON.parse(JSON.stringify(event)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return updatedEvents;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateEvent: (productUuid, modelUuid, updates) => {
|
updateEvent: (productUuid, modelUuid, updates) => {
|
||||||
|
|||||||
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