feat: Refactor human action handling to support animated travel and streamline action structure

This commit is contained in:
2025-07-03 12:11:58 +05:30
parent b5c69f3335
commit 98f4d48db2
10 changed files with 75 additions and 282 deletions

View File

@@ -1,31 +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 { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import InputToggle from "../../../../../ui/inputs/InputToggle";
import ActionsList from "../components/ActionsList";
function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"animation" | "animatedTravel">("animation");
const [activeAnimationOption, setActiveAnimationOption] = useState("");
const [animationOptions, setAnimationOptions] = useState<string[]>([]);
const [activeOption, setActiveOption] = useState<"animatedTravel">("animatedTravel");
const [speed, setSpeed] = useState("0.5");
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
const [isLoopAnimation, setIsLoopAnimation] = useState(false);
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore, assetStore } = useSceneContext();
const { getAssetById } = assetStore();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction } = productStore();
const { productStore } = useSceneContext();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
@@ -41,15 +35,10 @@ function HumanMechanics() {
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.actions) {
if (point?.action) {
setSelectedPointData(point);
if (point.actions.length > 0) {
setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName);
const asset = getAssetById(selectedEventData.data.modelUuid);
if (asset && asset.animations) {
setAnimationOptions(asset.animations)
}
}
const action = point.action;
setSelectedAction(action.actionUuid, action.actionName);
}
} else {
clearSelectedAction();
@@ -73,15 +62,11 @@ function HumanMechanics() {
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.actions) {
if (point?.action) {
setSelectedPointData(point);
const action = point.actions.find((a) => a.actionUuid === selectedAction.actionId);
if (action) {
setCurrentAction(action);
setIsLoopAnimation(action.loopAnimation ?? false);
setActiveOption(action.actionType as "animation" | "animatedTravel");
setActiveAnimationOption(action.animation || '')
}
const action = point.action;
setCurrentAction(action);
setActiveOption(action.actionType as "animatedTravel");
}
} else {
clearSelectedAction();
@@ -106,73 +91,18 @@ function HumanMechanics() {
};
const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId) return;
setActiveOption(actionType as "animation" | "animatedTravel");
if (!currentAction) return;
setActiveOption(actionType as "animatedTravel");
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ actionType: actionType as "animation" | "animatedTravel" }
currentAction.actionUuid,
{ actionType: actionType as "animatedTravel" }
);
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionType: actionType as "animation" | "animatedTravel" }
: action
);
setSelectedPointData({ ...selectedPointData, actions: updatedActions });
}
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
}
const handleSelectAnimation = (animationOption: string) => {
if (!selectedAction.actionId) return;
setActiveAnimationOption(animationOption);
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ animation: animationOption }
);
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, animation: animationOption }
: action
);
setSelectedPointData({ ...selectedPointData, actions: updatedActions });
}
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
}
const handleLoopAnimationChange = () => {
if (!selectedAction.actionId || !currentAction) return;
const updatedValue = !isLoopAnimation;
setIsLoopAnimation(updatedValue);
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ loopAnimation: updatedValue }
);
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, loopAnimation: updatedValue }
: action
);
setSelectedPointData({ ...selectedPointData, actions: updatedActions });
setCurrentAction(updatedActions.find((a) => a.actionUuid === selectedAction.actionId));
if (event && selectedPointData) {
const updatedAction = { ...selectedPointData.action, actionType: actionType as "animatedTravel" };
setSelectedPointData({ ...selectedPointData, action: updatedAction });
}
if (event) {
@@ -181,20 +111,17 @@ function HumanMechanics() {
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
if (!currentAction) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
currentAction.actionUuid,
{ actionName: newName }
);
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({ ...selectedPointData, actions: updatedActions });
if (event && selectedPointData) {
const updatedAction = { ...selectedPointData.action, actionName: newName };
setSelectedPointData({ ...selectedPointData, action: updatedAction });
}
if (event) {
@@ -217,11 +144,11 @@ function HumanMechanics() {
};
const handleClearPoints = () => {
if (!selectedAction.actionId || !selectedPointData) return;
if (!currentAction) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
currentAction.actionUuid,
{
travelPoints: {
startPoint: null,
@@ -235,66 +162,9 @@ function HumanMechanics() {
}
};
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction: HumanAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "animation",
animation: null,
loadCapacity: 1,
loopAnimation: true,
travelPoints: {
startPoint: null,
endPoint: null,
},
triggers: [],
};
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, actions: [...selectedPointData.actions, newAction] };
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
const event = removeAction(selectedProduct.productUuid, actionUuid);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === actionUuid);
const newActions = selectedPointData.actions.filter((a) => a.actionUuid !== actionUuid);
const updatedPoint = { ...selectedPointData, actions: newActions };
setSelectedPointData(updatedPoint);
if (selectedAction.actionId === actionUuid) {
const nextAction = newActions[index] || newActions[index - 1];
if (nextAction) {
setSelectedAction(nextAction.actionUuid, nextAction.actionName);
} else {
clearSelectedAction();
}
}
};
const availableActions = {
defaultOption: "animatedTravel",
options: ["animation", "animatedTravel"],
options: ["animatedTravel"],
};
return (
@@ -316,19 +186,20 @@ function HumanMechanics() {
</div>
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
{selectedAction.actionId && currentAction && (
{currentAction && (
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction={false}
handleAddAction={() => { }}
handleDeleteAction={() => { }}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName || ""}
value={currentAction.actionName || ""}
onRename={handleRenameAction}
/>
</div>
@@ -340,32 +211,19 @@ function HumanMechanics() {
onSelect={handleSelectActionType}
disabled={true}
/>
<LabledDropdown
label="Animation"
defaultOption={activeAnimationOption}
options={animationOptions}
onSelect={handleSelectAnimation}
disabled={true}
/>
<InputToggle
value={isLoopAnimation}
inputKey=""
label="Loop Animation"
onClick={handleLoopAnimationChange}
/>
{activeOption === 'animatedTravel' &&
{activeOption === 'animatedTravel' && (
<PickAndPlaceAction clearPoints={handleClearPoints} />
}
)}
</div>
<div className="tirgger">
<Trigger
selectedPointData={selectedPointData as any}
type={"Human"}
type="Human"
/>
</div>
</div>
)}
</section>
</section>
)}
</>
);
}