Compare commits

..

12 Commits

Author SHA1 Message Date
0b9f23ba4f tab switch simulation paused implemented 2025-07-17 17:15:53 +05:30
9d3365ae86 bug fix 2025-07-17 14:31:57 +05:30
2fd1cf352c schema changes 2025-07-17 13:07:06 +05:30
1d6d42b358 vehicle to human completed 2025-07-17 12:54:47 +05:30
fe09c3df56 human to conveyor, conveyor to human multiple actions completed 2025-07-17 09:44:08 +05:30
e9053ccd1b human assembly rotation and movement bug fixed 2025-07-14 10:45:24 +05:30
7a72d31008 bug fix in line 2025-07-14 09:48:02 +05:30
99b51df468 Merge remote-tracking branch 'origin/main-dev' into main-demo 2025-07-11 18:18:27 +05:30
a42007ec14 Merge remote-tracking branch 'origin/main-dev' into main-demo 2025-07-11 16:39:38 +05:30
b58cdf45b0 Merge remote-tracking branch 'origin/main-dev' into main-demo 2025-07-11 13:15:26 +05:30
646028f39f feat: add keyboard-based mouse action helper with Zustand integration
- Implemented `mouseActionHelper.ts` to track modifier keys (e.g. CONTROL, SHIFT)
- Dynamically constructs mouse+modifier combos (e.g. left+CONTROL)
- Updates Zustand store (`useMouseNoteStore`) with appropriate notes
- Supports multi-key combinations and cleans up listeners on unmount
2025-07-11 13:06:17 +05:30
24ff130d82 feat: Enhance cursor handling and mouse action notes in the footer and builder components 2025-07-11 12:31:00 +05:30
60 changed files with 1579 additions and 1170 deletions

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 1C11.866 1 15 4.13401 15 8C15 11.866 11.866 15 8 15H2C1.44772 15 1 14.5523 1 14V8C1 4.13401 4.13401 1 8 1Z" fill="black" stroke="white" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -1,4 +1,4 @@
<svg width="12" height="17" viewBox="0 0 12 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 16.8284L4.5 11.5L11.4142 11.4142L0 0V16.8284Z" fill="white"/>
<path d="M1 14.4141V2.41406L9 10.4141L4 10.5L1 14.4141Z" fill="black"/>
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.70849 18.2848L6.01914 13.9573C6.20421 13.7715 6.45487 13.6658 6.71711 13.663L13.3181 13.5936C14.2439 13.5839 14.6604 12.4297 13.9542 11.8309L1.64669 1.39613C0.996961 0.845261 0 1.30706 0 2.15888V17.5791C0 18.4709 1.07909 18.9167 1.70849 18.2848Z" fill="white"/>
<path d="M1.30078 14.5353V5.02997C1.30078 4.17815 2.29774 3.71636 2.94747 4.26722L10.4817 10.655C11.1864 11.2525 10.7733 12.4042 9.84957 12.4177L5.94136 12.4746C5.66374 12.4787 5.40031 12.5979 5.21412 12.8039L3.04259 15.206C2.42871 15.885 1.30078 15.4507 1.30078 14.5353Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 249 B

After

Width:  |  Height:  |  Size: 665 B

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import { HelpIcon } from "../icons/DashboardIcon";
import { useLogger } from "../ui/log/LoggerContext";
import { GetLogIcon } from "./getLogIcons";
@@ -8,10 +8,14 @@ import {
CurserRightIcon,
} from "../icons/LogIcons";
import ShortcutHelper from "./shortcutHelper";
import useVersionHistoryVisibleStore, { useShortcutStore } from "../../store/builder/store";
import useVersionHistoryVisibleStore, {
useShortcutStore,
} from "../../store/builder/store";
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useModuleStore, { useSubModuleStore } from "../../store/useModuleStore";
import { useVersionContext } from "../../modules/builder/version/versionContext";
import { mouseActionHelper } from "../../utils/mouseUtils/mouseHelper";
import { useMouseNoteStore } from "../../store/useUIToggleStore";
const Footer: React.FC = () => {
const { logs, setIsLogListVisible } = useLogger();
@@ -25,28 +29,41 @@ const Footer: React.FC = () => {
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { Leftnote, Middlenote, Rightnote } = useMouseNoteStore();
const mouseButtons = [
{
icon: <CurserLeftIcon />,
label: Leftnote !== "" ? Leftnote : "Pan",
mouse: "left",
},
{
icon: <CurserMiddleIcon />,
label: Middlenote !== "" ? Middlenote : "Scroll Zoom",
mouse: "middle",
},
{
icon: <CurserRightIcon />,
label: Rightnote !== "" ? Rightnote : "Orbit / Cancel action",
mouse: "right",
},
];
useEffect(() => {
const cleanup = mouseActionHelper();
return () => cleanup();
}, []);
return (
<div className="footer-container">
<div className="footer-wrapper">
<div className="selection-wrapper">
<div className="selector-wrapper">
<div className="icon">
<CurserLeftIcon />
{mouseButtons.map(({ icon, label, mouse }) => (
<div className="selector-wrapper" key={mouse}>
<div className="icon">{icon}</div>
<div className="selector">{label}</div>
</div>
<div className="selector">Selection</div>
</div>
<div className="selector-wrapper">
<div className="icon">
<CurserMiddleIcon />
</div>
<div className="selector">Rotate/Zoom</div>
</div>
<div className="selector-wrapper">
<div className="icon">
<CurserRightIcon />
</div>
<div className="selector">Pan/Context Menu</div>
</div>
))}
</div>
<div className="logs-wrapper">
@@ -68,12 +85,15 @@ const Footer: React.FC = () => {
)}
</button>
</div>
<div className="version" onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule('builder');
}}>
{(selectedVersion?.version) ?? 'v 0.0.0'}
<div
className="version"
onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
}}
>
{selectedVersion?.version ?? "v 0.0.0"}
<div className="icon">
<HelpIcon />
</div>
@@ -83,8 +103,9 @@ const Footer: React.FC = () => {
{!isPlaying && (
<div
className={`shortcut-helper-overlay ${showShortcuts ? "visible" : ""
}`}
className={`shortcut-helper-overlay ${
showShortcuts ? "visible" : ""
}`}
>
<ShortcutHelper setShowShortcuts={setShowShortcuts} />
</div>

View File

@@ -1,11 +1,10 @@
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import RenameInput from "../../../ui/inputs/RenameInput";
import Vector3Input from "../customInput/Vector3Input";
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
import {
useEditPosition,
usezonePosition,
useZones,
usezoneTarget,
} from "../../../../store/builder/store";
import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation";
@@ -19,12 +18,11 @@ const ZoneProperties: React.FC = () => {
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zonePosition, setZonePosition } = usezonePosition();
const { zoneTarget, setZoneTarget } = usezoneTarget();
// const { zones, setZones } = useZones();
const { assetStore, zoneStore } = useSceneContext();
const { zoneStore } = useSceneContext();
const { zones, setZoneName } = zoneStore()
const { projectId } = useParams();
const { userName, userId, organization, email } = getUserData();
const { organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();

View File

@@ -13,6 +13,7 @@ interface AssemblyActionProps {
swapOptions: string[];
swapDefaultOption: string;
onSwapSelect: (value: string) => void;
clearPoints: () => void;
}
const AssemblyAction: React.FC<AssemblyActionProps> = ({
@@ -20,6 +21,7 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
swapOptions,
swapDefaultOption,
onSwapSelect,
clearPoints
}) => {
return (
<>
@@ -37,6 +39,19 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
defaultOption={swapDefaultOption}
onSelect={onSwapSelect}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset Points</div>
<button
id="reset-button"
type="button"
className="regularDropdown-container"
onClick={clearPoints}
>
Clear
</button>
</div>
</div>
</>
);
};

View File

@@ -8,18 +8,29 @@ interface WorkerActionProps {
max: number;
step: number;
defaultValue: string;
disabled?: boolean,
disabled?: boolean;
onChange: (value: string) => void;
};
loadCount?: {
value: number;
min: number;
max: number;
step: number;
defaultValue: string,
disabled: false,
onChange: (value: number) => void;
};
clearPoints: () => void;
}
const WorkerAction: React.FC<WorkerActionProps> = ({
loadCapacity,
loadCount,
clearPoints,
}) => {
return (
<>
<div className="worker-action-container">
{/* Load Capacity Input */}
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
@@ -32,23 +43,39 @@ const WorkerAction: React.FC<WorkerActionProps> = ({
onClick={() => { }}
onChange={loadCapacity.onChange}
/>
{/* Load Count Input */}
{loadCount && (
<InputWithDropDown
label="Load Count"
value={loadCount.value.toString()}
min={loadCount.min}
max={loadCount.max}
disabled={loadCount.disabled}
defaultValue={loadCount.defaultValue}
step={loadCount.step}
activeOption="unit"
onClick={() => { }}
onChange={(value) => loadCount.onChange(parseInt(value))}
/>
)}
{/* Clear Points Button */}
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<div className="label">Reset Points</div>
<button
id="rest-button"
id="reset-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
onClick={clearPoints}
>
Clear
</button>
</div>
</div>
</>
</div>
);
};
export default WorkerAction;
export default WorkerAction;

View File

@@ -18,10 +18,17 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
function ConveyorMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "spawn" | "swap" | "delay" | "despawn">("default");
const [speed, setSpeed] = useState("0.5");
const [actionName, setActionName] = useState("Action Name");
const [material, setMaterial] = useState("Default material");
const [spawnCount, setSpawnCount] = useState("1");
const [spawnInterval, setSpawnInterval] = useState("1");
const [delay, setDelay] = useState("0");
const [selectedPointData, setSelectedPointData] = useState<ConveyorPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = productStore();
const { getPointByUuid, updateEvent, updateAction, getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
@@ -36,9 +43,21 @@ function ConveyorMechanics() {
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as ConveyorPointSchema | undefined;
if (point && "action" in point) {
const event = getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid
) as ConveyorEventSchema | undefined;
if (point && "action" in point && event) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as | "default" | "spawn" | "swap" | "delay" | "despawn");
setActiveOption(point.action.actionType);
setActionName(point.action.actionName);
setSpeed(event.speed?.toString() || "0.5");
setMaterial(point.action.material || "Default material");
setSpawnCount(point.action.spawnCount?.toString() || "1");
setSpawnInterval(point.action.spawnInterval?.toString() || "1");
setDelay(point.action.delay?.toString() || "0");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
@@ -53,19 +72,25 @@ function ConveyorMechanics() {
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
})
}
versionId: selectedVersion?.versionId || "",
});
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const event = updateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
{ speed: numericValue }
);
if (event) {
updateBackend(
@@ -75,16 +100,20 @@ function ConveyorMechanics() {
event
);
}
setSpeed(value);
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn";
if (!selectedPointData) return;
const validOption = option as "default" | "spawn" | "swap" | "delay" | "despawn";
setActiveOption(validOption);
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
actionType: validOption,
});
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
if (event) {
updateBackend(
@@ -97,8 +126,14 @@ function ConveyorMechanics() {
};
const handleRenameAction = (newName: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { actionName: newName });
if (!selectedPointData) return;
setActionName(newName);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionName: newName }
);
if (event) {
updateBackend(
@@ -111,10 +146,17 @@ function ConveyorMechanics() {
};
const handleSpawnCountChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
spawnCount: parseFloat(value),
});
if (!selectedPointData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setSpawnCount(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ spawnCount: numericValue }
);
if (event) {
updateBackend(
@@ -127,10 +169,17 @@ function ConveyorMechanics() {
};
const handleSpawnIntervalChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
spawnInterval: parseFloat(value),
});
if (!selectedPointData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setSpawnInterval(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ spawnInterval: numericValue }
);
if (event) {
updateBackend(
@@ -142,9 +191,15 @@ function ConveyorMechanics() {
}
};
const handleMaterialSelect = (material: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { material });
const handleMaterialSelect = (selectedMaterial: string) => {
if (!selectedPointData) return;
setMaterial(selectedMaterial);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ material: selectedMaterial }
);
if (event) {
updateBackend(
@@ -157,10 +212,17 @@ function ConveyorMechanics() {
};
const handleDelayChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
delay: parseFloat(value),
});
if (!selectedPointData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setDelay(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ delay: numericValue }
);
if (event) {
updateBackend(
@@ -177,31 +239,6 @@ function ConveyorMechanics() {
options: ["default", "spawn", "swap", "delay", "despawn"],
};
// Get current values from store
const currentSpeed = (getEventByModelUuid(
selectedProduct.productUuid, selectedEventData?.data.modelUuid || ""
) as ConveyorEventSchema | undefined)?.speed?.toString() || "0.5";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentMaterial = selectedPointData
? selectedPointData.action.material
: "Default material";
const currentSpawnCount = selectedPointData
? selectedPointData.action.spawnCount?.toString() || "1"
: "1";
const currentSpawnInterval = selectedPointData
? selectedPointData.action.spawnInterval?.toString() || "1"
: "1";
const currentDelay = selectedPointData
? selectedPointData.action.delay?.toString() || "0"
: "0";
return (
<>
<div key={selectedPointData?.uuid} className="global-props section">
@@ -209,10 +246,10 @@ function ConveyorMechanics() {
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
value={speed}
min={0}
step={0.1}
defaultValue={"0.5"}
defaultValue="0.5"
max={10}
activeOption="m/s"
onClick={() => { }}
@@ -222,24 +259,18 @@ function ConveyorMechanics() {
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
/>
<ActionsList selectedPointData={selectedPointData} />
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
value={actionName}
canEdit={false}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={
selectedPointData
? selectedPointData.action.actionType
: "default"
}
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
@@ -248,11 +279,11 @@ function ConveyorMechanics() {
<SpawnAction
onChangeCount={handleSpawnCountChange}
options={["Default material", "Material 1", "Material 2", "Material 3"]}
defaultOption={currentMaterial}
defaultOption={material}
onSelect={handleMaterialSelect}
onChangeInterval={handleSpawnIntervalChange}
intervalValue={currentSpawnInterval}
countValue={currentSpawnCount}
intervalValue={spawnInterval}
countValue={spawnCount}
intervalMin={1}
intervalMax={60}
intervalDefaultValue="1"
@@ -264,14 +295,14 @@ function ConveyorMechanics() {
{activeOption === "swap" && (
<SwapAction
options={["Default material", "Material 1", "Material 2", "Material 3"]}
defaultOption={currentMaterial}
defaultOption={material}
onSelect={handleMaterialSelect}
/>
)}
{activeOption === "despawn" && <DespawnAction />}
{activeOption === "delay" && (
<DelayAction
value={currentDelay}
value={delay}
defaultValue="0"
min={0}
max={60}
@@ -288,4 +319,4 @@ function ConveyorMechanics() {
);
}
export default ConveyorMechanics;
export default ConveyorMechanics;

View File

@@ -1,22 +1,25 @@
import { useEffect, useState } from "react";
import { MathUtils } from "three";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import ActionsList from "../components/ActionsList";
import WorkerAction from "../actions/workerAction";
import AssemblyAction from "../actions/assemblyAction";
import { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useParams } from "react-router-dom";
import WorkerAction from "../actions/workerAction";
import AssemblyAction from "../actions/assemblyAction";
function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
const [speed, setSpeed] = useState("0.5");
const [loadCount, setLoadCount] = useState(0);
const [loadCapacity, setLoadCapacity] = useState("1");
const [processTime, setProcessTime] = useState(10);
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
@@ -24,7 +27,7 @@ function HumanMechanics() {
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid } = productStore();
const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid, getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
@@ -40,19 +43,21 @@ function HumanMechanics() {
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
if (point?.actions?.length) {
setSelectedPointData(point);
setCurrentAction(point.action);
setSelectedAction(point.action.actionUuid, point.action.actionName);
const firstAction = point.actions[0];
setCurrentAction(firstAction);
setSpeed((
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as HumanEventSchema | undefined
)?.speed?.toString() || "1");
setLoadCapacity(point.action.loadCapacity.toString());
setProcessTime(point.action.processTime || 10);
setSwappedMaterial(point.action.swapMaterial || "Default material");
setLoadCapacity(firstAction.loadCapacity.toString());
setActiveOption(firstAction.actionType);
setLoadCount(firstAction.loadCount || 0);
setProcessTime(firstAction.processTime || 10);
setSwappedMaterial(firstAction.swapMaterial || "Default material");
}
} else {
clearSelectedAction();
@@ -60,26 +65,36 @@ function HumanMechanics() {
}, [selectedEventData, selectedProduct]);
useEffect(() => {
if (selectedEventData && selectedProduct.productUuid) {
if (selectedEventData && selectedEventData.data.type === "human") {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
setSelectedPointData(point);
setCurrentAction(point.action);
setActiveOption(point.action.actionType);
setSelectedAction(point.action.actionUuid, point.action.actionName);
const actionUuid = selectedAction.actionId || point?.actions[0].actionUuid || '';
const newCurrentAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (newCurrentAction && (newCurrentAction.actionType === 'assembly' || newCurrentAction?.actionType === 'worker')) {
if (!selectedAction.actionId) {
setSelectedAction(newCurrentAction.actionUuid, newCurrentAction.actionName);
}
setCurrentAction(newCurrentAction);
setActiveOption(newCurrentAction.actionType);
setLoadCapacity(newCurrentAction.loadCapacity.toString());
setLoadCount(newCurrentAction.loadCount || 0);
if (newCurrentAction.actionType === 'assembly') {
setProcessTime(newCurrentAction.processTime || 10);
setSwappedMaterial(newCurrentAction.swapMaterial || "Default material");
}
} else {
clearSelectedAction();
setCurrentAction(undefined);
}
} else {
clearSelectedAction();
setCurrentAction(undefined);
setSpeed("0.5");
setLoadCapacity("1");
}
}, [selectedEventData, selectedProduct, selectedAction]);
}, [selectedAction, selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
@@ -99,8 +114,9 @@ function HumanMechanics() {
const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
const updatedAction = { ...currentAction, actionType: actionType as "worker" };
const updatedPoint = { ...selectedPointData, action: updatedAction };
const updatedAction = { ...currentAction, actionType: actionType as "worker" | "assembly" };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
@@ -143,10 +159,9 @@ function HumanMechanics() {
const handleLoadCapacityChange = (value: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.loadCapacity = parseInt(value)
const updatedPoint = { ...selectedPointData, action: updatedAction };
const updatedAction = { ...currentAction, loadCapacity: parseInt(value) };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
@@ -163,13 +178,34 @@ function HumanMechanics() {
setLoadCapacity(value);
};
const handleLoadCountChange = (value: number) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction, loadCount: value };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
setLoadCount(value);
};
const handleProcessTimeChange = (value: number) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.processTime = value
const updatedPoint = { ...selectedPointData, action: updatedAction };
const updatedAction = { ...currentAction, processTime: value };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
@@ -189,10 +225,9 @@ function HumanMechanics() {
const handleMaterialChange = (value: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.swapMaterial = value
const updatedPoint = { ...selectedPointData, action: updatedAction };
const updatedAction = { ...currentAction, swapMaterial: value };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
@@ -212,11 +247,17 @@ function HumanMechanics() {
const handleClearPoints = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
delete updatedAction.pickUpPoint;
delete updatedAction.dropPoint;
const updatedAction: HumanAction = JSON.parse(JSON.stringify(currentAction));
const updatedPoint = { ...selectedPointData, action: updatedAction };
if (updatedAction.actionType === 'assembly') {
updatedAction.assemblyPoint = { position: null, rotation: null, }
} else {
updatedAction.pickUpPoint = { position: null, rotation: null, };
updatedAction.dropPoint = { position: null, rotation: null, }
}
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
@@ -237,12 +278,17 @@ function HumanMechanics() {
const newAction: HumanAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action`,
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "worker",
loadCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: [],
};
const updatedActions = [...(selectedPointData.actions || []), newAction];
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
@@ -254,27 +300,39 @@ function HumanMechanics() {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: newAction };
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = () => {
if (!selectedPointData) return;
if (!selectedPointData || !selectedAction.actionId) return;
const updatedActions = selectedPointData.actions.filter(action => action.actionUuid !== selectedAction.actionId);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = removeAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid
selectedAction.actionId
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: undefined as any };
setSelectedPointData(updatedPoint);
clearSelectedAction();
setCurrentAction(undefined);
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === selectedAction.actionId);
const nextAction = updatedPoint.actions[index] || updatedPoint.actions[index - 1];
if (nextAction) {
setSelectedAction(nextAction.actionUuid, nextAction.actionName);
const action = getActionByUuid(selectedProduct.productUuid, nextAction.actionUuid);
if (action) {
setCurrentAction(action as HumanAction);
}
} else {
clearSelectedAction();
setCurrentAction(undefined);
}
};
return (
@@ -299,7 +357,7 @@ function HumanMechanics() {
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction={false}
multipleAction={true}
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
@@ -315,7 +373,7 @@ function HumanMechanics() {
defaultOption={activeOption}
options={["worker", "assembly"]}
onSelect={handleSelectActionType}
disabled={true}
disabled={false}
/>
</div>
{currentAction.actionType === 'worker' &&
@@ -323,12 +381,21 @@ function HumanMechanics() {
loadCapacity={{
value: loadCapacity,
min: 1,
max: 5,
max: 20,
step: 1,
defaultValue: "10",
defaultValue: "1",
disabled: true,
onChange: handleLoadCapacityChange,
}}
loadCount={{
value: loadCount,
min: 0,
max: 20,
step: 1,
defaultValue: "1",
disabled: false,
onChange: handleLoadCountChange,
}}
clearPoints={handleClearPoints}
/>
}
@@ -343,6 +410,7 @@ function HumanMechanics() {
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
swapDefaultOption={swappedMaterial}
onSwapSelect={handleMaterialChange}
clearPoints={handleClearPoints}
/>
}
<div className="tirgger">

View File

@@ -12,8 +12,12 @@ import { useVersionContext } from "../../../../../../modules/builder/version/ver
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
function MachineMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "process">("default");
const [activeOption, setActiveOption] = useState<"process">("process");
const [actionName, setActionName] = useState("Action Name");
const [processTime, setProcessTime] = useState("1");
const [material, setMaterial] = useState("Default material");
const [selectedPointData, setSelectedPointData] = useState<MachinePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, updateAction } = productStore();
@@ -31,9 +35,13 @@ function MachineMechanics() {
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as MachinePointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "process");
setActionName(point.action.actionName);
setProcessTime(point.action.processTime?.toString() || "1");
setMaterial(point.action.swapMaterial || "Default material");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
@@ -48,22 +56,25 @@ function MachineMechanics() {
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
})
}
versionId: selectedVersion?.versionId || "",
});
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
if (!selectedPointData) return;
const validOption = option as "process";
setActiveOption(validOption);
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
actionType: validOption,
});
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionType: validOption }
);
if (event) {
updateBackend(
@@ -77,7 +88,13 @@ function MachineMechanics() {
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { actionName: newName });
setActionName(newName);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionName: newName }
);
if (event) {
updateBackend(
@@ -91,9 +108,16 @@ function MachineMechanics() {
const handleProcessTimeChange = (value: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
processTime: parseFloat(value),
});
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setProcessTime(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ processTime: numericValue }
);
if (event) {
updateBackend(
@@ -105,11 +129,15 @@ function MachineMechanics() {
}
};
const handleMaterialSelect = (material: string) => {
const handleMaterialSelect = (selectedMaterial: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
swapMaterial: material,
});
setMaterial(selectedMaterial);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ swapMaterial: selectedMaterial }
);
if (event) {
updateBackend(
@@ -121,19 +149,6 @@ function MachineMechanics() {
}
};
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentProcessTime = selectedPointData
? selectedPointData.action.processTime.toString()
: "1";
const currentMaterial = selectedPointData
? selectedPointData.action.swapMaterial
: "Default material";
const availableActions = {
defaultOption: "process",
options: ["process"],
@@ -146,28 +161,26 @@ function MachineMechanics() {
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
value={actionName}
canEdit={false}
/>
</div>
<ActionsList
selectedPointData={selectedPointData}
/>
<ActionsList selectedPointData={selectedPointData} />
<div className="selected-actions-list">
<LabledDropdown
defaultOption="process"
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "process" && (
<ProcessAction
value={currentProcessTime}
value={processTime}
min={0.1}
max={60}
defaultValue="1"
onChange={handleProcessTimeChange}
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
swapDefaultOption={currentMaterial}
swapDefaultOption={material}
onSwapSelect={handleMaterialSelect}
/>
)}
@@ -182,4 +195,4 @@ function MachineMechanics() {
);
}
export default MachineMechanics;
export default MachineMechanics;

View File

@@ -14,11 +14,13 @@ import { useVersionContext } from "../../../../../../modules/builder/version/ver
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
function RoboticArmMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">("default");
const [activeOption, setActiveOption] = useState<"pickAndPlace">("pickAndPlace");
const [speed, setSpeed] = useState("0.5");
const [selectedPointData, setSelectedPointData] = useState<RoboticArmPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction, } = productStore();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
@@ -33,16 +35,20 @@ function RoboticArmMechanics() {
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as RoboticArmPointSchema | undefined;
if (point?.actions) {
setSelectedPointData(point);
setSpeed(
(getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid
) as RoboticArmEventSchema | undefined)?.speed?.toString() || "0.5"
);
if (point.actions.length > 0) {
setActiveOption(
point.actions[0].actionType as "default" | "pickAndPlace"
);
setSelectedAction(
point.actions[0].actionUuid,
point.actions[0].actionName
);
const firstAction = point.actions[0];
setActiveOption(firstAction.actionType);
setSelectedAction(firstAction.actionUuid, firstAction.actionName);
}
}
} else {
@@ -57,33 +63,33 @@ function RoboticArmMechanics() {
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
versionId: selectedVersion?.versionId || "",
});
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
if (!selectedAction.actionId || !selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ actionName: newName }
);
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
}
const updatedActions = selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
if (event) {
updateBackend(
@@ -97,10 +103,15 @@ function RoboticArmMechanics() {
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setSpeed(value);
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
{ speed: parseFloat(value), }
{ speed: numericValue }
);
if (event) {
@@ -112,6 +123,7 @@ function RoboticArmMechanics() {
);
}
};
const handleClearPoints = () => {
if (!selectedAction.actionId || !selectedPointData) return;
@@ -166,15 +178,20 @@ function RoboticArmMechanics() {
);
}
const updatedPoint = { ...selectedPointData, actions: [...selectedPointData.actions, newAction], };
setSelectedPointData(updatedPoint);
setSelectedPointData({
...selectedPointData,
actions: [...selectedPointData.actions, newAction],
});
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
const event = removeAction(selectedProduct.productUuid, actionUuid);
const event = removeAction(
selectedProduct.productUuid,
actionUuid
);
if (event) {
updateBackend(
@@ -185,14 +202,13 @@ function RoboticArmMechanics() {
);
}
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === actionUuid);
const newActions = selectedPointData.actions.filter((a) => a.actionUuid !== actionUuid);
const index = selectedPointData.actions.findIndex(a => a.actionUuid === actionUuid);
const newActions = selectedPointData.actions.filter(a => a.actionUuid !== actionUuid);
const updatedPoint = {
setSelectedPointData({
...selectedPointData,
actions: newActions,
};
setSelectedPointData(updatedPoint);
});
if (selectedAction.actionId === actionUuid) {
const nextAction = newActions[index] || newActions[index - 1];
@@ -209,16 +225,7 @@ function RoboticArmMechanics() {
options: ["pickAndPlace"],
};
const currentSpeed = (getEventByModelUuid(selectedProduct.productUuid, selectedEventData?.data.modelUuid || "") as RoboticArmEventSchema | undefined)?.speed?.toString() || "0.5";
const currentAction = selectedPointData?.actions.find((a) => a.actionUuid === selectedAction.actionId);
const currentPickPoint = currentAction?.process.startPoint
? `${currentAction.process.startPoint[0]},${currentAction.process.startPoint[1]},${currentAction.process.startPoint[2]}`
: "";
const currentPlacePoint = currentAction?.process.endPoint
? `${currentAction.process.endPoint[0]},${currentAction.process.endPoint[1]},${currentAction.process.endPoint[2]}`
: "";
const currentAction = selectedPointData?.actions.find(a => a.actionUuid === selectedAction.actionId);
return (
<>
@@ -227,10 +234,10 @@ function RoboticArmMechanics() {
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
value={speed}
min={0}
step={0.1}
defaultValue={"0.5"}
defaultValue="0.5"
max={10}
activeOption="m/s"
onClick={() => { }}
@@ -252,7 +259,7 @@ function RoboticArmMechanics() {
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName || ""}
onRename={handleRenameAction}
canEdit={false}
/>
</div>
<div className="selected-actions-list">
@@ -277,4 +284,4 @@ function RoboticArmMechanics() {
);
}
export default RoboticArmMechanics;
export default RoboticArmMechanics;

View File

@@ -192,7 +192,7 @@ function StorageMechanics() {
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
canEdit={false}
/>
</div>
<div className="selected-actions-list">

View File

@@ -3,10 +3,7 @@ import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedAction,
useSelectedEventData,
} from "../../../../../../store/simulation/useSimulationStore";
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
import TravelAction from "../actions/TravelAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
@@ -17,18 +14,23 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useSelectedPath } from "../../../../../../store/builder/store";
function VehicleMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
const [activeOption, setActiveOption] = useState<"travel">("travel");
const [speed, setSpeed] = useState("0.5");
const [actionName, setActionName] = useState("Action Name");
const [loadCapacity, setLoadCapacity] = useState("1");
const [unloadDuration, setUnloadDuration] = useState("1");
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = productStore();
const { getPointByUuid, updateEvent, updateAction, getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { setSelectedPath } = useSelectedPath();
useEffect(() => {
if (selectedEventData && selectedEventData.data.type === "vehicle") {
@@ -41,6 +43,15 @@ function VehicleMechanics() {
if (point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "travel");
setActionName(point.action.actionName);
setSpeed(
(getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid
) as VehicleEventSchema | undefined)?.speed?.toString() || "0.5"
);
setLoadCapacity(point.action.loadCapacity?.toString() || "1");
setUnloadDuration(point.action.unLoadDuration?.toString() || "1");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
@@ -55,22 +66,25 @@ function VehicleMechanics() {
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
versionId: selectedVersion?.versionId || "",
});
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setSpeed(value);
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
{
speed: parseFloat(value),
}
{ speed: numericValue }
);
if (event) {
@@ -84,16 +98,15 @@ function VehicleMechanics() {
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
if (!selectedPointData) return;
const validOption = option as "travel";
setActiveOption(validOption);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
actionType: validOption,
}
{ actionType: validOption }
);
if (event) {
@@ -108,6 +121,8 @@ function VehicleMechanics() {
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
setActionName(newName);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
@@ -126,12 +141,15 @@ function VehicleMechanics() {
const handleLoadCapacityChange = (value: string) => {
if (!selectedPointData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setLoadCapacity(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
loadCapacity: parseFloat(value),
}
{ loadCapacity: numericValue }
);
if (event) {
@@ -146,12 +164,15 @@ function VehicleMechanics() {
const handleUnloadDurationChange = (value: string) => {
if (!selectedPointData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
setUnloadDuration(value);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
unLoadDuration: parseFloat(value),
}
{ unLoadDuration: numericValue }
);
if (event) {
@@ -164,46 +185,18 @@ function VehicleMechanics() {
}
};
const handlePickPointChange = (value: string) => {
const handleClearPoints = () => {
if (!selectedPointData) return;
};
const handleUnloadPointChange = (value: string) => {
if (!selectedPointData) return;
};
// Get current values from store
const currentSpeed =
(
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as VehicleEventSchema | undefined
)?.speed?.toString() || "0.5";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentLoadCapacity = selectedPointData
? selectedPointData.action.loadCapacity.toString()
: "1";
const currentUnloadDuration = selectedPointData
? selectedPointData.action.unLoadDuration.toString()
: "1";
function handleClearPoints() {
if (!selectedEventData || !selectedPointData?.action.actionUuid) return;
const event = updateAction(
selectedProduct.productUuid, selectedPointData.action.actionUuid, {
pickUpPoint: null,
unLoadPoint: null,
steeringAngle: 0,
})
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
pickUpPoint: null,
unLoadPoint: null,
steeringAngle: 0,
}
);
if (event) {
updateBackend(
@@ -213,7 +206,7 @@ function VehicleMechanics() {
event
);
}
}
};
const availableActions = {
defaultOption: "travel",
@@ -229,10 +222,10 @@ function VehicleMechanics() {
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
value={speed}
min={0}
step={0.1}
defaultValue={"0.5"}
defaultValue="0.5"
max={10}
activeOption="m/s"
onClick={() => { }}
@@ -246,13 +239,13 @@ function VehicleMechanics() {
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
value={actionName}
canEdit={false}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="travel"
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
@@ -260,14 +253,14 @@ function VehicleMechanics() {
{activeOption === "travel" && (
<TravelAction
loadCapacity={{
value: currentLoadCapacity,
value: loadCapacity,
min: 1,
max: 100,
defaultValue: "1",
onChange: handleLoadCapacityChange,
}}
unloadDuration={{
value: currentUnloadDuration,
value: unloadDuration,
min: 1,
max: 60,
defaultValue: "1",
@@ -284,36 +277,48 @@ function VehicleMechanics() {
type={"Vehicle"}
/>
</div>
<div style={{ display: "flex", gap: "10px", flexDirection: "column", alignItems: "center" }}>
<button style={{
backgroundColor: "#6f42c1",
color: "#f3f3fd",
borderRadius: "15px",
height: "30px",
width: "150px",
border: "none",
cursor: "pointer",
padding: "0 5px",
}} onClick={() => setSelectedPath("auto")}>Auto Generate Path</button>
<button style={{
backgroundColor: "#6f42c1",
color: "#f3f3fd",
borderRadius: "15px",
height: "30px",
width: "150px",
border: "none",
cursor: "pointer",
padding: "0 5px",
}} onClick={() => setSelectedPath("manual")}>Create Path</button>
<div style={{
display: "flex",
gap: "10px",
flexDirection: "column",
alignItems: "center"
}}>
<button
style={{
backgroundColor: "#6f42c1",
color: "#f3f3fd",
borderRadius: "15px",
height: "30px",
width: "150px",
border: "none",
cursor: "pointer",
padding: "0 5px",
}}
onClick={() => setSelectedPath("auto")}
>
Auto Generate Path
</button>
<button
style={{
backgroundColor: "#6f42c1",
color: "#f3f3fd",
borderRadius: "15px",
height: "30px",
width: "150px",
border: "none",
cursor: "pointer",
padding: "0 5px",
}}
onClick={() => setSelectedPath("manual")}
>
Create Path
</button>
</div>
</section>
</>
)
}
)}
</>
);
}
export default VehicleMechanics;
export default VehicleMechanics;

View File

@@ -65,8 +65,11 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
const action = getActionByUuid(selectedProduct.productUuid, currentAction);
const actionTriggers = action?.triggers || [];
setTriggers(actionTriggers);
if (actionTriggers.length === 0) {
setSelectedTrigger(undefined);
}
setSelectedTrigger(actionTriggers[0]);
}, [currentAction, selectedProduct]);
}, [currentAction, selectedProduct, selectedTrigger, selectedPointData]);
const triggeredModel = useMemo(() => {
if (!selectedProduct || !selectedTrigger?.triggeredAsset?.triggeredModel?.modelUuid)

View File

@@ -22,7 +22,6 @@ import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
import {
useActiveTool,
useAddAction,
useRefTextUpdate,
useSelectedWallItem,
useSocketStore,
useToggleView,
@@ -75,7 +74,6 @@ const Tools: React.FC = () => {
const { setActiveSubTool, activeSubTool } = useActiveSubTool();
const { setSelectedWallItem } = useSelectedWallItem();
const { setRefTextUpdate } = useRefTextUpdate();
const { setToggleUI } = useToggleStore();
const { setToggleView, toggleView } = useToggleView();
@@ -131,7 +129,6 @@ const Tools: React.FC = () => {
const resetTools = () => {
setToolMode(null);
setAddAction(null);
setRefTextUpdate((prev) => prev - 1);
};
const updateToolBehavior = (tool: string, is2D: boolean) => {
@@ -406,7 +403,6 @@ const useStoreHooks = () => {
...useActiveTool(),
...useToolMode(),
...useAddAction(),
...useRefTextUpdate(),
};
};

View File

@@ -2,93 +2,89 @@ import React, { useState } from "react";
import RenameInput from "./RenameInput";
type InputWithDropDownProps = {
label: string;
value: string;
min?: number;
max?: number;
step?: number;
defaultValue?: string;
disabled?: boolean;
options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option
onClick?: () => void;
onChange: (newValue: string) => void;
editableLabel?: boolean;
placeholder?: string; // New placeholder prop
label: string;
value: string;
min?: number;
max?: number;
step?: number;
defaultValue?: string;
disabled?: boolean;
options?: string[];
activeOption?: string;
onClick?: () => void;
onChange: (newValue: string) => void;
editableLabel?: boolean;
placeholder?: string;
};
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label,
value,
min,
max,
step,
defaultValue,
disabled = false,
options,
activeOption,
onClick,
onChange,
editableLabel = false,
placeholder = "Inherit", // Default empty placeholder
label,
value,
min,
max,
step,
defaultValue,
disabled = false,
options,
activeOption,
onClick,
onChange,
editableLabel = false,
placeholder = "Inherit",
}) => {
const separatedWords = label
.split(/(?=[A-Z])/)
.map((word) => word.trim())
.toString();
const [openDropdown, setOpenDropdown] = useState(false);
const [openDropdown, setOpenDropdown] = useState(false);
const separatedWords = label
.split(/(?=[A-Z])/)
.map((word) => word.trim())
.join(" ");
return (
<div className="value-field-container">
{editableLabel ? (
<RenameInput value={label} />
) : (
<label htmlFor={separatedWords} className="label">
{label}
</label>
)}
<div className="input default" id={separatedWords}>
<input
min={min}
max={max}
step={step}
type="number"
defaultValue={value}
// value={value}
disabled={disabled}
onChange={(e) => {
onChange(e.target.value);
}}
placeholder={placeholder} // Added placeholder prop
/>
{activeOption && (
<div
className="dropdown"
onClick={() => {
setOpenDropdown(true);
}}
>
<div className="active-option">{activeOption}</div>
{options && openDropdown && (
<div className="dropdown-options-list">
{options.map((option, index) => (
<div
key={index}
className={"dropdown-option"}
onClick={onClick}
>
{option}
</div>
))}
</div>
return (
<div className="value-field-container">
{editableLabel ? (
<RenameInput value={label} />
) : (
<label htmlFor={separatedWords} className="label">
{label}
</label>
)}
</div>
)}
</div>
</div>
);
<div className="input default" id={separatedWords}>
<input
min={min}
max={max}
step={step}
type="number"
value={value} // Controlled input
disabled={disabled}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
/>
{activeOption && (
<div
className="dropdown"
onClick={() => setOpenDropdown((prev) => !prev)}
>
<div className="active-option">{activeOption}</div>
{options && openDropdown && (
<div className="dropdown-options-list">
{options.map((option, index) => (
<div
key={index}
className="dropdown-option"
onClick={onClick}
>
{option}
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
};
export default InputWithDropDown;
export default InputWithDropDown;

View File

@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
import List from "./List";
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
import { useZones } from "../../../store/builder/store";
import { useSceneContext } from "../../../modules/scene/sceneContext";
interface DropDownListProps {
@@ -44,14 +43,12 @@ const DropDownList: React.FC<DropDownListProps> = ({
remove,
}) => {
const [isOpen, setIsOpen] = useState<boolean>(defaultOpen);
// const { zones } = useZones();
const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state
};
const [zoneDataList, setZoneDataList] = useState<ZoneData[]>([]);
// const { assetStore } = useSceneContext();
const { assetStore, zoneStore } = useSceneContext();
const { assets } = assetStore();
const { zones } = zoneStore()

View File

@@ -14,7 +14,6 @@ import {
} from "../../icons/ExportCommonIcons";
import {
useZoneAssetId,
useZones,
} from "../../../store/builder/store";
import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";

View File

@@ -34,7 +34,7 @@ import { useComparisonProduct } from "../../../store/simulation/useSimulationSto
import InputToggle from "../inputs/InputToggle";
const SimulationPlayer: React.FC = () => {
const MAX_SPEED = 4; // Maximum speed
const MAX_SPEED = 8; // Maximum speed
const isDragging = useRef(false);
const sliderRef = useRef<HTMLDivElement>(null);
@@ -109,12 +109,24 @@ const SimulationPlayer: React.FC = () => {
isDragging.current = false;
};
const handleVisibility = () => {
if (document.visibilityState !== 'visible' && isPlaying) {
setIsPaused(!isPaused);
if (isPaused) {
setIsPlaying(true);
}
echo.warn(`Simulation is ${isPaused ? "Resumed" : "Paused"}`);
}
}
useEffect(() => {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener('visibilitychange', handleVisibility);
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
document.removeEventListener('visibilitychange', handleVisibility);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -139,6 +139,11 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
paths: {
initPickup: [],
pickupDrop: [],
dropPickup: [],
},
triggers: []
}
}
@@ -256,14 +261,17 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -262,6 +262,11 @@ async function handleModelLoad(
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
paths: {
initPickup: [],
pickupDrop: [],
dropPickup: [],
},
triggers: [],
},
},
@@ -373,14 +378,17 @@ async function handleModelLoad(
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -15,7 +15,6 @@ import {
useToolMode,
useRenderDistance,
useLimitDistance,
useLoadingProgress,
} from "../../store/builder/store";
////////// 3D Function Imports //////////
@@ -57,7 +56,6 @@ export default function Builder() {
const { projectId } = useParams();
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
const { userId, organization } = getUserData();
const { loadingProgress } = useLoadingProgress();
useEffect(() => {
if (!toggleView) {
@@ -117,7 +115,8 @@ export default function Builder() {
<CalculateAreaGroup />
{loadingProgress == 0 && <NavMesh />}
<NavMesh />
<DxfFile />
<LayoutImage />

View File

@@ -1,9 +1,8 @@
import { useEffect, useRef } from 'react';
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from 'three';
import { TransformControls } from '@react-three/drei';
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
import * as Types from '../../../types/world/worldTypes';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { useVersionContext } from '../version/versionContext';

View File

@@ -9,6 +9,7 @@ import * as Constants from '../../../types/world/worldConstants';
import { useVersionContext } from '../version/versionContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
import { useSelectedPoints } from '../../../store/simulation/useSimulationStore';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
@@ -24,7 +25,7 @@ interface LineProps {
function Line({ points }: Readonly<LineProps>) {
const [isHovered, setIsHovered] = useState(false);
const { raycaster, camera, pointer, gl } = useThree();
const { raycaster, camera, pointer } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const [isDeletable, setIsDeletable] = useState(false);
const { socket } = useSocketStore();
@@ -215,7 +216,7 @@ function Line({ points }: Readonly<LineProps>) {
});
}
}
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
}
}
@@ -226,7 +227,7 @@ function Line({ points }: Readonly<LineProps>) {
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (hit) {
gl.domElement.style.cursor = 'move';
handleCanvasCursors('grabbing');
const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset);
const start = new THREE.Vector3(...points[0].position);
@@ -271,7 +272,7 @@ function Line({ points }: Readonly<LineProps>) {
const handleDragEnd = (points: [Point, Point]) => {
if (toolMode !== 'move' || !dragOffset) return;
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
setDragOffset(null);
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
const updatedWalls1 = getWallsByPointId(points[0].pointUuid);
@@ -375,18 +376,20 @@ function Line({ points }: Readonly<LineProps>) {
handlePointClick(points);
}}
onPointerOver={(e) => {
if (!hoveredLine && selectedPoints.length === 0 && e.buttons === 0 && !e.ctrlKey) {
if (selectedPoints.length === 0 && e.buttons === 0 && !e.ctrlKey) {
setHoveredLine(points);
setIsHovered(true)
if (toolMode === 'move' && !hoveredPoint) {
gl.domElement.style.cursor = 'pointer';
handleCanvasCursors('grab');
}
}
}}
onPointerOut={() => {
if (hoveredLine) {
if (hoveredLine && isHovered) {
setHoveredLine(null);
gl.domElement.style.cursor = 'default';
if (!hoveredPoint) {
handleCanvasCursors('default');
}
}
setIsHovered(false)
}}

View File

@@ -21,10 +21,11 @@ import { useSceneContext } from '../../scene/sceneContext';
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
import { getUserData } from '../../../functions/getUserData';
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
function Point({ point }: { readonly point: Point }) {
const materialRef = useRef<THREE.ShaderMaterial>(null);
const { raycaster, camera, pointer, gl } = useThree();
const { raycaster, camera, pointer } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const [isHovered, setIsHovered] = useState(false);
const [isSelected, setIsSelected] = useState(false);
@@ -37,7 +38,7 @@ function Point({ point }: { readonly point: Point }) {
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
const { hoveredPoint,hoveredLine, setHoveredPoint } = useBuilderStore();
const { selectedPoints } = useSelectedPoints();
const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
@@ -47,7 +48,7 @@ function Point({ point }: { readonly point: Point }) {
const colors = getColor(point);
useEffect(() => {
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
}, [toolMode])
function getColor(point: Point) {
@@ -117,7 +118,7 @@ function Point({ point }: { readonly point: Point }) {
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (hit) {
gl.domElement.style.cursor = 'move';
handleCanvasCursors('grabbing');
const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset);
const newPosition: [number, number, number] = [positionWithOffset.x, positionWithOffset.y, positionWithOffset.z];
@@ -155,7 +156,7 @@ function Point({ point }: { readonly point: Point }) {
};
const handleDragEnd = (point: Point) => {
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
setDragOffset(null);
if (toolMode !== 'move') return;
if (point.pointType === 'Aisle') {
@@ -399,7 +400,7 @@ function Point({ point }: { readonly point: Point }) {
});
}
}
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
}
}
@@ -445,14 +446,16 @@ function Point({ point }: { readonly point: Point }) {
setHoveredPoint(point);
setIsHovered(true);
if (toolMode === 'move') {
gl.domElement.style.cursor = 'pointer';
handleCanvasCursors('grab');
}
}
}}
onPointerOut={() => {
if (hoveredPoint) {
setHoveredPoint(null);
gl.domElement.style.cursor = 'default';
if(!hoveredLine){
handleCanvasCursors('default');
}
}
setIsHovered(false)
}}

View File

@@ -255,6 +255,8 @@ const CamModelsGroup = () => {
setCams((prev) => dedupeCams([...prev, ...newCams]));
});
}
}).catch(() => {
console.log('Error fetching active users data')
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -234,6 +234,11 @@ const CopyPasteControls3D = ({
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
paths: {
initPickup: [],
pickupDrop: [],
dropPickup: [],
},
triggers: []
}
}
@@ -347,13 +352,17 @@ const CopyPasteControls3D = ({
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
loadCount: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -208,6 +208,11 @@ const DuplicationControls3D = ({
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
paths: {
initPickup: [],
pickupDrop: [],
dropPickup: [],
},
triggers: []
}
}
@@ -321,13 +326,17 @@ const DuplicationControls3D = ({
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
loadCount: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -6,7 +6,6 @@ import {
useElevation,
useShadows,
useSunPosition,
useWallItems,
useTileDistance,
} from "../../../store/builder/store";
import * as CONSTANTS from "../../../types/world/worldConstants";
@@ -25,13 +24,12 @@ export default function Shadows() {
const { controls, gl } = useThree();
const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth();
const { wallItems } = useWallItems();
const { planeValue } = useTileDistance();
useEffect(() => {
gl.shadowMap.enabled = true;
gl.shadowMap.type = THREE.PCFShadowMap;
}, [gl, wallItems]);
}, [gl]);
useEffect(() => {
if (lightRef.current && targetRef.current) {

View File

@@ -3,7 +3,7 @@ import { useSceneContext } from "../../../../scene/sceneContext";
export function useDespawnHandler() {
const { materialStore } = useSceneContext();
const { getMaterialById, removeMaterial, setEndTime } = materialStore();
const { getMaterialById, removeMaterial, setEndTime, clearLocations } = materialStore();
const deSpawnLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -17,6 +17,7 @@ export function useDespawnHandler() {
setEndTime(material.materialId, performance.now());
removeMaterial(material.materialId);
clearLocations(material.materialId);
deSpawnLogStatus(material.materialName, `Despawned`);

View File

@@ -8,7 +8,7 @@ export function useAssemblyHandler() {
const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
const assemblyLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -24,6 +24,7 @@ export function useAssemblyHandler() {
if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
assemblyLogStatus(material.materialName, `performing assembly action`);

View File

@@ -8,7 +8,7 @@ export function useWorkerHandler() {
const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const { incrementHumanLoad, incrementLoadCount, addCurrentMaterial, addCurrentAction } = humanStore();
const workerLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -24,6 +24,8 @@ export function useWorkerHandler() {
if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
incrementLoadCount(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
workerLogStatus(material.materialName, `performing worker action`);

View File

@@ -11,7 +11,7 @@ export function useRetrieveHandler() {
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore();
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore();
const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getHumanById, incrementHumanLoad, incrementLoadCount, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getAssetById, setCurrentAnimation } = assetStore();
const { selectedProduct } = selectedProductStore();
const { getArmBotById, addCurrentAction } = armBotStore();
@@ -298,14 +298,15 @@ export function useRetrieveHandler() {
} else if (triggeredModel.type === 'human') {
const human = getHumanById(triggeredModel.modelUuid);
const humanAsset = getAssetById(triggeredModel.modelUuid);
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < human.point.action.loadCapacity) {
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < (action as HumanAction).loadCapacity) {
if (humanAsset && humanAsset.animationState?.current === 'idle') {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
} else if (humanAsset && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) {
if (human.currentLoad < human.point.action.loadCapacity) {
if (action && human.currentLoad < (action as HumanAction).loadCapacity) {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
@@ -315,10 +316,11 @@ export function useRetrieveHandler() {
removeLastMaterial(storageUnit.modelUuid);
updateCurrentLoad(storageUnit.modelUuid, -1);
incrementHumanLoad(human.modelUuid, 1);
incrementLoadCount(human.modelUuid, 1);
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
}
if (human.currentLoad + 1 < human.point.action.loadCapacity) {
if (human.currentLoad + 1 < (action as HumanAction).loadCapacity) {
}
}
}

View File

@@ -6,65 +6,92 @@ import { useSceneContext } from '../../../scene/sceneContext';
type ConveyorCallback = {
conveyorId: string;
callback: () => void;
except: string[];
};
export function useConveyorEventManager() {
const { conveyorStore } = useSceneContext();
const { getConveyorById } = conveyorStore();
const callbacksRef = useRef<ConveyorCallback[]>([]);
const { materialStore } = useSceneContext();
const { getMaterialsByCurrentModelUuid } = materialStore();
const callbacksRef = useRef<Map<string, ConveyorCallback[]>>(new Map());
const isCooldownRef = useRef<Map<string, boolean>>(new Map());
const isMonitoringRef = useRef(false);
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore();
useEffect(() => {
if (isReset) {
callbacksRef.current = [];
callbacksRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
}
}, [isReset])
}, [isReset]);
// Add a new conveyor to monitor
const addConveyorToMonitor = (conveyorId: string, callback: () => void) => {
// Avoid duplicates
if (!callbacksRef.current.some((entry) => entry.conveyorId === conveyorId)) {
callbacksRef.current.push({ conveyorId, callback });
const addConveyorToMonitor = (conveyorId: string, callback: () => void, except?: string[]) => {
if (!callbacksRef.current.has(conveyorId)) {
callbacksRef.current.set(conveyorId, []);
}
// Start monitoring if not already running
if (!isMonitoringRef.current) {
isMonitoringRef.current = true;
}
callbacksRef.current.get(conveyorId)!.push({ conveyorId, callback, except: except || [] });
isMonitoringRef.current = true;
};
// Remove a conveyor from monitoring
const removeConveyorFromMonitor = (conveyorId: string) => {
callbacksRef.current = callbacksRef.current.filter(
(entry) => entry.conveyorId !== conveyorId
);
// Stop monitoring if no more conveyors to track
if (callbacksRef.current.length === 0) {
callbacksRef.current.delete(conveyorId);
isCooldownRef.current.delete(conveyorId);
if (callbacksRef.current.size === 0) {
isMonitoringRef.current = false;
}
};
// Check conveyor states every frame
useFrame(() => {
if (!isMonitoringRef.current || callbacksRef.current.length === 0 || !isPlaying || isPaused) return;
if (!isMonitoringRef.current || !isPlaying || isPaused) return;
callbacksRef.current.forEach(({ conveyorId, callback }) => {
const conveyor = getConveyorById(conveyorId);
if (conveyor?.isPaused === false) {
callback();
removeConveyorFromMonitor(conveyorId); // Remove after triggering
callbacksRef.current.forEach((queue, conveyorId) => {
if (!queue || queue.length === 0) return;
if (isCooldownRef.current.get(conveyorId)) return;
const { callback, except } = queue[0];
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyorId);
let conditionMet = false;
if (!conveyorMaterials || conveyorMaterials.length === 0) {
conditionMet = true;
} else {
const pausedMaterials = conveyorMaterials.filter(m => m.isPaused);
if (pausedMaterials.length === 0) {
conditionMet = true;
} else {
const allPausedInExcept = pausedMaterials.filter(m => !except.includes(m.materialId));
if (allPausedInExcept.length === 0) {
conditionMet = true;
}
}
}
if (conditionMet) {
queue.shift();
if (callback) callback();
if (queue.length === 0) {
removeConveyorFromMonitor(conveyorId);
} else {
isCooldownRef.current.set(conveyorId, true);
setTimeout(() => {
isCooldownRef.current.set(conveyorId, false);
}, 1000);
}
}
});
});
// Cleanup on unmount
useEffect(() => {
return () => {
callbacksRef.current = [];
callbacksRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
};
}, []);
@@ -73,4 +100,4 @@ export function useConveyorEventManager() {
addConveyorToMonitor,
removeConveyorFromMonitor,
};
}
}

View File

@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { useSubModuleStore } from "../../../../store/useModuleStore";
import { useSelectedAction, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
import { useSelectedAction, useSelectedAsset, useSelectedEventData } from "../../../../store/simulation/useSimulationStore";
import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct";
import { QuadraticBezierLine } from "@react-three/drei";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
@@ -38,6 +38,7 @@ function TriggerConnector() {
const { selectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { setSelectedEventData } = useSelectedEventData();
const { projectId } = useParams();
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
@@ -62,6 +63,10 @@ function TriggerConnector() {
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
})
if (firstSelectedPoint?.pointUuid) {
setSelectedEventData(eventData, firstSelectedPoint.pointUuid)
}
}
useEffect(() => {
@@ -155,8 +160,8 @@ function TriggerConnector() {
// Handle Human point
else if (event.type === "human" && 'point' in event) {
const point = event.point;
if (point.action?.triggers) {
point.action.triggers.forEach(trigger => {
point.actions?.forEach(action => {
action.triggers?.forEach(trigger => {
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
@@ -166,7 +171,7 @@ function TriggerConnector() {
});
}
});
}
});
}
});

View File

@@ -2,14 +2,20 @@ import { useEffect, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../scene/sceneContext';
import { useProductContext } from '../../products/productContext';
export function useHumanEventManager() {
const { humanStore } = useSceneContext();
const { getHumanById } = humanStore();
const { humanStore, productStore, assetStore } = useSceneContext();
const { getHumanById, clearLoadCount, setCurrentPhase } = humanStore();
const { getAssetById } = assetStore();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const callbacksRef = useRef<Map<string, (() => void)[]>>(new Map());
const isMonitoringRef = useRef(false);
const actionQueueRef = useRef<Map<string, { actionType: "worker" | "assembly", actionUuid: string }[]>>(new Map());
const isCooldownRef = useRef<Map<string, boolean>>(new Map());
const isMonitoringRef = useRef(false);
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
@@ -18,23 +24,34 @@ export function useHumanEventManager() {
useEffect(() => {
if (isReset) {
callbacksRef.current.clear();
actionQueueRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
}
}, [isReset]);
const addHumanToMonitor = (humanId: string, callback: () => void) => {
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
const action = getActionByUuid(selectedProduct.productUuid, actionUuid || '') as HumanAction | undefined;
if (!action) return;
const actionType = action.actionType;
if (actionType !== "worker" && actionType !== "assembly") return;
if (!callbacksRef.current.has(humanId)) {
callbacksRef.current.set(humanId, []);
actionQueueRef.current.set(humanId, []);
}
callbacksRef.current.get(humanId)!.push(callback);
if (!isMonitoringRef.current) {
isMonitoringRef.current = true;
}
callbacksRef.current.get(humanId)!.push(callback);
actionQueueRef.current.get(humanId)!.push({ actionType, actionUuid });
isMonitoringRef.current = true;
};
const removeHumanFromMonitor = (humanId: string) => {
callbacksRef.current.delete(humanId);
actionQueueRef.current.delete(humanId);
isCooldownRef.current.delete(humanId);
if (callbacksRef.current.size === 0) {
@@ -43,57 +60,106 @@ export function useHumanEventManager() {
};
useFrame(() => {
if (!isMonitoringRef.current || !isPlaying || isPaused) return;
callbacksRef.current.forEach((queue, humanId) => {
if (queue.length === 0 || isCooldownRef.current.get(humanId)) return;
const actionQueue = actionQueueRef.current.get(humanId);
if (!actionQueue || actionQueue.length === 0) return;
const { actionType: expectedActionType, actionUuid } = actionQueue[0];
const human = getHumanById(humanId);
const actionType = human?.point.action.actionType;
const humanAsset = getAssetById(humanId);
const action = getActionByUuid(selectedProduct.productUuid, actionUuid) as HumanAction | undefined;
if (!humanAsset || !human || !action || action.actionType !== expectedActionType) return;
let conditionMet = false;
if (actionType === 'worker') {
conditionMet = Boolean(
human &&
human.isActive === false &&
human.state === 'idle' &&
human.isScheduled === false &&
human.currentLoad < human.point.action.loadCapacity
);
} else if (actionType === 'assembly') {
conditionMet = Boolean(
human &&
human.isActive === false &&
human.state === 'idle' &&
human.isScheduled === false
);
const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined;
if (expectedActionType === "worker") {
if (currentAction && currentAction.actionType === 'worker') {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
humanAsset.animationState?.current === 'idle' &&
human.currentLoad < currentAction.loadCapacity
);
if (human.totalLoadCount >= currentAction.loadCount && actionUuid === human.currentAction?.actionUuid) {
queue.shift();
actionQueue.shift();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
}
return;
}
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
setCurrentPhase(human.modelUuid, 'init');
}
} else {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
humanAsset.animationState?.current === 'idle' &&
human.currentLoad < action.loadCapacity
);
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
setCurrentPhase(human.modelUuid, 'init');
}
}
} else if (expectedActionType === "assembly") {
if (currentAction && currentAction.actionType === 'worker') {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
humanAsset.animationState?.current === 'idle' &&
human.currentLoad < currentAction.loadCapacity
);
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
setCurrentPhase(human.modelUuid, 'init');
}
} else {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
humanAsset.animationState?.current === 'idle' &&
human.currentLoad < action.loadCapacity
)
}
if (conditionMet) {
clearLoadCount(human.modelUuid);
}
}
if (conditionMet) {
const callback = queue.shift();
if (callback) {
callback();
actionQueue.shift();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
} else {
isCooldownRef.current.set(humanId, true);
setTimeout(() => {
isCooldownRef.current.set(humanId, false);
}, 1000);
}
if (callback) callback();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
} else {
isCooldownRef.current.set(humanId, true);
setTimeout(() => {
isCooldownRef.current.set(humanId, false);
}, 1000);
}
}
});
});
}, 0);
useEffect(() => {
return () => {
callbacksRef.current.clear();
isMonitoringRef.current = false;
actionQueueRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
};
}, []);

View File

@@ -4,18 +4,21 @@ import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
interface HumanAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
startUnloadingProcess: () => void;
currentPhase: string;
human: HumanStatus;
}
function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore, assetStore } = useSceneContext();
function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore, assetStore, productStore } = useSceneContext();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { getHumanById } = humanStore();
const { setCurrentAnimation } = assetStore();
const { isPaused } = usePauseButtonStore();
@@ -25,23 +28,29 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>(human.point?.action?.pickUpPoint?.rotation || [0, 0, 0])
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.pickUpPoint?.rotation || [0, 0, 0])
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (currentPhase === 'init-pickup' && path.length > 0) {
if (!human.currentAction?.actionUuid) return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (human.currentPhase === 'init-pickup' && path.length > 0) {
setCurrentPath(path);
setObjectRotation(human.point.action.pickUpPoint?.rotation ?? null)
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setObjectRotation(human.point.action?.dropPoint?.rotation ?? null)
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null)
} else if (human.currentPhase === 'init-assembly' && path.length > 0) {
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null)
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setObjectRotation(human.point.action?.pickUpPoint?.rotation ?? null)
} else if (human.currentPhase === 'pickup-drop' && path.length > 0) {
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null)
setCurrentPath(path);
} else if (human.currentPhase === 'drop-pickup' && path.length > 0) {
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null)
setCurrentPath(path);
}
}, [currentPhase, path, objectRotation]);
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
useEffect(() => {
completedRef.current = false;
@@ -74,7 +83,7 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const object = scene.getObjectByProperty('uuid', human.modelUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
if (isPaused || !isPlaying) return;
let totalDistance = 0;
const distances = [];
@@ -125,13 +134,13 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
if (human.currentMaterials.length > 0) {
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
}
} else {
if (human.currentMaterials.length > 0) {
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
@@ -178,7 +187,7 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
if (human.currentPhase === 'pickup-drop') {
requestAnimationFrame(startUnloadingProcess);
}
}

View File

@@ -2,11 +2,18 @@ import { useEffect, useRef, useState } from 'react';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
const MaterialAnimator = ({ human }: { human: HumanStatus; }) => {
const { productStore } = useSceneContext();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false);
const [isAttached, setIsAttached] = useState(false);
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const { scene } = useThree();
useEffect(() => {
@@ -39,11 +46,11 @@ const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
meshRef.current.visible = true;
setIsAttached(true);
}
}, [hasLoad, human.modelUuid, scene]);
}, [hasLoad, human.modelUuid, scene, human.currentPhase]);
return (
<>
{hasLoad && human.point.action.actionType === 'worker' && human.currentMaterials.length > 0 && (
{hasLoad && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
<MaterialModel
matRef={meshRef}
materialId={human.currentMaterials[0].materialId || ''}

View File

@@ -16,7 +16,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime, setMaterial } = materialStore();
const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
@@ -27,9 +27,8 @@ function HumanInstance({ human }: { human: HumanStatus }) {
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore();
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime, setCurrentPhase } = humanStore();
const [currentPhase, setCurrentPhase] = useState<string>('init');
const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null);
const idleTimeRef = useRef<number>(0);
@@ -57,34 +56,35 @@ function HumanInstance({ human }: { human: HumanStatus }) {
isSpeedRef.current = speed;
}, [speed]);
const computePath = useCallback(
(start: any, end: any) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
if (
segmentPath.length > 0 &&
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
) {
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
} else {
console.log("There is no path here...Choose valid path")
const { path: segmentPaths } = navMeshQuery.computePath(start, start);
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
}
} catch {
console.error("Failed to compute path");
return [];
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
if (
segmentPath.length > 0 &&
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
) {
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
} else {
console.log("There is no path here...Choose valid path")
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
}
}, [navMesh]);
} catch {
console.error("Failed to compute path");
return [];
}
}, [navMesh]);
function humanStatus(modelId: string, status: string) {
// console.log(`${modelId} , ${status}`);
}
function reset() {
setCurrentPhase('init');
setCurrentPhase(human.modelUuid, 'init');
setHumanActive(human.modelUuid, false);
setHumanState(human.modelUuid, 'idle');
setHumanScheduled(human.modelUuid, false);
@@ -120,23 +120,32 @@ function HumanInstance({ human }: { human: HumanStatus }) {
useEffect(() => {
if (isPlaying) {
if (!human.point.action.assemblyPoint || human.point.action.actionType === 'worker') return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
if (!humanMesh) return;
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
setPath(toPickupPath);
setHumanState(human.modelUuid, 'idle');
setCurrentPhase('waiting');
setCurrentPhase(human.modelUuid, 'init-assembly');
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') {
} else if (!human.isActive && human.state === 'idle' && human.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');
setCurrentPhase(human.modelUuid, 'assembling');
setHumanActive(human.modelUuid, true);
processStartTimeRef.current = performance.now();
processTimeRef.current = human.point.action.processTime || 0;
processTimeRef.current = (action as HumanAction).processTime || 0;
accumulatedPausedTimeRef.current = 0;
lastPauseTimeRef.current = null;
hasLoggedHalfway.current = false;
@@ -147,9 +156,9 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
}
} 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') {
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
setHumanState(human.modelUuid, 'idle');
setCurrentPhase('waiting');
setCurrentPhase(human.modelUuid, 'waiting');
setHumanActive(human.modelUuid, false);
setHumanScheduled(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
@@ -158,7 +167,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
decrementHumanLoad(human.modelUuid, 1);
const material = removeLastMaterial(human.modelUuid);
if (material) {
triggerPointActions(human.point.action, material.materialId);
triggerPointActions((action as HumanAction), material.materialId);
}
}
}
@@ -167,12 +176,14 @@ function HumanInstance({ human }: { human: HumanStatus }) {
reset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
const trackAssemblyProcess = useCallback(() => {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const now = performance.now();
if (!processStartTimeRef.current || !human.point.action.processTime) {
if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
return;
}
@@ -188,12 +199,12 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
const totalProcessTimeMs = human.point.action.processTime * 1000;
const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 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');
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
}
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
}
@@ -210,68 +221,47 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
}, [human.modelUuid, human.point.action.processTime, human.currentMaterials]);
}, [human.modelUuid, human.currentMaterials]);
useEffect(() => {
if (isPlaying) {
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint || human.point.action.actionType === 'assembly') return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
const toPickupPath = computePath(
new THREE.Vector3(human?.position[0], human?.position[1], human?.position[2]),
new THREE.Vector3(
human?.point?.action?.pickUpPoint?.position?.[0] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[1] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[2] ?? 0
)
);
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
if (!humanMesh) return;
const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);
setPath(toPickupPath);
setCurrentPhase('init-pickup');
setCurrentPhase(human.modelUuid, 'init-pickup');
setHumanState(human.modelUuid, 'running');
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
return;
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
if (humanAsset && human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const toDrop = computePath(
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
)
);
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'picking') {
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (action.pickUpPoint && action.dropPoint) {
const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
setPath(toDrop);
setCurrentPhase('pickup-drop');
setCurrentPhase(human.modelUuid, 'pickup-drop');
setHumanState(human.modelUuid, 'running');
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
} else if (human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
if (human.currentMaterials[0]?.materialId) {
setIsVisible(human.currentMaterials[0]?.materialId, false);
}
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
}
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const dropToPickup = computePath(
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
)
);
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'dropping' && human.currentLoad === 0) {
if (action.pickUpPoint && action.dropPoint) {
const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
setPath(dropToPickup);
setCurrentPhase('drop-pickup');
setCurrentPhase(human.modelUuid, 'drop-pickup');
setHumanState(human.modelUuid, 'running');
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
@@ -283,25 +273,32 @@ function HumanInstance({ human }: { human: HumanStatus }) {
reset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
function handleCallBack() {
if (currentPhase === 'init-pickup') {
setCurrentPhase('picking');
if (human.currentPhase === 'init-pickup') {
setCurrentPhase(human.modelUuid, 'picking');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
setPath([]);
} else if (currentPhase === 'pickup-drop') {
setCurrentPhase('dropping');
} if (human.currentPhase === 'init-assembly') {
setCurrentPhase(human.modelUuid, 'waiting');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
setPath([]);
} else if (human.currentPhase === 'pickup-drop') {
setCurrentPhase(human.modelUuid, 'dropping');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
humanStatus(human.modelUuid, 'Reached drop point');
setPath([]);
} else if (currentPhase === 'drop-pickup') {
setCurrentPhase('picking');
} else if (human.currentPhase === 'drop-pickup') {
setCurrentPhase(human.modelUuid, 'picking');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setHumanScheduled(human.modelUuid, false);
@@ -357,38 +354,35 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}, [human, isPlaying]);
function startUnloadingProcess() {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
if (human.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
if ((action as HumanAction).triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToConveyor(model);
}
} else if (model.type === 'machine') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToMachine(model);
}
} else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToArmBot(model);
}
} else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToStorageUnit(model);
}
} else if (model.type === 'vehicle') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToVehicle(model);
}
@@ -407,6 +401,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (model && humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -420,7 +415,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.currentLoad,
model.modelUuid,
model.point.action.storageCapacity,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -474,6 +469,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -487,7 +483,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
conveyor.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -539,6 +535,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -552,7 +549,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -609,6 +606,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -622,7 +620,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -679,6 +677,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToMachine(model: MachineEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -692,7 +691,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -782,7 +781,6 @@ function HumanInstance({ human }: { human: HumanStatus }) {
<HumanAnimator
path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
human={human}
reset={reset}
startUnloadingProcess={startUnloadingProcess}

View File

@@ -27,9 +27,10 @@ function HumanUi() {
const { humanStore, productStore } = useSceneContext();
const { selectedProduct } = selectedProductStore();
const { humans, getHumanById } = humanStore();
const { updateEvent } = productStore();
const { updateEvent, updateAction, getActionByUuid } = productStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]);
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]);
const [assemblyRotation, setAssemblyRotation] = useState<[number, number, number]>([0, 0, 0]);
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
@@ -48,9 +49,8 @@ function HumanUi() {
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const selectedHuman = selectedEventSphere ? getHumanById(selectedEventSphere.userData.modelUuid) : null;
const actionType = selectedHuman?.point?.action?.actionType || null;
const isAssembly = actionType === 'assembly';
const currentAction = getActionByUuid(selectedProduct.productUuid, selectedAction.actionId || '');
const isAssembly = currentAction?.actionType === 'assembly';
const updateBackend = (
productName: string,
@@ -68,10 +68,10 @@ function HumanUi() {
};
useEffect(() => {
if (!selectedEventSphere) return;
if (!selectedEventSphere || !selectedAction?.actionId) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !selectedHuman.point?.action) return;
if (!selectedHuman || !selectedHuman.point?.actions) return;
setSelectedHumanData({
position: selectedHuman.position,
@@ -86,42 +86,47 @@ function HumanUi() {
);
}
const action = selectedHuman.point.action;
const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId);
if (!action) return;
if (action.pickUpPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.pickUpPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setStartPosition([localPosition.x, 1, localPosition.z]);
setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
if (isAssembly) {
if (action.assemblyPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.assemblyPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setAssemblyPosition([localPosition.x, 1, localPosition.z]);
setAssemblyRotation(action.assemblyPoint.rotation || [0, 0, 0]);
} else {
setAssemblyPosition([0, 1, 0]);
setAssemblyRotation([0, 0, 0]);
}
} else {
setStartPosition([0, 1, 0]);
setStartRotation([0, Math.PI, 0]);
}
if (action.pickUpPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.pickUpPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setStartPosition([localPosition.x, 1, localPosition.z]);
setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
} else {
setStartPosition([0, 1, 0]);
setStartRotation([0, Math.PI, 0]);
}
if (action.dropPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.dropPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setEndPosition([localPosition.x, 1, localPosition.z]);
setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]);
} else {
setEndPosition([0, 1, 0]);
setEndRotation([0, 0, 0]);
if (action.dropPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.dropPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setEndPosition([localPosition.x, 1, localPosition.z]);
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]);
const handlePointerDown = (
e: any,
state: "start" | "end",
rotation: "start" | "end"
state: "start" | "end" | "assembly",
rotation: "start" | "end" | "assembly"
) => {
if (isAssembly) return;
e.stopPropagation();
const intersection = new Vector3();
const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
@@ -144,7 +149,10 @@ function HumanUi() {
if (outerGroup.current) {
localPoint = outerGroup.current.worldToLocal(intersection.clone());
}
const marker = state === "start" ? startMarker.current : endMarker.current;
const marker =
state === "start" ? startMarker.current :
state === "end" ? endMarker.current :
assemblyMarker.current;
if (marker && localPoint) {
const markerPos = new Vector3().copy(marker.position);
dragOffset.current.copy(markerPos.sub(localPoint));
@@ -154,7 +162,6 @@ function HumanUi() {
if (controls) (controls as any).enabled = false;
};
const handlePointerUp = () => {
(controls as any).enabled = true;
setIsDragging(null);
@@ -163,40 +170,46 @@ function HumanUi() {
if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !outerGroup.current) return;
if (!selectedHuman || !outerGroup.current || !currentAction) return;
const isAssembly = selectedHuman.point?.action?.actionType === 'assembly';
const updatedActions = selectedHuman.point.actions.map(action => {
if (action.actionUuid !== currentAction.actionUuid) return action;
let updatedAction;
if (isAssembly) {
if (!assemblyMarker.current || !outerGroup.current) return action;
if (isAssembly) {
updatedAction = {
...selectedHuman.point.action,
assemblyPoint: {
rotation: assemblyRotation
},
};
} else {
if (!startMarker.current || !endMarker.current) return;
const worldPosAssembly = new Vector3(...assemblyPosition);
const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());
const worldPosStart = new Vector3(...startPosition);
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
return {
...action,
assemblyPoint: {
position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
rotation: assemblyRotation
},
};
} else {
if (!startMarker.current || !endMarker.current || !outerGroup.current) return action;
const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
const worldPosStart = new Vector3(...startPosition);
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
updatedAction = {
...selectedHuman.point.action,
pickUpPoint: {
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
rotation: startRotation,
},
dropPoint: {
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
rotation: endRotation,
},
};
}
const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
return {
...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,
@@ -205,7 +218,7 @@ function HumanUi() {
...selectedHuman,
point: {
...selectedHuman.point,
action: updatedAction,
actions: updatedActions,
},
}
);
@@ -221,7 +234,7 @@ function HumanUi() {
};
useFrame(() => {
if (isAssembly || !isDragging || !plane.current || !raycaster || !outerGroup.current) return;
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
const intersectPoint = new Vector3();
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
if (!intersects) return;
@@ -232,6 +245,8 @@ function HumanUi() {
setStartPosition([localPoint.x, 1, localPoint.z]);
} else if (isDragging === "end") {
setEndPosition([localPoint.x, 1, localPoint.z]);
} else if (isDragging === "assembly") {
setAssemblyPosition([localPoint.x, 1, localPoint.z]);
}
});
@@ -240,28 +255,34 @@ function HumanUi() {
const currentPointerX = state.pointer.x;
const deltaX = currentPointerX - prevMousePos.current.x;
prevMousePos.current.x = currentPointerX;
const marker = isRotating === "start" ? isAssembly ? assemblyMarker.current : startMarker.current : isAssembly ? assemblyMarker.current : endMarker.current;
const marker =
isRotating === "start" ? startMarker.current :
isRotating === "end" ? endMarker.current :
assemblyMarker.current;
if (marker) {
const rotationSpeed = 10;
marker.rotation.y += deltaX * rotationSpeed;
if (isAssembly && isRotating === "start") {
setAssemblyRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
} else if (isRotating === "start") {
if (isRotating === "start") {
setStartRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
} else {
} else if (isRotating === "end") {
setEndRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
} else {
setAssemblyRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
}
}
});
@@ -281,7 +302,7 @@ function HumanUi() {
return () => {
window.removeEventListener("pointerup", handleGlobalPointerUp);
};
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyRotation]);
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyPosition, assemblyRotation]);
return (
<>
@@ -295,16 +316,13 @@ function HumanUi() {
<primitive
ref={assemblyMarker}
object={assemblyScene}
position={[0, 1, 0]}
position={assemblyPosition}
rotation={assemblyRotation}
onPointerDown={(e: any) => {
if (e.object.parent.name === "handle") {
e.stopPropagation();
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
prevMousePos.current.x = normalizedX;
setIsRotating("start");
setIsDragging(null);
if (controls) (controls as any).enabled = false;
handlePointerDown(e, "assembly", "assembly");
} else {
handlePointerDown(e, "assembly", "assembly");
}
}}
onPointerMissed={() => {
@@ -322,8 +340,11 @@ function HumanUi() {
position={startPosition}
rotation={startRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "start", "start");
if (e.object.parent.name === "handle") {
handlePointerDown(e, "start", "start");
} else {
handlePointerDown(e, "start", "start");
}
}}
onPointerMissed={() => {
setIsDragging(null);
@@ -339,8 +360,11 @@ function HumanUi() {
position={endPosition}
rotation={endRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "end", "end");
if (e.object.parent.name === "handle") {
handlePointerDown(e, "end", "end");
} else {
handlePointerDown(e, "end", "end");
}
}}
onPointerMissed={() => {
setIsDragging(null);
@@ -356,4 +380,4 @@ function HumanUi() {
);
}
export default HumanUi
export default HumanUi;

View File

@@ -12,7 +12,8 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
const matRef: any = useRef();
const { scene } = useThree();
const { selectedProductStore } = useProductContext();
const { productStore } = useSceneContext();
const { productStore, materialStore } = useSceneContext();
const { setIsPaused } = materialStore();
const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByPointUuid } = productStore();
const { selectedProduct } = selectedProductStore();
const { speed } = useAnimationPlaySpeed();
@@ -80,16 +81,23 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
return 1;
}
if (event.type === 'human') {
return event.speed;
}
} else {
return 1;
}
}
const callTrigger = () => {
if (!material.next) return;
const action = getActionByPointUuid(selectedProduct.productUuid, material.next.pointUuid);
if (action) {
triggerPointActions(action, material.materialId);
if (material.next) {
const action = getActionByPointUuid(selectedProduct.productUuid, material.next.pointUuid);
if (action) {
triggerPointActions(action, material.materialId);
}
} else {
setIsPaused(material.materialId, true);
}
}
@@ -97,7 +105,7 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
<>
{material.isRendered &&
<MaterialModel materialId={material.materialId} matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} />
<MaterialModel materialId={material.materialId} matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} rotation={[rotation.x, rotation.y, rotation.z]} />
}
<MaterialAnimator

View File

@@ -20,7 +20,7 @@ function Products() {
const { addMachine, clearMachines } = machineStore();
const { addConveyor, clearConveyors } = conveyorStore();
const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore();
const { addHuman, clearHumans } = humanStore();
const { addHuman, addCurrentAction, clearHumans } = humanStore();
const { isReset } = useResetButtonStore();
const { isPlaying } = usePlayButtonStore();
const { mainProduct } = useMainProduct();
@@ -162,6 +162,10 @@ function Products() {
product.eventDatas.forEach(events => {
if (events.type === 'human') {
addHuman(selectedProduct.productUuid, events);
if (events.point.actions.length > 0) {
addCurrentAction(events.modelUuid, events.point.actions[0].actionUuid);
}
}
});
}

View File

@@ -330,9 +330,6 @@ export function useTriggerHandler() {
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
@@ -343,88 +340,53 @@ export function useTriggerHandler() {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addVehicleToMonitor(vehicle.modelUuid, () => {
handleAction(action, materialId);
})
}
}
} else {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addVehicleToMonitor(vehicle.modelUuid, () => {
handleAction(action, materialId);
})
}
}
})
}, action.actionUuid)
}
}
} else if (model?.type === 'transfer') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (human) {
if (human.isActive === false && human.state === 'idle') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
addConveyorToMonitor(conveyor.modelUuid, () => {
addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId);
}
)
}, action.actionUuid)
}, [materialId])
}
} else {
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid, () => {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
}
})
}
}, action.actionUuid)
}
} else if (model?.type === 'machine') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
@@ -434,9 +396,6 @@ export function useTriggerHandler() {
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
@@ -445,46 +404,31 @@ export function useTriggerHandler() {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addMachineToMonitor(machine.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addMachineToMonitor(machine.modelUuid, () => {
handleAction(action, materialId);
})
}
}
} else {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addMachineToMonitor(machine.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addMachineToMonitor(machine.modelUuid, () => {
handleAction(action, materialId);
})
}
}
}
);
}, action.actionUuid);
}
}
} else {
@@ -492,23 +436,16 @@ export function useTriggerHandler() {
// Handle current action from arm bot
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setHumanScheduled(human.modelUuid, true);
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId)
}
);
addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId)
}, action.actionUuid);
}
}
@@ -517,9 +454,6 @@ export function useTriggerHandler() {
// Handle current action from arm bot
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
@@ -528,14 +462,9 @@ export function useTriggerHandler() {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId)
}
);
addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId)
}, action.actionUuid);
}
}
@@ -698,27 +627,12 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null);
if (action && human) {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
setIsVisible(materialId, false);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}, action.actionUuid)
}
}
}
@@ -964,8 +878,7 @@ export function useTriggerHandler() {
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId)
}
}
);
}, action.actionUuid);
}
}
}
@@ -1256,11 +1169,11 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null);
if (action) {
if (action && action.actionType === 'worker') {
if (human) {
if (human.isActive === false && human.state === 'idle' && !human.isScheduled && human.currentLoad < human.point.action.loadCapacity) {
if (human.isActive === false && human.state === 'idle' && !human.isScheduled && human.currentLoad < action.loadCapacity) {
setIsVisible(materialId, false);
@@ -1506,7 +1419,7 @@ export function useTriggerHandler() {
})
setIsPaused(material.materialId, false);
})
}, action.actionUuid)
} else {
setNextLocation(material.materialId, {
modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '',

View File

@@ -180,7 +180,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
setCurrentPath(updated);
};
return (
<>
{selectedPath === "auto" &&

View File

@@ -11,16 +11,20 @@ import InteractivePoints from '../animator/interactivePoint';
import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';
import { useHumanEventManager } from '../../../human/eventManager/useHumanEventManager';
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getHumanById } = humanStore();
const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad , incrementLoadCount } = humanStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler();
const { setCurrentAnimation, getAssetById } = assetStore();
const { addHumanToMonitor } = useHumanEventManager();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
@@ -96,7 +100,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
useEffect(() => {
if (isPlaying || selectedPath === "auto") {
if (isPlaying && selectedPath === "auto") {
if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
@@ -223,6 +227,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (agvDetail.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, agvDetail.point.action.triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
const triggeredAction = getActionByUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredAction?.actionUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
@@ -244,8 +249,8 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
} else if (model.type === 'human') {
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToHuman(model);
if (action && (triggeredAction?.actionType === 'assembly' || triggeredAction?.actionType === 'worker')) {
handleMaterialDropToHuman(model, triggeredAction);
}
}
} else {
@@ -260,76 +265,59 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
}
function handleMaterialDropToHuman(model: HumanEventSchema) {
function handleMaterialDropToHuman(model: HumanEventSchema, action: HumanAction) {
if (model) {
if (model.point.action.actionType === 'worker') {
loopMaterialDropToHuman(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
model.modelUuid,
model.point.action.loadCapacity,
agvDetail.point.action
);
if (action.actionType === 'worker') {
addHumanToMonitor(model.modelUuid, () => {
loopMaterialDropToHuman(
agvDetail,
model.modelUuid,
action.actionUuid
);
}, action.actionUuid || '')
}
}
}
function loopMaterialDropToHuman(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
vehicle: VehicleStatus,
humanId: string,
storageMaxCapacity: number,
action: VehicleAction
humanActionId: string
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
let currentVehicleLoad = vehicle.currentLoad;
const unloadLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(unloadLoop);
const human = getHumanById(humanId);
const humanAsset = getAssetById(humanId);
const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);
if (!human || human.currentAction?.actionUuid !== humanAction?.actionUuid) return;
if (!human.isActive && human.currentLoad < (humanAction?.loadCapacity || 0) && humanAsset?.animationState?.current === 'idle' && humanAction?.actionType === 'worker') {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
decrementVehicleLoad(vehicle.modelUuid, 1);
currentVehicleLoad -= 1;
const material = removeLastMaterial(vehicle.modelUuid);
if (material) {
setIsVisible(material.materialId, false);
addCurrentMaterial(humanId, material.materialType, material.materialId);
incrementHumanLoad(humanId, 1);
incrementLoadCount(humanId, 1);
}
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const human = getHumanById(humanId);
if (elapsedTime >= fixedInterval) {
if (human && agvDetail &&
human.currentLoad < storageMaxCapacity &&
vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0 && human.currentLoad < storageMaxCapacity) {
startTime = performance.now();
requestAnimationFrame(unloadLoop);
}
}
} else {
setTimeout(() => {
requestAnimationFrame(unloadLoop);
}
}, 500)
};
const human = getHumanById(humanId);
if (human && vehicleCurrentLoad > 0 && human?.currentLoad < storageMaxCapacity) {
unloadLoop();
const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);
if (human && human.currentAction?.actionUuid !== humanActionId && human.currentLoad < (humanAction?.loadCapacity || 0)) {
addCurrentAction(humanId, humanActionId);
setTimeout(() => {
unloadLoop();
}, 500)
}
}

View File

@@ -1,25 +1,32 @@
import { useRef } from "react";
import { useNavMesh } from "../../../../store/builder/store";
import PolygonGenerator from "./polygonGenerator";
import NavMeshDetails from "./navMeshDetails";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import * as Types from "../../../../types/world/worldTypes";
import { useLoadingProgress, useNavMesh, useTileDistance } from "../../../../store/builder/store";
import useModuleStore from "../../../../store/useModuleStore";
import PolygonGenerator from "./polygonGenerator";
import NavMeshDetails from "./navMeshDetails";
function NavMesh() {
let groupRef = useRef() as Types.RefGroup;
const { setNavMesh } = useNavMesh();
const { loadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { planeValue, gridValue } = useTileDistance();
return (
<>
<PolygonGenerator groupRef={groupRef} />
<NavMeshDetails setNavMesh={setNavMesh} groupRef={groupRef} />
{activeModule === 'simulation' && loadingProgress === 0 &&
<>
<PolygonGenerator groupRef={groupRef} />
<NavMeshDetails setNavMesh={setNavMesh} groupRef={groupRef} />
<group ref={groupRef} visible={false} name="Meshes">
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
<planeGeometry args={[300, 300]} />
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh>
</group>
<group ref={groupRef} visible={false} name="Meshes">
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
<planeGeometry args={[planeValue.width, planeValue.height]} />
</mesh>
</group>
</>
}
</>
)
}

View File

@@ -1,10 +1,9 @@
import React, { useEffect } from "react";
import React, { useEffect, useMemo } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { generateSoloNavMesh } from "@recast-navigation/generators";
import { init as initRecastNavigation } from "@recast-navigation/core";
import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView } from "../../../../store/builder/store";
interface NavMeshDetailsProps {
@@ -12,45 +11,48 @@ interface NavMeshDetailsProps {
groupRef: React.MutableRefObject<THREE.Group | null>;
}
const NAVMESH_CONFIG = {
// cellSize: 0.2,
// cellHeight: 0.7,
// walkableRadius: 0.5,
cellSize: 0.3,
cellHeight: 0.7,
walkableRadius: 0.7,
};
export default function NavMeshDetails({
setNavMesh,
groupRef,
}: NavMeshDetailsProps) {
const { aisleStore, wallStore } = useSceneContext();
const { aisles } = aisleStore();
const { scene } = useThree();
const { walls } = wallStore();
const { toggleView } = useToggleView();
const meshes = useMemo(() => {
return groupRef.current?.children.filter((child): child is THREE.Mesh =>
child instanceof THREE.Mesh
) || [];
}, [groupRef.current?.children]);
useEffect(() => {
if (toggleView) return;
if (toggleView || meshes.length === 0) return;
const initializeNavigation = async () => {
try {
await initRecastNavigation();
if (!groupRef.current || groupRef.current.children.length === 0) {
return;
}
const meshes = groupRef?.current?.children as THREE.Mesh[];
const [positions, indices] = getPositionsAndIndices(meshes);
// const cellSize = 0.2;
// const cellHeight = 0.7;
// const walkableRadius = 0.5;
const { cellSize, cellHeight, walkableRadius } = NAVMESH_CONFIG;
const cellSize = 0.3;
const cellHeight = 0.7;
const walkableRadius = 0.7;
const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs: cellSize,
ch: cellHeight,
walkableRadius: Math.round(walkableRadius / cellHeight),
});
if (!success || !navMesh) {
return;
}
if (!success || !navMesh) return;
setNavMesh(navMesh);
@@ -62,12 +64,12 @@ export default function NavMeshDetails({
debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer);
} catch (error) {
echo.error("Failed to initialize navigation")
console.error("Failed to initialize navigation:", error);
}
};
initializeNavigation();
}, [scene, groupRef, aisles, walls, toggleView]);
}, [meshes, setNavMesh, toggleView, scene]);
return null;
}

View File

@@ -76,42 +76,18 @@ export default function PolygonGenerator({
turf.lineString(line.map((p: any) => p?.position))
);
const validLineFeatures = lineFeatures.map(ls => {
const coords = ls.geometry.coordinates.map(coord => coord.join(','));
if (coords.length < 2) return null;
const start = coords[0];
const end = coords[coords.length - 1];
const middle = coords.slice(1, -1);
const seen = new Set<string>([start, end]);
const filteredMiddle: string[] = [];
for (const point of middle) {
if (!seen.has(point)) {
seen.add(point);
filteredMiddle.push(point);
}
let validLineFeatures = lineFeatures.filter((line) => {
const coords = line.geometry.coordinates;
return coords.length >= 2;
}).filter((line) => {
if (line.geometry.coordinates[0].toString() !== line.geometry.coordinates[1].toString()) {
return true;
}
})
const newCoords = [start, ...filteredMiddle, end];
if (validLineFeatures.length < 3) { validLineFeatures = [] }
if (newCoords.length >= 4) {
const resultCoords = newCoords.map(str => str.split(',').map(Number));
return {
...ls,
geometry: {
...ls.geometry,
coordinates: resultCoords,
},
};
}
return null;
}).filter(Boolean);
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures as any) as any);
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
renderWallGeometry(wallPoints);

View File

@@ -4,10 +4,8 @@ import {
useSocketStore,
useOrganization,
useUserName,
useWallItems,
useSaveVersion,
useProjectName,
useZones,
useActiveTool,
} from "../store/builder/store";
import { useNavigate, useParams } from "react-router-dom";
@@ -27,7 +25,7 @@ import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
import { VersionProvider } from "../modules/builder/version/versionContext";
import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
import { handleCanvasCursors } from "../utils/handleCanvasCursors";
import { handleCanvasCursors } from "../utils/mouseUtils/handleCanvasCursors";
const Project: React.FC = () => {
let navigate = useNavigate();
@@ -36,8 +34,6 @@ const Project: React.FC = () => {
const { setActiveModule } = useModuleStore();
const { setUserName } = useUserName();
const { setOrganization } = useOrganization();
const { setWallItems } = useWallItems();
const { setZones } = useZones();
const { isVersionSaved } = useSaveVersion();
const { projectId } = useParams();
const { setProjectName } = useProjectName();
@@ -110,8 +106,6 @@ const Project: React.FC = () => {
}, [isVersionSaved]);
useEffect(() => {
setWallItems([]);
setZones([]);
setActiveModule("builder");
if (email) {
const token = localStorage.getItem("token");

View File

@@ -1,4 +1,3 @@
import * as THREE from "three";
import { create } from "zustand";
import { io } from "socket.io-client";
import * as CONSTANTS from "../../types/world/worldConstants";
@@ -146,39 +145,11 @@ export const useToggleView = create<any>((set: any) => ({
setToggleView: (x: any) => set(() => ({ toggleView: x })),
}));
export const useUpdateScene = create<any>((set: any) => ({
updateScene: false,
setUpdateScene: (x: any) => set(() => ({ updateScene: x })),
}));
export const useWalls = create<any>((set: any) => ({
walls: [],
setWalls: (x: any) => set(() => ({ walls: x })),
}));
export const useRoomsState = create<any>((set: any) => ({
roomsState: [],
setRoomsState: (x: any) => set(() => ({ roomsState: x })),
}));
export const useZones = create<any>((set: any) => ({
zones: [],
setZones: (callback: any) =>
set((state: any) => ({
zones: typeof callback === "function" ? callback(state.zones) : callback,
})),
}));
interface ZonePointsState {
zonePoints: THREE.Vector3[];
setZonePoints: (points: THREE.Vector3[]) => void;
}
export const useZonePoints = create<ZonePointsState>((set) => ({
zonePoints: [],
setZonePoints: (points) => set({ zonePoints: points }),
}));
export const useSelectedItem = create<any>((set: any) => ({
selectedItem: {
name: "",
@@ -215,45 +186,11 @@ export const useMenuVisible = create<any>((set: any) => ({
setMenuVisible: (x: any) => set(() => ({ menuVisible: x })),
}));
// export const useDeleteTool = create<any>((set: any) => ({
// deleteTool: false,
// setDeleteTool: (x: any) => set(() => ({ deleteTool: x })),
// }));
export const useToolMode = create<any>((set: any) => ({
toolMode: null,
setToolMode: (x: any) => set(() => ({ toolMode: x })),
}));
export const useNewLines = create<any>((set: any) => ({
newLines: [],
setNewLines: (x: any) => set(() => ({ newLines: x })),
}));
export const useDeletedLines = create<any>((set: any) => ({
deletedLines: [],
setDeletedLines: (x: any) => set(() => ({ deletedLines: x })),
}));
export const useMovePoint = create<any>((set: any) => ({
movePoint: false,
setMovePoint: (x: any) => set(() => ({ movePoint: x })),
}));
// export const useDeletePointOrLine = create<any>((set: any) => ({
// deletePointOrLine: false,
// setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })),
// }));
export const useWallItems = create<any>((set: any) => ({
wallItems: [],
setWallItems: (callback: any) =>
set((state: any) => ({
wallItems:
typeof callback === "function" ? callback(state.wallItems) : callback,
})),
}));
export const useSelectedWallItem = create<any>((set: any) => ({
selectedWallItem: null,
setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })),
@@ -315,24 +252,6 @@ export const useActiveLayer = create<any>((set: any) => ({
setActiveLayer: (x: any) => set({ activeLayer: x }),
}));
interface RefTextUpdateState {
refTextupdate: number;
setRefTextUpdate: (
callback: (currentValue: number) => number | number
) => void;
}
export const useRefTextUpdate = create<RefTextUpdateState>((set) => ({
refTextupdate: -1000,
setRefTextUpdate: (callback) =>
set((state) => ({
refTextupdate:
typeof callback === "function"
? callback(state.refTextupdate)
: callback,
})),
}));
export const useResetCamera = create<any>((set: any) => ({
resetCamera: false,
setResetCamera: (x: any) => set({ resetCamera: x }),
@@ -353,11 +272,6 @@ export const useActiveSubTool = create<any>((set: any) => ({
setActiveSubTool: (x: any) => set({ activeSubTool: x }),
}));
export const use2DUndoRedo = create<any>((set: any) => ({
is2DUndoRedo: null,
set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }),
}));
export const useElevation = create<any>((set: any) => ({
elevation: 45,
setElevation: (x: any) => set({ elevation: x }),
@@ -434,21 +348,6 @@ export const useDrieUIValue = create<any>((set: any) => ({
})),
}));
export const useStartSimulation = create<any>((set: any) => ({
startSimulation: false,
setStartSimulation: (x: any) => set({ startSimulation: x }),
}));
export const useEyeDropMode = create<any>((set: any) => ({
eyeDropMode: false,
setEyeDropMode: (x: any) => set({ eyeDropMode: x }),
}));
export const useEditingPoint = create<any>((set: any) => ({
editingPoint: false,
setEditingPoint: (x: any) => set({ editingPoint: x }),
}));
export const usezoneTarget = create<any>((set: any) => ({
zoneTarget: [],
setZoneTarget: (x: any) => set({ zoneTarget: x }),

View File

@@ -12,6 +12,11 @@ interface HumansStore {
) => void;
clearHumans: () => void;
setCurrentPhase: (modelUuid: string, phase: string) => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void;
removeCurrentAction: (modelUuid: string) => void;
setHumanActive: (modelUuid: string, isActive: boolean) => void;
setHumanScheduled: (modelUuid: string, isPicking: boolean) => void;
setHumanLoad: (modelUuid: string, load: number) => void;
@@ -22,6 +27,11 @@ interface HumansStore {
incrementHumanLoad: (modelUuid: string, incrementBy: number) => void;
decrementHumanLoad: (modelUuid: string, decrementBy: number) => void;
incrementLoadCount: (modelUuid: string, incrementBy: number) => void;
decrementLoadCount: (modelUuid: string, decrementBy: number) => void;
clearLoadCount: (modelUuid: string) => void;
addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void;
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void;
removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
@@ -50,10 +60,12 @@ export const createHumanStore = () => {
state.humans.push({
...event,
productUuid,
currentPhase: 'init',
isActive: false,
isScheduled: false,
idleTime: 0,
activeTime: 0,
totalLoadCount: 0,
currentLoad: 0,
currentMaterials: [],
distanceTraveled: 0
@@ -83,6 +95,39 @@ export const createHumanStore = () => {
});
},
setCurrentPhase: (modelUuid, phase) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentPhase = phase;
}
});
},
addCurrentAction: (modelUuid, actionUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
const action = human.point.actions.find(h => h.actionUuid === actionUuid);
if (action) {
human.currentAction = {
actionUuid: action.actionUuid,
actionName: action.actionName
};
}
}
});
},
removeCurrentAction: (modelUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentAction = undefined;
}
});
},
setHumanActive: (modelUuid, isActive) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
@@ -137,6 +182,33 @@ export const createHumanStore = () => {
});
},
incrementLoadCount: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount += incrementBy;
}
});
},
decrementLoadCount: (modelUuid, decrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount -= decrementBy;
}
});
},
clearLoadCount: (modelUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount = 0;
}
});
},
addCurrentMaterial: (modelUuid, materialType, materialId) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);

View File

@@ -36,6 +36,8 @@ type MaterialsStore = {
} | null
) => MaterialSchema | undefined;
clearLocations: (materialId: string) => void;
setMaterial: (materialId: string, materialType: string) => MaterialSchema | undefined;
setStartTime: (materialId: string, startTime: number) => MaterialSchema | undefined;
setEndTime: (materialId: string, endTime: number) => MaterialSchema | undefined;
@@ -140,6 +142,18 @@ export const createMaterialStore = () => {
return updatedMaterial;
},
clearLocations: (materialId) => {
let updatedMaterial: MaterialSchema | undefined;
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) {
delete material.previous;
delete material.next;
updatedMaterial = JSON.parse(JSON.stringify(material));
}
});
return updatedMaterial;
},
setMaterial: (materialId, materialType) => {
let updatedMaterial: MaterialSchema | undefined;
set((state) => {

View File

@@ -32,13 +32,13 @@ type ProductsStore = {
productUuid: string,
modelUuid: string,
pointUuid: string,
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]
) => EventsSchema | undefined;
removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
updateAction: (
productUuid: string,
actionUuid: string,
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']>
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]>
) => EventsSchema | undefined;
// Trigger-level actionss
@@ -66,8 +66,8 @@ type ProductsStore = {
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema | undefined;
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']) | undefined;
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']) | undefined;
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]) | undefined;
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]) | undefined;
getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;

View File

@@ -203,8 +203,8 @@ export const useSelectedAnimation = create<SelectedAnimationState>()(
);
interface IsDraggingState {
isDragging: "start" | "end" | null;
setIsDragging: (state: "start" | "end" | null) => void;
isDragging: "start" | "end" | "assembly" | null;
setIsDragging: (state: "start" | "end" | "assembly" | null) => void;
}
export const useIsDragging = create<IsDraggingState>()(
@@ -219,8 +219,8 @@ export const useIsDragging = create<IsDraggingState>()(
);
interface IsRotatingState {
isRotating: "start" | "end" | null;
setIsRotating: (state: "start" | "end" | null) => void;
isRotating: "start" | "end" | "assembly" | null;
setIsRotating: (state: "start" | "end" | "assembly" | null) => void;
}
export const useIsRotating = create<IsRotatingState>()(

View File

@@ -10,6 +10,9 @@ interface VehiclesStore {
modelUuid: string,
updates: Partial<Omit<VehicleStatus, "modelUuid" | "productUuid">>
) => void;
addPathPoint: (modelUuid: string, pathKey: keyof VehicleAction["paths"], point: VehicleAction["paths"]["initPickup"][number]) => VehicleAction["paths"];
updatePathPoint: (modelUuid: string, pathKey: keyof VehicleAction["paths"], pointId: string, updates: Partial<VehicleAction["paths"]["initPickup"][number]>) => VehicleAction["paths"];
deletePathPoint: (modelUuid: string, pathKey: keyof VehicleAction["paths"], pointId: string) => VehicleAction["paths"];
clearVehicles: () => void;
setVehicleActive: (modelUuid: string, isActive: boolean) => void;
@@ -79,6 +82,48 @@ export const createVehicleStore = () => {
});
},
addPathPoint: (modelUuid, pathKey, point) => {
let updatedPaths: VehicleAction["paths"] = { initPickup: [], pickupDrop: [], dropPickup: [] };
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
const path = vehicle.point.action.paths[pathKey];
path.push(point);
updatedPaths = vehicle.point.action.paths;
}
});
return updatedPaths;
},
updatePathPoint: (modelUuid, pathKey, pointId, updates) => {
let updatedPaths: VehicleAction["paths"] = { initPickup: [], pickupDrop: [], dropPickup: [] };
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
const path = vehicle.point.action.paths[pathKey];
const index = path.findIndex(p => p.pointId === pointId);
if (index !== -1) {
Object.assign(path[index], updates);
updatedPaths = vehicle.point.action.paths;
}
}
});
return updatedPaths;
},
deletePathPoint: (modelUuid, pathKey, pointId) => {
let updatedPaths: VehicleAction["paths"] = { initPickup: [], pickupDrop: [], dropPickup: [] };
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
const path = vehicle.point.action.paths[pathKey];
vehicle.point.action.paths[pathKey] = path.filter(p => p.pointId !== pointId);
updatedPaths = vehicle.point.action.paths;
}
});
return updatedPaths;
},
clearVehicles: () => {
set((state) => {
state.vehicles = [];

View File

@@ -24,3 +24,30 @@ export const usePlayerStore = create<PlayerState>((set) => ({
hidePlayer: false, // initial state
setHidePlayer: (hide) => set({ hidePlayer: hide }), // state updater
}));
interface MouseNoteState {
Leftnote: string;
Middlenote: string;
Rightnote: string;
setNotes: (notes: {
Leftnote: string;
Middlenote: string;
Rightnote: string;
}) => void;
setLeftnote: (note: string) => void;
setMiddlenote: (note: string) => void;
setRightnote: (note: string) => void;
resetNotes: () => void;
}
export const useMouseNoteStore = create<MouseNoteState>((set) => ({
Leftnote: '',
Middlenote: '',
Rightnote: '',
setNotes: (notes) => set(notes),
setLeftnote: (note) => set({ Leftnote: note }),
setMiddlenote: (note) => set({ Middlenote: note }),
setRightnote: (note) => set({ Rightnote: note }),
resetNotes: () =>
set({ Leftnote: '', Middlenote: '', Rightnote: '' }),
}));

View File

@@ -1,6 +1,7 @@
$cursor-default: url("../../assets/cursors/default.svg") 0 0, default;
$cursor-pen: url("../../assets/cursors/pen.svg") 0 0, default;
$cursor-delete: url("../../assets/cursors/pointing.svg") 4 0, default;
$cursor-comment: url("../../assets/cursors/comment.svg") 0 16, default;
$cursor-draw: url("../../assets/cursors/cell.svg") 8 8, default;
$cursor-cross: url("../../assets/cursors/cross.svg") 8 8, default;
$cursor-grab: url("../../assets/cursors/open.svg") 8 8, default;
@@ -45,3 +46,15 @@ $cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default;
}
}
.scene-container.hand-closed {
canvas {
cursor: $cursor-grabing !important;
}
}
.scene-container.comment {
canvas {
cursor: $cursor-comment !important;
}
}

View File

@@ -40,6 +40,29 @@ interface VehicleAction {
steeringAngle: number;
pickUpPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
unLoadPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
paths: {
initPickup: {
pointId: string;
position: [number, number, number];
isCurved: boolean;
handleA: [number, number, number] | null;
handleB: [number, number, number] | null;
}[],
pickupDrop: {
pointId: string;
position: [number, number, number];
isCurved: boolean;
handleA: [number, number, number] | null;
handleB: [number, number, number] | null;
}[],
dropPickup: {
pointId: string;
position: [number, number, number];
isCurved: boolean;
handleA: [number, number, number] | null;
handleB: [number, number, number] | null;
}[],
}
triggers: TriggerSchema[];
}
@@ -73,11 +96,12 @@ interface HumanAction {
actionUuid: string;
actionName: string;
actionType: "worker" | "assembly";
processTime?: number;
processTime: number;
swapMaterial?: string;
assemblyPoint?: { rotation: [number, number, number] | null; }
assemblyPoint?: { 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; }
loadCount: number;
loadCapacity: number;
triggers: TriggerSchema[];
}
@@ -124,7 +148,7 @@ interface HumanPointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: HumanAction;
actions: HumanAction[];
}
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema;
@@ -223,19 +247,18 @@ interface StorageUnitStatus extends StorageEventSchema {
interface HumanStatus extends HumanEventSchema {
productUuid: string;
currentPhase: string;
isActive: boolean;
isScheduled: boolean;
idleTime: number;
activeTime: number;
totalLoadCount: number;
currentLoad: number;
currentMaterials: { materialType: string; materialId: string; }[];
distanceTraveled: number;
currentAction?: {
actionUuid: string;
actionName: string;
animationUuid: string;
materialType?: string | null;
materialId?: string | null;
};
}
@@ -306,4 +329,4 @@ type IK = {
maxDistance?: number;
maxheight?: number;
minheight?: number;
} ;
};

View File

@@ -3,14 +3,20 @@ export const handleCanvasCursors = (name: string) => {
if (!canvas) return;
const cursorMap: Record<string, string> = {
default: '',
'draw-wall': 'draw',
'draw-aisle': 'draw',
'draw-zone': 'draw',
'draw-floor': 'draw',
measure: 'measure',
delete: 'pointer',
pointer: 'pointer',
grab: 'hand',
grabbing: 'hand-closed',
pen: 'pen',
'free-hand': 'hand',
move: 'move',
comment: 'comment',
// Add more mappings as needed
};

View File

@@ -0,0 +1,45 @@
import { useMouseNoteStore } from "../../store/useUIToggleStore";
const actionNotes: Record<string, string> = {
'left+CONTROL': 'Box Select',
'left+SHIFT': 'Multi Select',
'middle+CONTROL': 'Zoom In',
};
export function mouseActionHelper() {
const setNotes = useMouseNoteStore.getState().setNotes;
const activeKeys = new Set<string>();
function updateNotesFromKeys() {
const sortedKeys = Array.from(activeKeys).sort();
const leftKey = ['left', ...sortedKeys].join('+');
const middleKey = ['middle', ...sortedKeys].join('+');
const rightKey = ['right', ...sortedKeys].join('+');
setNotes({
Leftnote: actionNotes[leftKey] || '',
Middlenote: actionNotes[middleKey] || '',
Rightnote: actionNotes[rightKey] || '',
});
}
function handleKeyDown(event: KeyboardEvent) {
activeKeys.add(event.key.toUpperCase());
updateNotesFromKeys();
}
function handleKeyUp(event: KeyboardEvent) {
activeKeys.delete(event.key.toUpperCase());
updateNotesFromKeys();
}
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
};
}

View File

@@ -53,7 +53,7 @@ const KeyPressListener: React.FC = () => {
const { setCreateNewVersion } = useVersionHistoryStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setSelectedComment } = useSelectedComment();
const { setDfxUploaded, setDxfWallGenerate } = useDfxUpload();
const { setDfxUploaded } = useDfxUpload();
const isTextInput = (element: Element | null): boolean =>
element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
@@ -128,6 +128,9 @@ const KeyPressListener: React.FC = () => {
setActiveTool("measure");
setToolMode("MeasurementScale");
}
if (key === "C") {
setActiveTool("comment");
}
};
const handleSidebarShortcuts = (key: string) => {