setIsLogListVisible(false)}
+ >
+ {/* eslint-disable-next-line */}
+
{
+ e.stopPropagation();
+ }}
>
-
-
-
-
- {/* Tabs */}
-
-
- {["all", "info", "warning", "error"].map((type) => (
-
- ))}
-
-
-
-
- {/* Log Entries */}
-
- {filteredLogs.length > 0 ? (
- filteredLogs.map((log) => (
-
-
{GetLogIcon(log.type)}
-
-
{log.message}
-
- {formatTimestamp(log.timestamp)}
-
+
+
+
+
+
- ))
- ) : (
-
- There are no logs to display at the moment.
- )}
+
+ {/* Logs Section */}
+
+
-
-
+ ) : (
+
{
+ setOpen(false);
+ setIsLogListVisible(false);
+ }}
+ >
+
+
+
+
+ )}
+ >
);
};
diff --git a/app/src/components/ui/menu/contextMenu.tsx b/app/src/components/ui/menu/contextMenu.tsx
index 9069da8..db98c21 100644
--- a/app/src/components/ui/menu/contextMenu.tsx
+++ b/app/src/components/ui/menu/contextMenu.tsx
@@ -1,187 +1,216 @@
import React from "react";
-import { ArrayIcon, CopyIcon, DeleteIcon, DublicateIcon, FlipXAxisIcon, FlipZAxisIcon, FocusIcon, GroupIcon, ModifiersIcon, MoveIcon, PasteIcon, RenameIcon, RotateIcon, SubMenuIcon, TransformIcon } from "../../icons/ContextMenuIcons";
+import {
+ ArrayIcon,
+ CopyIcon,
+ DeleteIcon,
+ DublicateIcon,
+ FlipXAxisIcon,
+ FlipZAxisIcon,
+ FocusIcon,
+ GroupIcon,
+ ModifiersIcon,
+ MoveIcon,
+ PasteIcon,
+ RenameIcon,
+ RotateIcon,
+ SubMenuIcon,
+ TransformIcon,
+} from "../../icons/ContextMenuIcons";
+
+// Union of valid visibility keys
+type VisibilityKey =
+ | "rename"
+ | "focus"
+ | "flipX"
+ | "flipZ"
+ | "move"
+ | "rotate"
+ | "duplicate"
+ | "copy"
+ | "paste"
+ | "modifier"
+ | "group"
+ | "array"
+ | "delete"
+ | "transform" // virtual key for submenu
+ | "groupArray"; // virtual key for submenu
type ContextMenuProps = {
- visibility: {
- rename: boolean;
- focus: boolean;
- flipX: boolean;
- flipZ: boolean;
- move: boolean;
- rotate: boolean;
- duplicate: boolean;
- copy: boolean;
- paste: boolean;
- modifier: boolean;
- group: boolean;
- array: boolean;
- delete: boolean;
- };
- onRename: () => void;
- onFocus: () => void;
- onFlipX: () => void;
- onFlipZ: () => void;
- onMove: () => void;
- onRotate: () => void;
- onDuplicate: () => void;
- onCopy: () => void;
- onPaste: () => void;
- onGroup: () => void;
- onArray: () => void;
- onDelete: () => void;
+ visibility: Record<
+ Exclude
,
+ boolean
+ >;
+ onRename: () => void;
+ onFocus: () => void;
+ onFlipX: () => void;
+ onFlipZ: () => void;
+ onMove: () => void;
+ onRotate: () => void;
+ onDuplicate: () => void;
+ onCopy: () => void;
+ onPaste: () => void;
+ onGroup: () => void;
+ onArray: () => void;
+ onDelete: () => void;
+};
+
+type MenuItem = {
+ key: VisibilityKey;
+ label: string;
+ icon?: React.ReactNode;
+ shortcut?: string;
+ onClick?: () => void;
+ submenuIcon?: React.ReactNode;
+ children?: MenuItem[];
};
const ContextMenu: React.FC = ({
- visibility,
- onRename,
- onFocus,
- onFlipX,
- onFlipZ,
- onMove,
- onRotate,
- onDuplicate,
- onCopy,
- onPaste,
- onGroup,
- onArray,
- onDelete,
+ visibility,
+ onRename,
+ onFocus,
+ onFlipX,
+ onFlipZ,
+ onMove,
+ onRotate,
+ onDuplicate,
+ onCopy,
+ onPaste,
+ onGroup,
+ onArray,
+ onDelete,
}) => {
+ const items: MenuItem[] = [
+ {
+ key: "rename",
+ label: "Rename",
+ icon: ,
+ shortcut: "F2",
+ onClick: onRename,
+ },
+ {
+ key: "focus",
+ label: "Focus",
+ icon: ,
+ shortcut: "F",
+ onClick: onFocus,
+ },
+ {
+ key: "flipX",
+ label: "Flip to X axis",
+ icon: ,
+ onClick: onFlipX,
+ },
+ {
+ key: "flipZ",
+ label: "Flip to Z axis",
+ icon: ,
+ onClick: onFlipZ,
+ },
+ {
+ key: "transform",
+ label: "Transform",
+ icon: ,
+ submenuIcon: ,
+ children: [
+ {
+ key: "move",
+ label: "Move",
+ icon: ,
+ shortcut: "G",
+ onClick: onMove,
+ },
+ {
+ key: "rotate",
+ label: "Rotate",
+ icon: ,
+ shortcut: "R",
+ onClick: onRotate,
+ },
+ ],
+ },
+ {
+ key: "duplicate",
+ label: "Duplicate",
+ icon: ,
+ shortcut: "Ctrl + D",
+ onClick: onDuplicate,
+ },
+ {
+ key: "copy",
+ label: "Copy Objects",
+ icon: ,
+ shortcut: "Ctrl + C",
+ onClick: onCopy,
+ },
+ {
+ key: "paste",
+ label: "Paste Objects",
+ icon: ,
+ shortcut: "Ctrl + V",
+ onClick: onPaste,
+ },
+ { key: "modifier", label: "Modifiers", icon: },
+ {
+ key: "groupArray",
+ label: "Group / Array",
+ children: [
+ {
+ key: "group",
+ label: "Group",
+ icon: ,
+ shortcut: "Ctrl + G",
+ onClick: onGroup,
+ },
+ { key: "array", label: "Array", icon: , onClick: onArray },
+ ],
+ },
+ {
+ key: "delete",
+ label: "Delete",
+ icon: ,
+ shortcut: "X",
+ onClick: onDelete,
+ },
+ ];
+ const renderItem = (item: MenuItem): React.ReactNode => {
+ if (item.children) {
+ const children = item.children.filter(
+ (child) => visibility[child.key as keyof typeof visibility]
+ );
+ if (!children.length) return null;
+ return (
+
+
+
{children.map(renderItem)}
+
+ );
+ }
+
+ if (!visibility[item.key as keyof typeof visibility]) return null;
return (
-
- {visibility.rename && (
-
- )}
- {visibility.focus && (
-
- )}
- {visibility.flipX && (
-
- )}
- {visibility.flipZ && (
-
- )}
- {(visibility.move || visibility.rotate) && (
-
-
-
-
- {visibility.move && (
-
- )}
- {visibility.rotate && (
-
- )}
-
-
- )}
- {visibility.duplicate && (
-
-
-
Ctrl + D
-
- )}
- {visibility.copy && (
-
-
-
Ctrl + C
-
- )}
- {visibility.paste && (
-
-
-
Ctrl + V
-
- )}
- {visibility.modifier && (
-
- )}
- {(visibility.group || visibility.array) && (
-
-
-
- {visibility.group && (
-
-
- Ctrl + G
-
- )}
- {visibility.array && (
-
- )}
-
-
- )}
- {visibility.delete && (
-
- )}
-
+
+
+ {item.shortcut &&
{item.shortcut}}
+
);
+ };
+
+ return {items.map(renderItem)}
;
};
export default ContextMenu;
diff --git a/app/src/hooks/useCameraShortcuts.ts b/app/src/hooks/useCameraShortcuts.ts
new file mode 100644
index 0000000..e4b897a
--- /dev/null
+++ b/app/src/hooks/useCameraShortcuts.ts
@@ -0,0 +1,47 @@
+import { useEffect } from "react";
+import { useThree } from "@react-three/fiber";
+import * as THREE from "three";
+import type { CameraControls } from "@react-three/drei";
+
+export const useCameraShortcuts = (controlsRef: React.RefObject) => {
+ const { camera } = useThree();
+
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (!controlsRef.current) return;
+
+ // get current distance from camera to target
+ const target = new THREE.Vector3();
+ controlsRef.current.getTarget(target);
+
+ const distance = camera.position.distanceTo(target);
+ let pos: THREE.Vector3 | null = null;
+
+ switch (e.key) {
+ case "1": // Front
+ pos = new THREE.Vector3(0, 0, distance).add(target);
+ break;
+ case "3": // Right
+ pos = new THREE.Vector3(distance, 0, 0).add(target);
+ break;
+ case "7": // Top
+ pos = new THREE.Vector3(0, distance, 0).add(target);
+ break;
+ case "9": // Back
+ pos = new THREE.Vector3(0, 0, -distance).add(target);
+ break;
+ }
+
+ if (pos) {
+ controlsRef.current.setLookAt(
+ pos.x, pos.y, pos.z, // camera position
+ target.x, target.y, target.z, // keep same target
+ true // smooth transition
+ );
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [controlsRef, camera]);
+};
diff --git a/app/src/modules/builder/asset/assetsGroup.tsx b/app/src/modules/builder/asset/assetsGroup.tsx
index 072c9a7..9d36efc 100644
--- a/app/src/modules/builder/asset/assetsGroup.tsx
+++ b/app/src/modules/builder/asset/assetsGroup.tsx
@@ -232,18 +232,22 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "storageUnit",
+ storageCapacity: 10,
+ storageCount: 10,
+ materialType: "Default material",
subType: item.eventData.subType || '',
point: {
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: "store",
- storageCapacity: 10,
- triggers: []
- }
+ actions: [
+ {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Action 1",
+ actionType: "store",
+ triggers: []
+ }
+ ]
}
};
addEvent(storageEvent);
@@ -268,6 +272,11 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
actionType: "worker",
loadCount: 1,
assemblyCount: 1,
+ assemblyCondition: {
+ conditionType: 'material',
+ materialType: "Default material"
+ },
+ manufactureCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
diff --git a/app/src/modules/builder/asset/functions/addAssetModel.ts b/app/src/modules/builder/asset/functions/addAssetModel.ts
index c64064a..3eac4a2 100644
--- a/app/src/modules/builder/asset/functions/addAssetModel.ts
+++ b/app/src/modules/builder/asset/functions/addAssetModel.ts
@@ -345,18 +345,22 @@ async function handleModelLoad(
rotation: newFloorItem.rotation,
state: "idle",
type: "storageUnit",
+ storageCapacity: 10,
+ storageCount: 10,
+ materialType: "Default material",
subType: selectedItem.subType || '',
point: {
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: "store",
- storageCapacity: 10,
- triggers: [],
- },
+ actions: [
+ {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Action 1",
+ actionType: "store",
+ triggers: [],
+ }
+ ],
},
};
addEvent(storageEvent);
@@ -386,6 +390,11 @@ async function handleModelLoad(
actionType: "worker",
loadCount: 1,
assemblyCount: 1,
+ assemblyCondition: {
+ conditionType: 'material',
+ materialType: "Default material"
+ },
+ manufactureCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
diff --git a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts
index 1fc26ae..360c513 100644
--- a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts
+++ b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts
@@ -3,7 +3,7 @@ import { CameraControls } from '@react-three/drei';
import { ThreeEvent, useThree } from '@react-three/fiber';
import { useCallback, useEffect, useRef } from 'react';
-import { useActiveTool, useDeletableFloorItem, useSelectedFloorItem, useToggleView } from '../../../../../../store/builder/store';
+import { useActiveTool, useDeletableFloorItem, useResourceManagementId, useSelectedFloorItem, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
import { useSocketStore } from '../../../../../../store/builder/store';
import { useSceneContext } from '../../../../../scene/sceneContext';
@@ -20,9 +20,11 @@ import { upsertProductOrEventApi } from '../../../../../../services/simulation/p
export function useModelEventHandlers({
boundingBox,
groupRef,
+ asset
}: {
boundingBox: THREE.Box3 | null,
groupRef: React.RefObject,
+ asset: Asset
}) {
const { controls, gl, camera } = useThree();
const { activeTool } = useActiveTool();
@@ -32,12 +34,14 @@ export function useModelEventHandlers({
const { socket } = useSocketStore();
const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
const { push3D } = undoRedo3DStore();
- const { removeAsset } = assetStore();
+ const { getAssetById, removeAsset } = assetStore();
+ const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
+ const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
const { removeEvent, getEventByModelUuid } = eventStore();
const { getIsEventInProduct, addPoint, deleteEvent } = productStore();
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
- const { setSelectedFloorItem } = useSelectedFloorItem();
+ const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedVersionStore } = useVersionContext();
@@ -66,11 +70,32 @@ export function useModelEventHandlers({
});
};
+ useEffect(() => {
+ if (!zoneAssetId) return
+ if (zoneAssetId.id === asset.modelUuid) {
+ handleDblClick(asset);
+ }
+
+ }, [zoneAssetId])
+ useEffect(() => {
+ if (!resourceManagementId) return
+ if (resourceManagementId === asset.modelUuid) {
+
+
+ handleDblClick(asset);
+ }
+
+ }, [resourceManagementId])
+
+ useEffect(() => {
+ if (!selectedFloorItem) {
+ setZoneAssetId(null);
+ }
+ }, [selectedFloorItem])
+
const handleDblClick = (asset: Asset) => {
- if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
-
+ if (asset && activeTool === "cursor" && boundingBox && groupRef.current && (activeModule === 'builder' || (activeModule === 'simulation' && resourceManagementId))) {
const frontView = false;
-
if (frontView) {
const size = boundingBox.getSize(new THREE.Vector3());
const center = boundingBox.getCenter(new THREE.Vector3());
@@ -91,6 +116,7 @@ export function useModelEventHandlers({
paddingBottom: 5,
paddingRight: 5,
});
+
} else {
const collisionPos = new THREE.Vector3();
@@ -111,6 +137,7 @@ export function useModelEventHandlers({
}
setSelectedFloorItem(groupRef.current);
+ setResourceManagementId("");
}
};
diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx
index 9d8c0a3..ecce40e 100644
--- a/app/src/modules/builder/asset/models/model/model.tsx
+++ b/app/src/modules/builder/asset/models/model/model.tsx
@@ -159,7 +159,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
});
}, []);
- const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef });
+ const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef, asset });
return (
{
if (!toggleView) {
setHoveredLine(null);
diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx
index 419c1d8..aa60691 100644
--- a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx
+++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx
@@ -1,13 +1,5 @@
import { useMemo } from "react";
-import {
- Shape,
- Vector2,
- DoubleSide,
- TextureLoader,
- RepeatWrapping,
- SRGBColorSpace,
- NoColorSpace,
-} from "three";
+import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three";
import { useLoader } from "@react-three/fiber";
import { Extrude } from "@react-three/drei";
import useModuleStore from "../../../../../store/useModuleStore";
@@ -36,212 +28,188 @@ import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal
import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png";
function FloorInstance({ floor }: { floor: Floor }) {
- const { togglView } = useToggleView();
- const { activeModule } = useModuleStore();
- const { selectedFloor, setSelectedFloor, setSelectedDecal } =
- useBuilderStore();
- const savedTheme = localStorage.getItem("theme");
+ const { togglView } = useToggleView();
+ const { activeModule } = useModuleStore();
+ const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
+ const savedTheme = localStorage.getItem("theme");
- const materials: Record<
- string,
- {
- map: string;
- roughnessMap?: string;
- metalnessMap?: string;
- normalMap?: string;
- textureTileScale?: [number, number];
+ const materials: Record<
+ string,
+ {
+ map: string;
+ roughnessMap?: string;
+ metalnessMap?: string;
+ normalMap?: string;
+ textureTileScale?: [number, number];
+ }
+ > = {
+ "Default Material": { map: savedTheme === "dark" ? texturePathDark : texturePath, },
+ "Material 1": { map: material1 },
+ "Material 2": {
+ map: material2Map,
+ roughnessMap: material2MetalicRoughnessMap,
+ metalnessMap: material2MetalicRoughnessMap,
+ normalMap: material2NormalMap,
+ textureTileScale: [0.1, 0.1],
+ },
+ "Material 3": {
+ map: material3Map,
+ roughnessMap: material3MetalicRoughnessMap,
+ metalnessMap: material3MetalicRoughnessMap,
+ normalMap: material3NormalMap,
+ textureTileScale: [0.35, 0.5],
+ },
+ "Material 4": {
+ map: material4Map,
+ roughnessMap: material4RoughnessMap,
+ metalnessMap: material4MetalicMap,
+ normalMap: material4NormalMap,
+ },
+ };
+
+ const shape = useMemo(() => {
+ const shape = new Shape();
+ const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
+ if (points.length < 3) return null;
+ shape.moveTo(points[0].x, points[0].y);
+ for (let i = 1; i < points.length; i++) {
+ shape.lineTo(points[i].x, points[i].y);
+ }
+ return shape;
+ }, [floor]);
+
+ const textureScale = Constants.floorConfig.textureScale;
+
+ // Helper function to handle texture maps and filter out null values
+ function getMaterialMaps(material: any, defaultMap: any) {
+ const materialMap = material.map || defaultMap;
+ const normalMap = material.normalMap || null;
+ const roughnessMap = material.roughnessMap || null;
+ const metalnessMap = material.metalnessMap || null;
+
+ return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null);
}
- > = {
- "Default Material": {
- map: savedTheme === "dark" ? texturePathDark : texturePath,
- },
- "Material 1": {
- map: material1,
- },
- "Material 2": {
- map: material2Map,
- roughnessMap: material2MetalicRoughnessMap,
- metalnessMap: material2MetalicRoughnessMap,
- normalMap: material2NormalMap,
- textureTileScale: [0.1, 0.1],
- },
- "Material 3": {
- map: material3Map,
- roughnessMap: material3MetalicRoughnessMap,
- metalnessMap: material3MetalicRoughnessMap,
- normalMap: material3NormalMap,
- textureTileScale: [0.35, 0.5],
- },
- "Material 4": {
- map: material4Map,
- roughnessMap: material4RoughnessMap,
- metalnessMap: material4MetalicMap,
- normalMap: material4NormalMap,
- },
- };
- const shape = useMemo(() => {
- const shape = new Shape();
- const points = floor.points.map(
- (p) => new Vector2(p.position[0], p.position[2])
- );
- if (points.length < 3) return null;
- shape.moveTo(points[0].x, points[0].y);
- for (let i = 1; i < points.length; i++) {
- shape.lineTo(points[i].x, points[i].y);
- }
- return shape;
- }, [floor]);
+ // Default material map
+ const defaultMaterialMap = materials["Default Material"].map;
- const textureScale = Constants.floorConfig.textureScale;
+ // Get top and side material maps
+ const topMaterial = materials[floor.topMaterial];
+ const sideMaterial = materials[floor.sideMaterial];
- // Helper function to handle texture maps and filter out null values
- function getMaterialMaps(material: any, defaultMap: any) {
- const materialMap = material.map || defaultMap;
- const normalMap = material.normalMap || null;
- const roughnessMap = material.roughnessMap || null;
- const metalnessMap = material.metalnessMap || null;
+ // Get the filtered lists for top and side textures
+ const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
+ const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
- return [materialMap, normalMap, roughnessMap, metalnessMap].filter(
- (texture): texture is string => texture !== null
- );
- }
+ // Use loader to load top and side textures
+ const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList);
- // Default material map
- const defaultMaterialMap = materials["Default Material"].map;
+ const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList);
- // Get top and side material maps
- const topMaterial = materials[floor.topMaterial];
- const sideMaterial = materials[floor.sideMaterial];
+ // Early exit if materials are missing
+ if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;
- // Get the filtered lists for top and side textures
- const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
- const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
-
- // Use loader to load top and side textures
- const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] =
- useLoader(TextureLoader, topTexturesList);
-
- const [
- sideTexture,
- sideNormalTexture,
- sideRoughnessTexture,
- sideMetalicTexture,
- ] = useLoader(TextureLoader, sideTexturesList);
-
- // Early exit if materials are missing
- if (!materials[floor.topMaterial] || !materials[floor.sideMaterial])
- return null;
-
- // Combine and pair textures with their corresponding material
- const textureMaterialMap = [
- {
- textures: [
- topTexture,
- topNormalTexture,
- topRoughnessTexture,
- topMetalicTexture,
- ],
- materialKey: floor.topMaterial,
- },
- {
- textures: [
- sideTexture,
- sideNormalTexture,
- sideRoughnessTexture,
- sideMetalicTexture,
- ],
- materialKey: floor.sideMaterial,
- },
- ];
-
- // Apply texture settings
- textureMaterialMap.forEach(({ textures, materialKey }) => {
- const tileScale = materials[materialKey]?.textureTileScale ?? [
- textureScale,
- textureScale,
+ // Combine and pair textures with their corresponding material
+ const textureMaterialMap = [
+ {
+ textures: [
+ topTexture,
+ topNormalTexture,
+ topRoughnessTexture,
+ topMetalicTexture,
+ ],
+ materialKey: floor.topMaterial,
+ },
+ {
+ textures: [
+ sideTexture,
+ sideNormalTexture,
+ sideRoughnessTexture,
+ sideMetalicTexture,
+ ],
+ materialKey: floor.sideMaterial,
+ },
];
- textures.forEach((tex, idx) => {
- if (!tex) return;
- tex.wrapS = tex.wrapT = RepeatWrapping;
- tex.repeat.set(tileScale[0], tileScale[1]);
- tex.anisotropy = 16;
- // First texture is always the color map (use SRGB), others should be linear
- tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
+ // Apply texture settings
+ textureMaterialMap.forEach(({ textures, materialKey }) => {
+ const tileScale = materials[materialKey]?.textureTileScale ?? [
+ textureScale,
+ textureScale,
+ ];
+
+ textures.forEach((tex, idx) => {
+ if (!tex) return;
+ tex.wrapS = tex.wrapT = RepeatWrapping;
+ tex.repeat.set(tileScale[0], tileScale[1]);
+ tex.anisotropy = 16;
+ // First texture is always the color map (use SRGB), others should be linear
+ tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
+ });
});
- });
- if (!shape) return null;
+ if (!shape) return null;
- return (
- {
- if (!togglView && activeModule === "builder") {
- if (e.object.userData.floorUuid) {
- e.stopPropagation();
- setSelectedFloor(e.object);
- setSelectedDecal(null);
- }
- }
- }}
- onPointerMissed={() => {
- if (
- selectedFloor &&
- selectedFloor.userData.floorUuid === floor.floorUuid
- ) {
- setSelectedFloor(null);
- }
- }}
- >
-
-
-
-
-
- );
+ return (
+ {
+ if (!togglView && activeModule === "builder") {
+ if (e.object.userData.floorUuid) {
+ e.stopPropagation();
+ setSelectedFloor(e.object);
+ setSelectedDecal(null);
+ }
+ }
+ }}
+ onPointerMissed={() => {
+ if (selectedFloor && selectedFloor.userData.floorUuid === floor.floorUuid) {
+ setSelectedFloor(null);
+ }
+ }}
+ >
+
+
+
+
+
+ );
}
export default FloorInstance;
diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx
index 474eb9e..462660e 100644
--- a/app/src/modules/builder/line/line.tsx
+++ b/app/src/modules/builder/line/line.tsx
@@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
import { useSelectedPoints } from '../../../store/simulation/useSimulationStore';
+import { calculateAssetTransformationOnWall } from '../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
@@ -18,6 +19,8 @@ import { useSelectedPoints } from '../../../store/simulation/useSimulationStore'
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
+// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
+// import { deleteWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
interface LineProps {
points: [Point, Point];
@@ -30,9 +33,10 @@ function Line({ points }: Readonly) {
const [isDeletable, setIsDeletable] = useState(false);
const { socket } = useSocketStore();
const { toolMode } = useToolMode();
- const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
+ const { wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
const { push2D } = undoRedo2DStore();
- const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
+ const { getWallAssetsByWall, updateWallAsset, removeWallAsset } = wallAssetStore();
+ const { removeWallByPoints, setPosition: setWallPosition, getWallByPoints, getConnectedWallsByWallId } = wallStore();
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore();
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore();
const { userId, organization } = getUserData();
@@ -110,6 +114,33 @@ function Line({ points }: Readonly) {
const removedWall = removeWallByPoints(points);
if (removedWall && projectId) {
+ const assetsOnWall = getWallAssetsByWall(removedWall.wallUuid);
+
+ assetsOnWall.forEach((asset) => {
+ if (projectId && asset) {
+
+ removeWallAsset(asset.modelUuid);
+
+ // API
+
+ // deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
+
+ // SOCKET
+
+ const data = {
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization,
+ modelUuid: asset.modelUuid,
+ wallUuid: asset.wallUuid
+ }
+
+ socket.emit('v1:wall-asset:delete', data);
+
+ }
+ })
+
// API
// deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid);
@@ -361,13 +392,16 @@ function Line({ points }: Readonly) {
const offset = new THREE.Vector3().subVectors(midPoint, hit);
setDragOffset(offset);
- if (points[0].pointType === 'Wall') {
- const walls = getWallsByPointId(points[0].pointUuid);
- setInitialPositions({ walls });
- } else if (points[0].pointType === 'Floor') {
+ if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
+ const wall = getWallByPoints(points);
+ if (wall) {
+ const walls = getConnectedWallsByWallId(wall.wallUuid, false);
+ setInitialPositions({ walls });
+ }
+ } else if (points[0].pointType === 'Floor' && points[0].pointType === 'Floor') {
const floors = getFloorsByPointId(points[0].pointUuid);
setInitialPositions({ floors });
- } else if (points[0].pointType === 'Zone') {
+ } else if (points[0].pointType === 'Zone' && points[0].pointType === 'Zone') {
const zones = getZonesByPointId(points[0].pointUuid);
setInitialPositions({ zones });
}
@@ -378,14 +412,48 @@ function Line({ points }: Readonly) {
if (toolMode !== 'move' || !dragOffset) return;
handleCanvasCursors('default');
setDragOffset(null);
+
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
- const updatedWalls1 = getWallsByPointId(points[0].pointUuid);
- const updatedWalls2 = getWallsByPointId(points[1].pointUuid);
- const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
+ const wall = getWallByPoints(points);
+ if (!wall) return;
+ const updatedWalls = getConnectedWallsByWallId(wall.wallUuid, false);
if (updatedWalls.length > 0 && projectId) {
updatedWalls.forEach(updatedWall => {
+ const initialWall = initialPositions.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
+
+ if (initialWall) {
+ const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
+
+ assetsOnWall.forEach(asset => {
+ const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
+
+ const updatedWallAsset = updateWallAsset(asset.modelUuid, {
+ position: [position[0], asset.position[1], position[2]],
+ rotation: rotation
+ });
+
+ if (projectId && updatedWallAsset) {
+ // API
+
+ // upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
+
+ // SOCKET
+
+ const data = {
+ wallAssetData: updatedWallAsset,
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization
+ }
+
+ socket.emit('v1:wall-asset:add', data);
+ }
+ });
+ }
+
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx
index cf44c26..ac6b26a 100644
--- a/app/src/modules/builder/point/point.tsx
+++ b/app/src/modules/builder/point/point.tsx
@@ -19,9 +19,12 @@ import { useSceneContext } from '../../scene/sceneContext';
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
+// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
+// import { deleteWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
import { getUserData } from '../../../functions/getUserData';
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
+import { calculateAssetTransformationOnWall } from '../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall';
function Point({ point }: { readonly point: Point }) {
const materialRef = useRef(null);
@@ -32,12 +35,13 @@ function Point({ point }: { readonly point: Point }) {
const [dragOffset, setDragOffset] = useState(null);
const { socket } = useSocketStore();
const { toolMode } = useToolMode();
- const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
+ const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
const { push2D } = undoRedo2DStore();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
+ const { getWallAssetsByWall, updateWallAsset, removeWallAsset } = wallAssetStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore();
const { selectedPoints } = useSelectedPoints();
@@ -48,7 +52,7 @@ function Point({ point }: { readonly point: Point }) {
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
const colors = getColor(point);
- const [initialPositions, setInitialPositions] = useState<{
+ const [initialStates, setInitialStates] = useState<{
aisles?: Aisle[],
walls?: Wall[],
floors?: Floor[],
@@ -163,16 +167,16 @@ function Point({ point }: { readonly point: Point }) {
if (point.pointType === 'Aisle') {
const aisles = getAislesByPointId(point.pointUuid);
- setInitialPositions({ aisles });
+ setInitialStates({ aisles });
} else if (point.pointType === 'Wall') {
const walls = getWallsByPointId(point.pointUuid);
- setInitialPositions({ walls });
+ setInitialStates({ walls });
} else if (point.pointType === 'Floor') {
const floors = getFloorsByPointId(point.pointUuid);
- setInitialPositions({ floors });
+ setInitialStates({ floors });
} else if (point.pointType === 'Zone') {
const zones = getZonesByPointId(point.pointUuid);
- setInitialPositions({ zones });
+ setInitialStates({ zones });
}
}
};
@@ -204,8 +208,8 @@ function Point({ point }: { readonly point: Point }) {
})
})
- if (initialPositions.aisles && initialPositions.aisles.length > 0) {
- const updatedPoints = initialPositions.aisles.map((aisle) => ({
+ if (initialStates.aisles && initialStates.aisles.length > 0) {
+ const updatedPoints = initialStates.aisles.map((aisle) => ({
type: "Aisle" as const,
lineData: aisle,
newData: updatedAisles.find(a => a.aisleUuid === aisle.aisleUuid),
@@ -226,6 +230,39 @@ function Point({ point }: { readonly point: Point }) {
if (updatedWalls && updatedWalls.length > 0 && projectId) {
updatedWalls.forEach((updatedWall) => {
+ const initialWall = initialStates.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
+
+ if (initialWall) {
+ const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
+
+ assetsOnWall.forEach(asset => {
+ const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
+
+ const updatedWallAsset = updateWallAsset(asset.modelUuid, {
+ position: [position[0], asset.position[1], position[2]],
+ rotation: rotation
+ });
+
+ if (projectId && updatedWallAsset) {
+ // API
+
+ // upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
+
+ // SOCKET
+
+ const data = {
+ wallAssetData: updatedWallAsset,
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization
+ }
+
+ socket.emit('v1:wall-asset:add', data);
+ }
+ });
+ }
+
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
@@ -244,8 +281,8 @@ function Point({ point }: { readonly point: Point }) {
});
}
- if (initialPositions.walls && initialPositions.walls.length > 0) {
- const updatedPoints = initialPositions.walls.map((wall) => ({
+ if (initialStates.walls && initialStates.walls.length > 0) {
+ const updatedPoints = initialStates.walls.map((wall) => ({
type: "Wall" as const,
lineData: wall,
newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid),
@@ -283,8 +320,8 @@ function Point({ point }: { readonly point: Point }) {
});
}
- if (initialPositions.floors && initialPositions.floors.length > 0) {
- const updatedPoints = initialPositions.floors.map((floor) => ({
+ if (initialStates.floors && initialStates.floors.length > 0) {
+ const updatedPoints = initialStates.floors.map((floor) => ({
type: "Floor" as const,
lineData: floor,
newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid),
@@ -322,8 +359,8 @@ function Point({ point }: { readonly point: Point }) {
});
}
- if (initialPositions.zones && initialPositions.zones.length > 0) {
- const updatedPoints = initialPositions.zones.map((zone) => ({
+ if (initialStates.zones && initialStates.zones.length > 0) {
+ const updatedPoints = initialStates.zones.map((zone) => ({
type: "Zone" as const,
lineData: zone,
newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid),
@@ -340,7 +377,7 @@ function Point({ point }: { readonly point: Point }) {
}
}
- setInitialPositions({});
+ setInitialStates({});
}
const handlePointClick = (point: Point) => {
@@ -393,6 +430,33 @@ function Point({ point }: { readonly point: Point }) {
if (removedWalls.length > 0) {
setHoveredPoint(null);
removedWalls.forEach(wall => {
+ const assetsOnWall = getWallAssetsByWall(wall.wallUuid);
+
+ assetsOnWall.forEach((asset) => {
+ if (projectId && asset) {
+
+ removeWallAsset(asset.modelUuid);
+
+ // API
+
+ // deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
+
+ // SOCKET
+
+ const data = {
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization,
+ modelUuid: asset.modelUuid,
+ wallUuid: asset.wallUuid
+ }
+
+ socket.emit('v1:wall-asset:delete', data);
+
+ }
+ })
+
if (projectId) {
// API
diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx
index 9023dc6..9d68d73 100644
--- a/app/src/modules/builder/wall/Instances/instance/wall.tsx
+++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx
@@ -10,16 +10,16 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import * as Constants from '../../../../../types/world/worldConstants';
-// import DecalInstance from '../../../Decal/decalInstance';
+import DecalInstance from '../../../Decal/decalInstance';
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
function Wall({ wall }: { readonly wall: Wall }) {
const { wallStore, wallAssetStore } = useSceneContext();
- const { walls, addDecal } = wallStore();
- const { wallAssets, getAssetsByWall } = wallAssetStore();
- const assets = getAssetsByWall(wall.wallUuid);
+ const { walls } = wallStore();
+ const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
+ const assets = getWallAssetsByWall(wall.wallUuid);
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
const { togglView } = useToggleView();
const { activeModule } = useModuleStore();
@@ -28,6 +28,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
const { getWallType, isWallFlipped } = useWallClassification(walls);
const [visible, setVisible] = useState(true);
const meshRef = useRef();
+ const prevVisibleRef = useRef(null);
const wallType = getWallType(wall);
const wallFlipped = isWallFlipped(wall);
@@ -80,7 +81,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
map: wall.outsideMaterial === 'Default Material' ? defaultWallTexture : material1WallTexture,
}),
];
- }, [defaultWallTexture, material1WallTexture, wall]);
+ }, [defaultWallTexture, material1WallTexture, wall, visible]);
const geometry = useMemo(() => new THREE.BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
@@ -89,15 +90,23 @@ function Wall({ wall }: { readonly wall: Wall }) {
const v = new THREE.Vector3();
const u = new THREE.Vector3();
- if (!wallVisibility && wallType.type === 'room') {
+ let nextVisible = true;
+
+ if (!wallVisibility && wallType.type === "room") {
meshRef.current.getWorldDirection(v);
camera.getWorldDirection(u);
if (!u || !v) return;
- setVisible((2 * v.dot(u)) <= 0.1);
- } else {
- setVisible(true);
+ nextVisible = (2 * v.dot(u)) <= 0.1;
}
- })
+
+ if (prevVisibleRef.current !== nextVisible) {
+ prevVisibleRef.current = nextVisible;
+ setVisible(nextVisible);
+ assets.forEach((asset) => {
+ setVisibility(asset.modelUuid, nextVisible);
+ })
+ }
+ });
return (
0) return;
- const decal: Decal = {
- decalUuid: THREE.MathUtils.generateUUID(),
- decalName: 'Decal',
- decalId: 'Default Decal',
- decalPosition: [0, 0, wall.wallThickness / 2 + 0.001],
- decalRotation: 0,
- decalScale: 1,
- decalType: { type: 'Wall', wallUuid: wall.wallUuid }
- }
- addDecal(wall.wallUuid, decal);
-
}
}
}}
@@ -174,9 +170,9 @@ function Wall({ wall }: { readonly wall: Wall }) {
>
- {/* {wall.decals.map((decal) => (
+ {wall.decals.map((decal) => (
- ))} */}
+ ))}
);
diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts
new file mode 100644
index 0000000..aa881c8
--- /dev/null
+++ b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts
@@ -0,0 +1,46 @@
+import * as THREE from 'three';
+
+const calculateAssetTransformationOnWall = (
+ asset: WallAsset,
+ initialWall: Wall,
+ newWall: Wall
+): { position: [number, number, number], rotation: [number, number, number] } => {
+ const [initialStartPoint, initialEndPoint] = initialWall.points;
+ const [newStartPoint, newEndPoint] = newWall.points;
+
+ const initialWallVector = new THREE.Vector3(initialEndPoint.position[0] - initialStartPoint.position[0], 0, initialEndPoint.position[2] - initialStartPoint.position[2]);
+
+ const assetVector = new THREE.Vector3(asset.position[0] - initialStartPoint.position[0], 0, asset.position[2] - initialStartPoint.position[2]);
+
+ const initialWallLength = initialWallVector.length();
+ const initialWallNormalized = initialWallVector.normalize();
+ const dotProduct = assetVector.dot(initialWallNormalized);
+
+ const projection = initialWallNormalized.clone().multiplyScalar(dotProduct);
+ const perpendicular = new THREE.Vector3().subVectors(assetVector, projection);
+ const distanceFromWall = perpendicular.length();
+
+ const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y;
+ const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1);
+
+ const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength));
+
+ const newWallVector = new THREE.Vector3(newEndPoint.position[0] - newStartPoint.position[0], 0, newEndPoint.position[2] - newStartPoint.position[2]);
+
+ const x = newStartPoint.position[0] + (newEndPoint.position[0] - newStartPoint.position[0]) * percentage;
+ const z = newStartPoint.position[2] + (newEndPoint.position[2] - newStartPoint.position[2]) * percentage;
+
+ const newWallNormal = new THREE.Vector3(-newWallVector.z, 0, newWallVector.x).normalize();
+
+ const offsetX = newWallNormal.x * signedDistance;
+ const offsetZ = newWallNormal.z * signedDistance;
+
+ const wallAngle = Math.atan2(newWallVector.z, newWallVector.x);
+
+ return {
+ position: [x + offsetX, asset.position[1], z + offsetZ],
+ rotation: [0, -wallAngle, 0]
+ };
+};
+
+export { calculateAssetTransformationOnWall };
\ No newline at end of file
diff --git a/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx
similarity index 100%
rename from app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx
rename to app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx
diff --git a/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx
index 581dfe7..d47c263 100644
--- a/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx
+++ b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { useSceneContext } from '../../../scene/sceneContext'
import { useToggleView } from '../../../../store/builder/store';
-import WallAssetInstance from './Instances/wallAssetInstance';
+import WallAssetInstance from './Instance/wallAssetInstance';
function WallAssetInstances() {
const { wallAssetStore } = useSceneContext();
diff --git a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx
index 5001e28..fb95236 100644
--- a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx
+++ b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx
@@ -9,7 +9,7 @@ import { useVersionContext } from '../version/versionContext';
import { getUserData } from '../../../functions/getUserData';
import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment';
-import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
+// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
function WallAssetCreator() {
const { socket } = useSocketStore();
diff --git a/app/src/modules/scene/controls/contextControls/contextControls.tsx b/app/src/modules/scene/controls/contextControls/contextControls.tsx
index 38219ec..142f8d1 100644
--- a/app/src/modules/scene/controls/contextControls/contextControls.tsx
+++ b/app/src/modules/scene/controls/contextControls/contextControls.tsx
@@ -1,194 +1,243 @@
-import { useEffect, useState } from 'react';
-import { useThree } from '@react-three/fiber';
-import { CameraControls, Html, ScreenSpace } from '@react-three/drei';
-import { useContextActionStore, useRenameModeStore, useSelectedAssets } from '../../../../store/builder/store';
-import ContextMenu from '../../../../components/ui/menu/contextMenu';
+import { useEffect, useRef, useState } from "react";
+import { useThree } from "@react-three/fiber";
+import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
+import {
+ useContextActionStore,
+ useRenameModeStore,
+ useSelectedAssets,
+} from "../../../../store/builder/store";
+import ContextMenu from "../../../../components/ui/menu/contextMenu";
function ContextControls() {
- const { gl, controls } = useThree();
- const [canRender, setCanRender] = useState(false);
- const [visibility, setVisibility] = useState({ rename: true, focus: true, flipX: true, flipZ: true, move: true, rotate: true, duplicate: true, copy: true, paste: true, modifier: false, group: false, array: false, delete: true, });
- const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
- const { selectedAssets } = useSelectedAssets();
- const { setContextAction } = useContextActionStore();
- const { setIsRenameMode } = useRenameModeStore();
+ const { gl, controls } = useThree();
+ const [canRender, setCanRender] = useState(false);
+ const [visibility, setVisibility] = useState({
+ rename: true,
+ focus: true,
+ flipX: true,
+ flipZ: true,
+ move: true,
+ rotate: true,
+ duplicate: true,
+ copy: true,
+ paste: true,
+ modifier: false,
+ group: false,
+ array: false,
+ delete: true,
+ });
+ const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
+ const { selectedAssets } = useSelectedAssets();
+ const { setContextAction } = useContextActionStore();
+ const { setIsRenameMode } = useRenameModeStore();
+ const rightDrag = useRef(false);
+ const isRightMouseDown = useRef(false);
- useEffect(() => {
- if (selectedAssets.length === 1) {
- setVisibility({
- rename: true,
- focus: true,
- flipX: true,
- flipZ: true,
- move: true,
- rotate: true,
- duplicate: true,
- copy: true,
- paste: true,
- modifier: false,
- group: false,
- array: false,
- delete: true,
- });
- } else if (selectedAssets.length > 1) {
- setVisibility({
- rename: false,
- focus: true,
- flipX: true,
- flipZ: true,
- move: true,
- rotate: true,
- duplicate: true,
- copy: true,
- paste: true,
- modifier: false,
- group: true,
- array: false,
- delete: true,
- });
- } else {
- setVisibility({
- rename: false,
- focus: false,
- flipX: false,
- flipZ: false,
- move: false,
- rotate: false,
- duplicate: false,
- copy: false,
- paste: false,
- modifier: false,
- group: false,
- array: false,
- delete: false,
- });
- }
- }, [selectedAssets]);
+ useEffect(() => {
+ if (selectedAssets.length === 1) {
+ setVisibility({
+ rename: true,
+ focus: true,
+ flipX: true,
+ flipZ: true,
+ move: true,
+ rotate: true,
+ duplicate: true,
+ copy: true,
+ paste: true,
+ modifier: false,
+ group: false,
+ array: false,
+ delete: true,
+ });
+ } else if (selectedAssets.length > 1) {
+ setVisibility({
+ rename: false,
+ focus: true,
+ flipX: true,
+ flipZ: true,
+ move: true,
+ rotate: true,
+ duplicate: true,
+ copy: true,
+ paste: true,
+ modifier: false,
+ group: true,
+ array: false,
+ delete: true,
+ });
+ } else {
+ setVisibility({
+ rename: false,
+ focus: false,
+ flipX: false,
+ flipZ: false,
+ move: false,
+ rotate: false,
+ duplicate: false,
+ copy: false,
+ paste: false,
+ modifier: false,
+ group: false,
+ array: false,
+ delete: false,
+ });
+ }
+ }, [selectedAssets]);
- useEffect(() => {
- const canvasElement = gl.domElement;
+ useEffect(() => {
+ const canvasElement = gl.domElement;
- const handleContextClick = (event: MouseEvent) => {
- event.preventDefault();
- if (selectedAssets.length > 0) {
- setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2 });
- setCanRender(true);
- if (controls) {
- (controls as CameraControls).enabled = false;
- }
- } else {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- }
- };
+ const onPointerDown = (evt: any) => {
+ if (evt.button === 2) {
+ isRightMouseDown.current = true;
+ rightDrag.current = false;
+ }
+ };
- if (selectedAssets.length > 0) {
- canvasElement.addEventListener('contextmenu', handleContextClick)
- } else {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setMenuPosition({ x: 0, y: 0 });
- }
+ const onPointerMove = () => {
+ if (isRightMouseDown.current) {
+ rightDrag.current = true;
+ }
+ };
- return () => {
- canvasElement.removeEventListener('contextmenu', handleContextClick);
- };
- }, [gl, selectedAssets]);
+ const onPointerUp = (evt: any) => {
+ if (evt.button === 2) {
+ isRightMouseDown.current = false;
+ }
+ };
- const handleAssetRename = () => {
+ const handleContextClick = (event: MouseEvent) => {
+ event.preventDefault();
+ if (rightDrag.current) return;
+ if (selectedAssets.length > 0) {
+ setMenuPosition({
+ x: event.clientX - gl.domElement.width / 2,
+ y: event.clientY - gl.domElement.height / 2,
+ });
+ setCanRender(true);
+ if (controls) {
+ (controls as CameraControls).enabled = false;
+ }
+ } else {
setCanRender(false);
if (controls) {
- (controls as CameraControls).enabled = true;
+ (controls as CameraControls).enabled = true;
}
- setContextAction("renameAsset");
- setIsRenameMode(true);
- }
- const handleAssetFocus = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("focusAsset");
- }
- const handleAssetMove = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("moveAsset")
- }
- const handleAssetRotate = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("rotateAsset")
- }
- const handleAssetCopy = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("copyAsset")
- }
- const handleAssetPaste = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("pasteAsset")
- }
- const handleAssetDelete = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("deleteAsset")
- }
- const handleAssetDuplicate = () => {
- setCanRender(false);
- if (controls) {
- (controls as CameraControls).enabled = true;
- }
- setContextAction("duplicateAsset")
+ }
+ };
+
+ if (selectedAssets.length > 0) {
+ canvasElement.addEventListener("pointerdown", onPointerDown);
+ canvasElement.addEventListener("pointermove", onPointerMove);
+ canvasElement.addEventListener("pointerup", onPointerUp);
+ canvasElement.addEventListener("contextmenu", handleContextClick);
+ } else {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setMenuPosition({ x: 0, y: 0 });
}
- return (
- <>
- {canRender && (
-
-
- handleAssetRename()}
- onFocus={() => handleAssetFocus()}
- onFlipX={() => console.log("Flip to X")}
- onFlipZ={() => console.log("Flip to Z")}
- onMove={() => handleAssetMove()}
- onRotate={() => handleAssetRotate()}
- onDuplicate={() => handleAssetDuplicate()}
- onCopy={() => handleAssetCopy()}
- onPaste={() => handleAssetPaste()}
- onGroup={() => console.log("Group")}
- onArray={() => console.log("Array")}
- onDelete={() => handleAssetDelete()}
- />
-
-
- )}
- >
- );
+ return () => {
+ canvasElement.removeEventListener("pointerdown", onPointerDown);
+ canvasElement.removeEventListener("pointermove", onPointerMove);
+ canvasElement.removeEventListener("pointerup", onPointerUp);
+ canvasElement.removeEventListener("contextmenu", handleContextClick);
+ };
+ }, [controls, gl, selectedAssets]);
+
+ const handleAssetRename = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("renameAsset");
+ setIsRenameMode(true);
+ };
+ const handleAssetFocus = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("focusAsset");
+ };
+ const handleAssetMove = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("moveAsset");
+ };
+ const handleAssetRotate = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("rotateAsset");
+ };
+ const handleAssetCopy = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("copyAsset");
+ };
+ const handleAssetPaste = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("pasteAsset");
+ };
+ const handleAssetDelete = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("deleteAsset");
+ };
+ const handleAssetDuplicate = () => {
+ setCanRender(false);
+ if (controls) {
+ (controls as CameraControls).enabled = true;
+ }
+ setContextAction("duplicateAsset");
+ };
+
+ return (
+ <>
+ {canRender && (
+
+
+ handleAssetRename()}
+ onFocus={() => handleAssetFocus()}
+ onFlipX={() => console.log("Flip to X")}
+ onFlipZ={() => console.log("Flip to Z")}
+ onMove={() => handleAssetMove()}
+ onRotate={() => handleAssetRotate()}
+ onDuplicate={() => handleAssetDuplicate()}
+ onCopy={() => handleAssetCopy()}
+ onPaste={() => handleAssetPaste()}
+ onGroup={() => console.log("Group")}
+ onArray={() => console.log("Array")}
+ onDelete={() => handleAssetDelete()}
+ />
+
+
+ )}
+ >
+ );
}
export default ContextControls;
diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx
index c41a4bd..d63f0ac 100644
--- a/app/src/modules/scene/controls/controls.tsx
+++ b/app/src/modules/scene/controls/controls.tsx
@@ -18,6 +18,7 @@ import ContextControls from "./contextControls/contextControls";
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls";
+import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts";
export default function Controls() {
const controlsRef = useRef(null);
@@ -116,6 +117,7 @@ export default function Controls() {
stopInterval();
};
}, [toggleView, state, socket]);
+ useCameraShortcuts(controlsRef);
return (
<>
diff --git a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx
index f6d04f0..6c10141 100644
--- a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx
@@ -9,6 +9,7 @@ import { useSceneContext } from "../../../sceneContext";
import { useVersionContext } from "../../../../builder/version/versionContext";
import { useSelectedPoints } from "../../../../../store/simulation/useSimulationStore";
import useModuleStore from "../../../../../store/useModuleStore";
+import { calculateAssetTransformationOnWall } from "../../../../builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall";
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
@@ -19,7 +20,7 @@ function MoveControls2D({
movedObjects,
setMovedObjects,
pastedObjects,
- setpastedObjects,
+ setPastedObjects,
duplicatedObjects,
setDuplicatedObjects,
rotatedObjects,
@@ -36,7 +37,8 @@ function MoveControls2D({
const { projectId } = useParams();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
- const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
+ const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
+ const { getWallAssetsByWall, updateWallAsset } = wallAssetStore();
const { push2D } = undoRedo2DStore();
const { setPosition: setAislePosition, getAislesByPointId, getAisleById } = aisleStore();
const { setPosition: setWallPosition, getWallsByPointId, getWallById } = wallStore();
@@ -45,6 +47,12 @@ function MoveControls2D({
const [dragOffset, setDragOffset] = useState(null);
const [initialPositions, setInitialPositions] = useState>({});
const [initialStates, setInitialStates] = useState>({});
+ const [initial, setInitial] = useState<{
+ aisles?: Aisle[],
+ walls?: Wall[],
+ floors?: Floor[],
+ zones?: Zone[]
+ }>({});
const [isMoving, setIsMoving] = useState(false);
useEffect(() => {
@@ -66,7 +74,7 @@ function MoveControls2D({
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
- placeMovedAssets();
+ placeMovedPoints();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
@@ -110,7 +118,7 @@ function MoveControls2D({
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
- }, [camera, controls, scene, toggleView, selectedPoints, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]);
+ }, [camera, controls, scene, toggleView, selectedPoints, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, initial]);
useEffect(() => {
if (toolMode !== 'move' || !toggleView) {
@@ -169,13 +177,35 @@ function MoveControls2D({
if (selectedPoints.length === 0) return;
const states: Record = {};
+ const initials: {
+ aisles?: Aisle[] | undefined;
+ walls?: Wall[];
+ floors?: Floor[];
+ zones?: Zone[];
+ } = {}
selectedPoints.forEach((point: THREE.Object3D) => {
states[point.uuid] = {
position: new THREE.Vector3().copy(point.position),
rotation: point.rotation ? new THREE.Euler().copy(point.rotation) : undefined
};
+
+ if (point.userData.pointType === "Aisle") {
+ const aisles = getAislesByPointId(point.userData.pointUuid);
+ initials.aisles = [...(initials.aisles ?? []), ...aisles,].filter((aisle, index, self) => index === self.findIndex((a) => a.aisleUuid === aisle.aisleUuid));
+ } else if (point.userData.pointType === "Wall") {
+ const walls = getWallsByPointId(point.userData.pointUuid);
+ initials.walls = [...(initials.walls ?? []), ...walls,].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
+ } else if (point.userData.pointType === "Floor") {
+ const floors = getFloorsByPointId(point.userData.pointUuid);
+ initials.floors = [...(initials.floors ?? []), ...floors,].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid));
+ } else if (point.userData.pointType === "Zone") {
+ const zones = getZonesByPointId(point.userData.pointUuid);
+ initials.zones = [...(initials.zones ?? []), ...zones,].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid));
+ }
});
+
+ setInitial(initials)
setInitialStates(states);
const positions: Record = {};
@@ -221,7 +251,7 @@ function MoveControls2D({
}, 0)
};
- const placeMovedAssets = () => {
+ const placeMovedPoints = () => {
if (movedObjects.length === 0) return;
const undoPoints: UndoRedo2DDataTypeSchema[] = [];
@@ -229,6 +259,7 @@ function MoveControls2D({
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
const processedFloors: UndoRedo2DDataTypeSchema[] = [];
const processedZones: UndoRedo2DDataTypeSchema[] = [];
+ const wallAssetUpdates: WallAsset[] = [];
movedObjects.forEach((movedObject: THREE.Object3D) => {
if (movedObject.userData.pointUuid) {
@@ -262,11 +293,11 @@ function MoveControls2D({
lineData: {
...updatedAisle,
points: [
- updatedAisle.points[0].pointUuid === point.pointUuid
- ? { ...updatedAisle.points[0], position: [old.position.x, old.position.y, old.position.z] }
+ initialStates[updatedAisle.points[0].pointUuid] ?
+ { ...updatedAisle.points[0], position: initialStates[updatedAisle.points[0].pointUuid].position }
: updatedAisle.points[0],
- updatedAisle.points[1].pointUuid === point.pointUuid
- ? { ...updatedAisle.points[1], position: [old.position.x, old.position.y, old.position.z] }
+ initialStates[updatedAisle.points[1].pointUuid] ?
+ { ...updatedAisle.points[1], position: initialStates[updatedAisle.points[1].pointUuid].position }
: updatedAisle.points[1]
] as [Point, Point],
},
@@ -281,6 +312,24 @@ function MoveControls2D({
if (updatedWalls?.length && projectId) {
updatedWalls.forEach(updatedWall => {
+ const initialWall = initial.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
+
+ if (initialWall) {
+ const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
+
+ assetsOnWall.forEach((asset) => {
+ const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
+
+ const updatedWallAsset: WallAsset = {
+ ...asset,
+ position: [position[0], asset.position[1], position[2]],
+ rotation,
+ };
+
+ wallAssetUpdates.push(updatedWallAsset);
+ });
+ }
+
// API
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
@@ -302,11 +351,11 @@ function MoveControls2D({
lineData: {
...updatedWall,
points: [
- updatedWall.points[0].pointUuid === point.pointUuid
- ? { ...updatedWall.points[0], position: [old.position.x, old.position.y, old.position.z] }
+ initialStates[updatedWall.points[0].pointUuid] ?
+ { ...updatedWall.points[0], position: initialStates[updatedWall.points[0].pointUuid].position }
: updatedWall.points[0],
- updatedWall.points[1].pointUuid === point.pointUuid
- ? { ...updatedWall.points[1], position: [old.position.x, old.position.y, old.position.z] }
+ initialStates[updatedWall.points[1].pointUuid] ?
+ { ...updatedWall.points[1], position: initialStates[updatedWall.points[1].pointUuid].position }
: updatedWall.points[1]
] as [Point, Point],
},
@@ -399,6 +448,36 @@ function MoveControls2D({
});
setTimeout(() => {
+
+ if (wallAssetUpdates.length > 0) {
+ wallAssetUpdates.filter((wallAssets, index, self) => index === self.findIndex((w) => w.modelUuid === wallAssets.modelUuid));
+ wallAssetUpdates.forEach((updatedWallAsset) => {
+ if (projectId && updatedWallAsset) {
+
+ updateWallAsset(updatedWallAsset.modelUuid, {
+ position: updatedWallAsset.position,
+ rotation: updatedWallAsset.rotation
+ });
+
+ // API
+
+ // upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
+
+ // SOCKET
+
+ const data = {
+ wallAssetData: updatedWallAsset,
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization
+ }
+
+ socket.emit('v1:wall-asset:add', data);
+ }
+ });
+ }
+
if (processedWalls.length > 0) {
const wallMap = new Map();
@@ -525,7 +604,7 @@ function MoveControls2D({
};
const clearSelection = () => {
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
diff --git a/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx b/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx
index 3ca6f99..a12196d 100644
--- a/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx
@@ -28,7 +28,7 @@ const SelectionControls2D: React.FC = () => {
const [movedObjects, setMovedObjects] = useState([]);
const [rotatedObjects, setRotatedObjects] = useState([]);
const [copiedObjects, setCopiedObjects] = useState([]);
- const [pastedObjects, setpastedObjects] = useState([]);
+ const [pastedObjects, setPastedObjects] = useState([]);
const [duplicatedObjects, setDuplicatedObjects] = useState([]);
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
@@ -38,7 +38,8 @@ const SelectionControls2D: React.FC = () => {
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { hoveredLine, hoveredPoint } = useBuilderStore();
- const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
+ const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
+ const { getWallAssetsByWall, removeWallAsset } = wallAssetStore();
const { push2D } = undoRedo2DStore();
const { removePoint: removeAislePoint } = aisleStore();
const { removePoint: removeWallPoint } = wallStore();
@@ -216,7 +217,7 @@ const SelectionControls2D: React.FC = () => {
}, [selectionBox, pointer, controls, selectedPoints, setSelectedPoints]);
const clearSelection = () => {
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
clearSelectedPoints();
};
@@ -271,6 +272,34 @@ const SelectionControls2D: React.FC = () => {
const removedWalls = removeWallPoint(point.pointUuid);
if (removedWalls.length > 0) {
removedWalls.forEach(wall => {
+
+ const assetsOnWall = getWallAssetsByWall(wall.wallUuid);
+
+ assetsOnWall.forEach((asset) => {
+ if (projectId && asset) {
+
+ removeWallAsset(asset.modelUuid);
+
+ // API
+
+ // deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
+
+ // SOCKET
+
+ const data = {
+ projectId: projectId,
+ versionId: selectedVersion?.versionId || '',
+ userId: userId,
+ organization: organization,
+ modelUuid: asset.modelUuid,
+ wallUuid: asset.wallUuid
+ }
+
+ socket.emit('v1:wall-asset:delete', data);
+
+ }
+ })
+
if (projectId) {
// API
@@ -594,7 +623,7 @@ const SelectionControls2D: React.FC = () => {
return (
<>
-
+
>
);
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx
index 47a8039..c484cb2 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx
@@ -12,17 +12,7 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
-const CopyPasteControls3D = ({
- copiedObjects,
- setCopiedObjects,
- pastedObjects,
- setpastedObjects,
- setDuplicatedObjects,
- movedObjects,
- setMovedObjects,
- rotatedObjects,
- setRotatedObjects,
-}: any) => {
+const CopyPasteControls3D = () => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
@@ -32,7 +22,7 @@ const CopyPasteControls3D = ({
const { push3D } = undoRedo3DStore();
const { addEvent } = eventStore();
const { projectId } = useParams();
- const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore();
+ const { assets, addAsset, updateAsset, removeAsset, getAssetById, copiedObjects, setCopiedObjects, pastedObjects, setPastedObjects, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects } = assetStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
@@ -182,7 +172,7 @@ const CopyPasteControls3D = ({
return clone;
});
- setpastedObjects(newPastedObjects);
+ setPastedObjects(newPastedObjects);
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
@@ -391,18 +381,22 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "storageUnit",
+ storageCapacity: 10,
+ storageCount: 10,
+ materialType: "Default material",
subType: pastedAsset.userData.eventData.subType || '',
point: {
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: "store",
- storageCapacity: 10,
- triggers: []
- }
+ actions: [
+ {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Action 1",
+ actionType: "store",
+ triggers: []
+ }
+ ]
}
}
addEvent(storageEvent);
@@ -432,6 +426,11 @@ const CopyPasteControls3D = ({
actionType: "worker",
loadCapacity: 1,
assemblyCount: 1,
+ assemblyCondition: {
+ conditionType: 'material',
+ materialType: "Default material"
+ },
+ manufactureCount: 1,
loadCount: 1,
processTime: 10,
triggers: []
@@ -594,7 +593,7 @@ const CopyPasteControls3D = ({
const clearSelection = () => {
setMovedObjects([]);
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
index 8c54f26..4f0394a 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
@@ -13,15 +13,7 @@ import { handleAssetPositionSnap } from "./functions/handleAssetPositionSnap";
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
-const DuplicationControls3D = ({
- duplicatedObjects,
- setDuplicatedObjects,
- setpastedObjects,
- movedObjects,
- setMovedObjects,
- rotatedObjects,
- setRotatedObjects,
-}: any) => {
+const DuplicationControls3D = () => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
@@ -31,7 +23,7 @@ const DuplicationControls3D = ({
const { push3D } = undoRedo3DStore();
const { addEvent } = eventStore();
const { projectId } = useParams();
- const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore();
+ const { assets, addAsset, updateAsset, removeAsset, getAssetById, duplicatedObjects, setDuplicatedObjects, setPastedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects } = assetStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
@@ -458,18 +450,22 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "storageUnit",
+ storageCapacity: 10,
+ storageCount: 10,
+ materialType: "Default material",
subType: duplicatedAsset.userData.eventData.subType || '',
point: {
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: "store",
- storageCapacity: 10,
- triggers: []
- }
+ actions: [
+ {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Action 1",
+ actionType: "store",
+ triggers: []
+ }
+ ]
}
}
addEvent(storageEvent);
@@ -499,6 +495,11 @@ const DuplicationControls3D = ({
actionType: "worker",
loadCapacity: 1,
assemblyCount: 1,
+ assemblyCondition: {
+ conditionType: 'material',
+ materialType: "Default material"
+ },
+ manufactureCount: 1,
loadCount: 1,
processTime: 10,
triggers: []
@@ -661,7 +662,7 @@ const DuplicationControls3D = ({
const clearSelection = () => {
setMovedObjects([]);
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
index 21ddd33..e0940c9 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
@@ -15,17 +15,7 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
-function MoveControls3D({
- movedObjects,
- setMovedObjects,
- pastedObjects,
- setpastedObjects,
- duplicatedObjects,
- setDuplicatedObjects,
- rotatedObjects,
- setRotatedObjects,
- boundingBoxRef,
-}: any) {
+function MoveControls3D({ boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
@@ -38,7 +28,7 @@ function MoveControls3D({
const { projectId } = useParams();
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
const { push3D } = undoRedo3DStore();
- const { updateAsset, getAssetById } = assetStore();
+ const { updateAsset, getAssetById, movedObjects, setMovedObjects, pastedObjects, setPastedObjects, duplicatedObjects, setDuplicatedObjects, rotatedObjects, setRotatedObjects } = assetStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
@@ -74,6 +64,7 @@ function MoveControls3D({
setContextAction(null);
moveAssets()
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [contextAction])
useEffect(() => {
@@ -186,7 +177,8 @@ function MoveControls3D({
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
- }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
const pointPosition = new THREE.Vector3().copy(point.position);
@@ -220,7 +212,7 @@ function MoveControls3D({
}
});
setAxisConstraint(null);
- }, 100)
+ }, 50)
}, [movedObjects, initialStates, updateAsset]);
useEffect(() => {
@@ -233,6 +225,7 @@ function MoveControls3D({
setDragOffset(newOffset);
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [axisConstraint, camera, movedObjects])
useFrame(() => {
@@ -462,7 +455,7 @@ function MoveControls3D({
};
const clearSelection = () => {
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
index 8ea8e64..5af7a3e 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
@@ -14,16 +14,7 @@ import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
-function RotateControls3D({
- rotatedObjects,
- setRotatedObjects,
- movedObjects,
- setMovedObjects,
- pastedObjects,
- setpastedObjects,
- duplicatedObjects,
- setDuplicatedObjects
-}: any) {
+function RotateControls3D() {
const { camera, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
@@ -35,7 +26,7 @@ function RotateControls3D({
const { projectId } = useParams();
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
const { push3D } = undoRedo3DStore();
- const { updateAsset } = assetStore();
+ const { updateAsset, rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, pastedObjects, setPastedObjects, duplicatedObjects, setDuplicatedObjects } = assetStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
@@ -161,7 +152,7 @@ function RotateControls3D({
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
- }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent]);
+ }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
const resetToInitialRotations = useCallback(() => {
setTimeout(() => {
@@ -185,7 +176,7 @@ function RotateControls3D({
}
}
});
- }, 100)
+ }, 50)
}, [rotatedObjects, initialRotations, initialPositions, updateAsset]);
useFrame(() => {
@@ -397,7 +388,7 @@ function RotateControls3D({
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
const clearSelection = () => {
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx
index 4dd2063..ed458c2 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { SelectionHelper } from "../selectionHelper";
@@ -23,18 +23,13 @@ const SelectionControls3D: React.FC = () => {
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
- const [movedObjects, setMovedObjects] = useState([]);
- const [rotatedObjects, setRotatedObjects] = useState([]);
- const [copiedObjects, setCopiedObjects] = useState([]);
- const [pastedObjects, setpastedObjects] = useState([]);
- const [duplicatedObjects, setDuplicatedObjects] = useState([]);
const boundingBoxRef = useRef();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const { contextAction, setContextAction } = useContextActionStore()
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
const { push3D } = undoRedo3DStore();
- const { removeAsset, getAssetById } = assetStore();
+ const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
const { toolMode } = useToolMode();
const { selectedVersionStore } = useVersionContext();
@@ -72,6 +67,7 @@ const SelectionControls3D: React.FC = () => {
setContextAction(null);
deleteSelection()
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [contextAction])
useEffect(() => {
@@ -227,12 +223,14 @@ const SelectionControls3D: React.FC = () => {
helper.enabled = false;
helper.dispose();
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
useEffect(() => {
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
clearSelection();
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeModule, toolMode, toggleView]);
const selectAssets = useCallback(() => {
@@ -267,13 +265,13 @@ const SelectionControls3D: React.FC = () => {
}, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]);
const clearSelection = () => {
- setpastedObjects([]);
+ setPastedObjects([]);
setDuplicatedObjects([]);
setSelectedAssets([]);
};
const deleteSelection = () => {
- if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
+ if (selectedAssets.length > 0 && duplicatedObjects.length === 0 && pastedObjects.length === 0) {
const undoActions: UndoRedo3DAction[] = [];
const assetsToDelete: AssetData[] = [];
@@ -366,21 +364,22 @@ const SelectionControls3D: React.FC = () => {
selectedUUIDs.forEach((uuid: string) => {
removeAsset(uuid);
});
+
+ echo.warn("Selected models removed!");
+ clearSelection();
}
- echo.success("Selected models removed!");
- clearSelection();
};
return (
<>
-
+
-
+
-
+
-
+
>
);
diff --git a/app/src/modules/scene/helpers/StatsHelper.tsx b/app/src/modules/scene/helpers/StatsHelper.tsx
index 486488b..7414b70 100644
--- a/app/src/modules/scene/helpers/StatsHelper.tsx
+++ b/app/src/modules/scene/helpers/StatsHelper.tsx
@@ -1,7 +1,8 @@
import { useEffect, useState } from "react";
-import { Stats } from "@react-three/drei";
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
+import { Perf } from 'r3f-perf';
+
export default function StatsHelper() {
const [visible, setVisible] = useState(false);
@@ -18,5 +19,5 @@ export default function StatsHelper() {
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
- return visible ? : null;
+ return visible ? : null;
}
diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx
index 63adb95..a8f9661 100644
--- a/app/src/modules/scene/scene.tsx
+++ b/app/src/modules/scene/scene.tsx
@@ -16,8 +16,7 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
import { getUserData } from "../../functions/getUserData";
import { useSceneContext } from "./sceneContext";
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
-
-import StatsHelper from "./helpers/StatsHelper";
+import { compressImage } from "../../utils/compressImage";
export default function Scene({ layout }: { readonly layout: "Main Layout" | "Comparison Layout"; }) {
const map = useMemo(() => [
@@ -35,23 +34,24 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
const { loadingProgress } = useLoadingProgress();
useEffect(() => {
- if (!projectId && loadingProgress > 1) return;
+ if (!projectId || loadingProgress !== 0) return;
getAllProjects(userId, organization).then((projects) => {
if (!projects || !projects.Projects) return;
let project = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId);
const canvas = document.getElementById("sceneCanvas")?.getElementsByTagName("canvas")[0];
if (!canvas) return;
- const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
- const updateProjects = {
- projectId: project?._id,
- organization,
- userId,
- projectName: project?.projectName,
- thumbnail: screenshotDataUrl,
- };
- if (projectSocket) {
- projectSocket.emit("v1:project:update", updateProjects);
- }
+ compressImage((canvas as HTMLCanvasElement)?.toDataURL("image/png")).then((screenshotDataUrl) => {
+ const updateProjects = {
+ projectId: project?._id,
+ organization,
+ userId,
+ projectName: project?.projectName,
+ thumbnail: screenshotDataUrl,
+ };
+ if (projectSocket) {
+ projectSocket.emit("v1:project:update", updateProjects);
+ }
+ });
}).catch((err) => {
console.error(err);
});
@@ -68,7 +68,7 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
onContextMenu={(e) => { e.preventDefault(); }}
performance={{ min: 0.9, max: 1.0 }}
onCreated={(e) => { e.scene.background = layout === "Main Layout" ? null : new Color(0x19191d); }}
- gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true }}
+ gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
>
@@ -81,7 +81,6 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
-
);
diff --git a/app/src/modules/scene/setup/setup.tsx b/app/src/modules/scene/setup/setup.tsx
index d51d8af..1abdf20 100644
--- a/app/src/modules/scene/setup/setup.tsx
+++ b/app/src/modules/scene/setup/setup.tsx
@@ -1,13 +1,12 @@
import Sun from '../environment/sky'
import Shadows from '../environment/shadow'
import PostProcessing from '../postProcessing/postProcessing'
+import StatsHelper from '../helpers/StatsHelper';
import Controls from '../controls/controls';
import { AdaptiveDpr, AdaptiveEvents, Environment } from '@react-three/drei'
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
-import SecondaryCamera from '../../secondaryCamera/secondaryCamera';
-
function Setup() {
return (
<>
@@ -24,6 +23,7 @@ function Setup() {
{/* */}
+
diff --git a/app/src/modules/scene/tools/autoRotate.tsx b/app/src/modules/scene/tools/autoRotate.tsx
new file mode 100644
index 0000000..f0ff7a0
--- /dev/null
+++ b/app/src/modules/scene/tools/autoRotate.tsx
@@ -0,0 +1,47 @@
+import { useEffect, useRef, useState } from 'react'
+import { useThree, useFrame } from '@react-three/fiber'
+import type CameraControlsImpl from 'camera-controls'
+
+export default function AutoRotate() {
+ const { gl, controls } = useThree()
+ const [isIdle, setIsIdle] = useState(false)
+ const idleTimeout = useRef(null)
+ const lastInteractionTime = useRef(Date.now())
+
+ const cameraControls = controls as CameraControlsImpl | null
+
+ const resetIdleTimer = () => {
+ lastInteractionTime.current = Date.now()
+ if (isIdle) setIsIdle(false)
+ if (idleTimeout.current) clearTimeout(idleTimeout.current)
+ idleTimeout.current = setTimeout(() => {
+ setIsIdle(true)
+ }, 30_000)
+ }
+
+ useEffect(() => {
+ const dom = gl.domElement
+ const listener = () => resetIdleTimer()
+
+ dom.addEventListener('pointerdown', listener)
+ dom.addEventListener('wheel', listener)
+ window.addEventListener('keydown', listener)
+
+ resetIdleTimer()
+
+ return () => {
+ dom.removeEventListener('pointerdown', listener)
+ dom.removeEventListener('wheel', listener)
+ window.removeEventListener('keydown', listener)
+ }
+ }, [gl])
+
+ useFrame((_, delta) => {
+ if (isIdle && cameraControls) {
+ cameraControls.rotate(delta * 0.1, 0, true)
+ cameraControls.update(delta)
+ }
+ })
+
+ return null
+}
diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx
index 1da41b3..9f8189d 100644
--- a/app/src/modules/scene/tools/measurementTool.tsx
+++ b/app/src/modules/scene/tools/measurementTool.tsx
@@ -1,241 +1,272 @@
import * as THREE from "three";
-import { useEffect, useRef, useState } from "react";
+import { useCallback, useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import { useToolMode } from "../../../store/builder/store";
-import { Html } from "@react-three/drei";
+import { Html, Line } from "@react-three/drei";
const MeasurementTool = () => {
- const { gl, raycaster, pointer, camera, scene } = useThree();
- const { toolMode } = useToolMode();
+ const { gl, raycaster, pointer, camera, scene } = useThree();
+ const { toolMode } = useToolMode();
- const [points, setPoints] = useState([]);
- const [tubeGeometry, setTubeGeometry] = useState(
- null
- );
- const groupRef = useRef(null);
- const [startConePosition, setStartConePosition] =
- useState(null);
- const [endConePosition, setEndConePosition] = useState(
- null
- );
- const [startConeQuaternion, setStartConeQuaternion] = useState(
- new THREE.Quaternion()
- );
- const [endConeQuaternion, setEndConeQuaternion] = useState(
- new THREE.Quaternion()
- );
- const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 });
+ const [points, setPoints] = useState([]);
+ const [linePoints, setLinePoints] = useState(null);
+ const [axisLock, setAxisLock] = useState<"x" | "y" | "z" | null>(null);
+ const groupRef = useRef(null);
+ const keysPressed = useRef>(new Set());
- const MIN_RADIUS = 0.001, MAX_RADIUS = 0.1;
- const MIN_CONE_RADIUS = 0.01, MAX_CONE_RADIUS = 0.4;
- const MIN_CONE_HEIGHT = 0.035, MAX_CONE_HEIGHT = 2.0;
+ // Axis lock key handling
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.altKey) {
+ if (e.key.toLowerCase() === "x") keysPressed.current.add("x");
+ else if (e.key.toLowerCase() === "y") keysPressed.current.add("y");
+ else if (e.key.toLowerCase() === "z") keysPressed.current.add("z");
- useEffect(() => {
- const canvasElement = gl.domElement;
- let drag = false;
- let isLeftMouseDown = false;
-
- const onMouseDown = () => {
- isLeftMouseDown = true;
- drag = false;
- };
-
- const onMouseUp = (evt: any) => {
- isLeftMouseDown = false;
- if (evt.button === 0 && !drag) {
- raycaster.setFromCamera(pointer, camera);
- const intersects = raycaster
- .intersectObjects(scene.children, true)
- .filter(
- (intersect) =>
- !intersect.object.name.includes("Roof") &&
- !intersect.object.name.includes("MeasurementReference") &&
- !intersect.object.name.includes("agv-collider") &&
- !intersect.object.name.includes("zonePlane") &&
- !intersect.object.name.includes("SelectionGroup") &&
- !intersect.object.name.includes("selectionAssetGroup") &&
- !intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
- !intersect.object.name.includes("SelectionGroupBoundingBox") &&
- !intersect.object.name.includes("SelectionGroupBoundingLine") &&
- intersect.object.type !== "GridHelper"
- );
-
- if (intersects.length > 0) {
- const intersectionPoint = intersects[0].point.clone();
- if (points.length < 2) {
- setPoints([...points, intersectionPoint]);
- } else {
- setPoints([intersectionPoint]);
- }
- }
- }
- };
-
- const onMouseMove = () => {
- if (isLeftMouseDown) drag = true;
- };
-
- const onContextMenu = (evt: any) => {
- evt.preventDefault();
- if (!drag) {
- evt.preventDefault();
- setPoints([]);
- setTubeGeometry(null);
- }
- };
-
- if (toolMode === "MeasurementScale") {
- canvasElement.addEventListener("pointerdown", onMouseDown);
- canvasElement.addEventListener("pointermove", onMouseMove);
- canvasElement.addEventListener("pointerup", onMouseUp);
- canvasElement.addEventListener("contextmenu", onContextMenu);
- } else {
- resetMeasurement();
- setPoints([]);
- }
-
- return () => {
- canvasElement.removeEventListener("pointerdown", onMouseDown);
- canvasElement.removeEventListener("pointermove", onMouseMove);
- canvasElement.removeEventListener("pointerup", onMouseUp);
- canvasElement.removeEventListener("contextmenu", onContextMenu);
- };
- }, [toolMode, camera, raycaster, pointer, scene, points]);
-
- useFrame(() => {
- if (points.length === 1) {
- raycaster.setFromCamera(pointer, camera);
- const intersects = raycaster
- .intersectObjects(scene.children, true)
- .filter(
- (intersect) =>
- !intersect.object.name.includes("Roof") &&
- !intersect.object.name.includes("MeasurementReference") &&
- !intersect.object.name.includes("agv-collider") &&
- !intersect.object.name.includes("zonePlane") &&
- !intersect.object.name.includes("SelectionGroup") &&
- !intersect.object.name.includes("selectionAssetGroup") &&
- !intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
- !intersect.object.name.includes("SelectionGroupBoundingBox") &&
- !intersect.object.name.includes("SelectionGroupBoundingLine") &&
- intersect.object.type !== "GridHelper"
- );
-
- if (intersects.length > 0) {
- updateMeasurement(points[0], intersects[0].point);
- }
- } else if (points.length === 2) {
- updateMeasurement(points[0], points[1]);
- } else {
- resetMeasurement();
- }
- });
-
- const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => {
- const distance = start.distanceTo(end);
-
- const radius = THREE.MathUtils.clamp(distance * 0.02, MIN_RADIUS, MAX_RADIUS);
- const coneRadius = THREE.MathUtils.clamp(distance * 0.05, MIN_CONE_RADIUS, MAX_CONE_RADIUS);
- const coneHeight = THREE.MathUtils.clamp(distance * 0.2, MIN_CONE_HEIGHT, MAX_CONE_HEIGHT);
-
- setConeSize({ radius: coneRadius, height: coneHeight });
-
- const direction = new THREE.Vector3().subVectors(end, start).normalize();
-
- const offset = direction.clone().multiplyScalar(coneHeight * 0.5);
-
- let tubeStart = start.clone().add(offset);
- let tubeEnd = end.clone().sub(offset);
-
- tubeStart.y = Math.max(tubeStart.y, 0);
- tubeEnd.y = Math.max(tubeEnd.y, 0);
-
- const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]);
- setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false));
-
- setStartConePosition(tubeStart);
- setEndConePosition(tubeEnd);
- setStartConeQuaternion(getArrowOrientation(start, end));
- setEndConeQuaternion(getArrowOrientation(end, start));
+ if (keysPressed.current.has("x")) setAxisLock("x");
+ else if (keysPressed.current.has("y")) setAxisLock("y");
+ else if (keysPressed.current.has("z")) setAxisLock("z");
+ } else if (e.key === "Escape") {
+ setPoints([]);
+ setLinePoints(null);
+ setAxisLock(null);
+ }
};
- const resetMeasurement = () => {
- setTubeGeometry(null);
- setStartConePosition(null);
- setEndConePosition(null);
+ const handleKeyUp = (e: KeyboardEvent) => {
+ keysPressed.current.delete(e.key.toLowerCase());
+ if (keysPressed.current.has("x")) setAxisLock("x");
+ else if (keysPressed.current.has("y")) setAxisLock("y");
+ else if (keysPressed.current.has("z")) setAxisLock("z");
+ else setAxisLock(null);
};
- const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => {
- const direction = new THREE.Vector3()
- .subVectors(end, start)
- .normalize()
- .negate();
- const quaternion = new THREE.Quaternion();
- quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction);
- return quaternion;
+ window.addEventListener("keydown", handleKeyDown);
+ window.addEventListener("keyup", handleKeyUp);
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown);
+ window.removeEventListener("keyup", handleKeyUp);
+ };
+ }, []);
+
+ const getLineColor = useCallback(() => {
+ switch (axisLock) {
+ case "x":
+ return "#d94522"; // Red for X axis
+ case "y":
+ return "#22ab2e"; // Green for Y axis
+ case "z":
+ return "#227bd9"; // Blue for Z axis
+ default:
+ return "#b18ef1"; // Default purple
+ }
+ }, [axisLock]);
+
+ // Apply axis lock to a point
+ const applyAxisLock = useCallback(
+ (point: THREE.Vector3, referencePoint: THREE.Vector3) => {
+ const lockedPoint = point.clone();
+
+ switch (axisLock) {
+ case "x":
+ lockedPoint.y = referencePoint.y;
+ lockedPoint.z = referencePoint.z;
+ break;
+ case "y":
+ lockedPoint.x = referencePoint.x;
+ lockedPoint.z = referencePoint.z;
+ break;
+ case "z":
+ lockedPoint.x = referencePoint.x;
+ lockedPoint.y = referencePoint.y;
+ break;
+ }
+
+ return lockedPoint;
+ },
+ [axisLock]
+ );
+
+ useEffect(() => {
+ const canvasElement = gl.domElement;
+ let drag = false;
+ let isLeftMouseDown = false;
+
+ const onMouseDown = () => {
+ isLeftMouseDown = true;
+ drag = false;
};
- useEffect(() => {
- if (points.length === 2) {
- // console.log(points[0].distanceTo(points[1]));
+ const onMouseUp = (evt: any) => {
+ isLeftMouseDown = false;
+ if (evt.button === 0 && !drag) {
+ raycaster.setFromCamera(pointer, camera);
+ const intersects = raycaster
+ .intersectObjects(scene.children, true)
+ .filter(
+ (intersect) =>
+ !intersect.object.name.includes("Roof") &&
+ !intersect.object.name.includes("MeasurementReference") &&
+ !intersect.object.name.includes("agv-collider") &&
+ !intersect.object.name.includes("zonePlane") &&
+ !intersect.object.name.includes("SelectionGroup") &&
+ !intersect.object.name.includes("selectionAssetGroup") &&
+ !intersect.object.name.includes(
+ "SelectionGroupBoundingBoxLine"
+ ) &&
+ !intersect.object.name.includes("SelectionGroupBoundingBox") &&
+ !intersect.object.name.includes("SelectionGroupBoundingLine") &&
+ intersect.object.type !== "GridHelper"
+ );
+
+ if (intersects.length > 0) {
+ let intersectionPoint = intersects[0].point.clone();
+ if (axisLock && points.length > 0) {
+ intersectionPoint = applyAxisLock(
+ intersectionPoint,
+ points[points.length - 1]
+ );
+ }
+ if (points.length < 2) {
+ setPoints([...points, intersectionPoint]);
+ } else {
+ setPoints([intersectionPoint]);
+ }
}
- }, [points]);
+ }
+ };
- return (
-
- {startConePosition && (
-
-
-
-
- )}
- {endConePosition && (
-
-
-
-
- )}
- {tubeGeometry && (
-
-
-
- )}
+ const onMouseMove = () => {
+ if (isLeftMouseDown) drag = true;
+ };
- {startConePosition && endConePosition && (
-
-
- {(startConePosition.distanceTo(endConePosition) + (coneSize.height)).toFixed(2)} m
-
-
- )}
-
- );
+ const onContextMenu = (evt: any) => {
+ evt.preventDefault();
+ if (!drag) {
+ setPoints([]);
+ setLinePoints(null);
+ }
+ };
+
+ if (toolMode === "MeasurementScale") {
+ canvasElement.addEventListener("pointerdown", onMouseDown);
+ canvasElement.addEventListener("pointermove", onMouseMove);
+ canvasElement.addEventListener("pointerup", onMouseUp);
+ canvasElement.addEventListener("contextmenu", onContextMenu);
+ } else {
+ setPoints([]);
+ setLinePoints(null);
+ }
+
+ return () => {
+ canvasElement.removeEventListener("pointerdown", onMouseDown);
+ canvasElement.removeEventListener("pointermove", onMouseMove);
+ canvasElement.removeEventListener("pointerup", onMouseUp);
+ canvasElement.removeEventListener("contextmenu", onContextMenu);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [toolMode, camera, raycaster, pointer, scene, points, axisLock]);
+
+ useFrame(() => {
+ if (points.length === 1) {
+ // live preview for second point
+ raycaster.setFromCamera(pointer, camera);
+ const intersects = raycaster
+ .intersectObjects(scene.children, true)
+ .filter(
+ (intersect) =>
+ !intersect.object.name.includes("Roof") &&
+ !intersect.object.name.includes("MeasurementReference") &&
+ !intersect.object.name.includes("agv-collider") &&
+ !intersect.object.name.includes("zonePlane") &&
+ !intersect.object.name.includes("SelectionGroup") &&
+ !intersect.object.name.includes("selectionAssetGroup") &&
+ !intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
+ !intersect.object.name.includes("SelectionGroupBoundingBox") &&
+ !intersect.object.name.includes("SelectionGroupBoundingLine") &&
+ intersect.object.type !== "GridHelper"
+ );
+
+ if (intersects.length > 0) {
+ let tempEnd = intersects[0].point.clone();
+ if (axisLock) {
+ tempEnd = applyAxisLock(tempEnd, points[0]);
+ }
+ updateMeasurement(points[0], tempEnd);
+ }
+ } else if (points.length === 2) {
+ // second point already fixed
+ updateMeasurement(points[0], points[1]);
+ } else {
+ setLinePoints(null);
+ }
+ });
+
+ const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => {
+ setLinePoints([start.clone(), end.clone()]);
+ };
+
+ return (
+
+ {linePoints && (
+ <>
+ {/* Outline line */}
+
+
+ {/* Main line */}
+
+ >
+ )}
+
+ {points.map((point, index) => (
+
+
+
+ ))}
+
+ {linePoints && linePoints.length === 2 && (
+
+ {linePoints[0].distanceTo(linePoints[1]).toFixed(2)} m
+
+ )}
+
+ );
};
export default MeasurementTool;
diff --git a/app/src/modules/simulation/actions/human/actionHandler/useAssemblyHandler.ts b/app/src/modules/simulation/actions/human/actionHandler/useManufacturerHandler.ts
similarity index 72%
rename from app/src/modules/simulation/actions/human/actionHandler/useAssemblyHandler.ts
rename to app/src/modules/simulation/actions/human/actionHandler/useManufacturerHandler.ts
index 602de27..25dee10 100644
--- a/app/src/modules/simulation/actions/human/actionHandler/useAssemblyHandler.ts
+++ b/app/src/modules/simulation/actions/human/actionHandler/useManufacturerHandler.ts
@@ -2,7 +2,7 @@ import { useCallback } from "react";
import { useSceneContext } from "../../../../scene/sceneContext";
import { useProductContext } from "../../../products/productContext";
-export function useAssemblyHandler() {
+export function useManufacturerHandler() {
const { materialStore, humanStore, productStore } = useSceneContext();
const { getMaterialById } = materialStore();
const { getModelUuidByActionUuid } = productStore();
@@ -10,12 +10,12 @@ export function useAssemblyHandler() {
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
- const assemblyLogStatus = (materialUuid: string, status: string) => {
+ const manufactureLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
}
- const handleAssembly = useCallback((action: HumanAction, materialId?: string) => {
- if (!action || action.actionType !== 'assembly' || !materialId) return;
+ const handleManufacturer = useCallback((action: HumanAction, materialId?: string) => {
+ if (!action || action.actionType !== 'manufacturer' || !materialId) return;
const material = getMaterialById(materialId);
if (!material) return;
@@ -27,11 +27,11 @@ export function useAssemblyHandler() {
addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
- assemblyLogStatus(material.materialName, `performing assembly action`);
+ manufactureLogStatus(material.materialName, `performing manufacturer action`);
}, [getMaterialById]);
return {
- handleAssembly,
+ handleManufacturer,
};
}
\ No newline at end of file
diff --git a/app/src/modules/simulation/actions/human/useHumanActions.ts b/app/src/modules/simulation/actions/human/useHumanActions.ts
index ba29a1b..301669e 100644
--- a/app/src/modules/simulation/actions/human/useHumanActions.ts
+++ b/app/src/modules/simulation/actions/human/useHumanActions.ts
@@ -1,18 +1,18 @@
import { useEffect, useCallback } from 'react';
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
-import { useAssemblyHandler } from './actionHandler/useAssemblyHandler';
+import { useManufacturerHandler } from './actionHandler/useManufacturerHandler';
export function useHumanActions() {
const { handleWorker } = useWorkerHandler();
- const { handleAssembly } = useAssemblyHandler();
+ const { handleManufacturer } = useManufacturerHandler();
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
handleWorker(action, materialId);
}, [handleWorker]);
- const handleAssemblyAction = useCallback((action: HumanAction, materialId: string) => {
- handleAssembly(action, materialId);
- }, [handleAssembly]);
+ const handleManufactureAction = useCallback((action: HumanAction, materialId: string) => {
+ handleManufacturer(action, materialId);
+ }, [handleManufacturer]);
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
if (!action) return;
@@ -21,13 +21,13 @@ export function useHumanActions() {
case 'worker':
handleWorkerAction(action, materialId);
break;
- case 'assembly':
- handleAssemblyAction(action, materialId);
+ case 'manufacturer':
+ handleManufactureAction(action, materialId);
break;
default:
console.warn(`Unknown Human action type: ${action.actionType}`);
}
- }, [handleWorkerAction, handleAssemblyAction]);
+ }, [handleWorkerAction, handleManufactureAction]);
const cleanup = useCallback(() => {
}, []);
diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
index 18e8bfd..37c991c 100644
--- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
+++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
@@ -38,41 +38,35 @@ export function useRetrieveHandler() {
echo.info(`${materialUuid}, ${status}`);
}
- const createNewMaterial = useCallback((materialId: string, materialType: string, action: StorageAction) => {
+ const createNewMaterial = useCallback((materialId: string, materialType: string, action: StorageAction, visible: boolean = false) => {
const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
const pointUuid = getPointUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
if (!modelUuid || !pointUuid) return null;
const currentTime = performance.now();
- if (action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
- action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid &&
- action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid
- ) {
- const newMaterial: MaterialSchema = {
- materialId: materialId,
- materialName: `${materialType}-${Date.now()}`,
- materialType: materialType,
- isActive: false,
- isVisible: false,
- isPaused: false,
- isRendered: true,
- startTime: currentTime,
- previous: {
- modelUuid: modelUuid,
- pointUuid: pointUuid,
- actionUuid: action.actionUuid
- },
- current: {
- modelUuid: modelUuid,
- pointUuid: pointUuid,
- actionUuid: action.actionUuid
- },
- };
+ const newMaterial: MaterialSchema = {
+ materialId: materialId,
+ materialName: `${materialType}-${Date.now()}`,
+ materialType: materialType,
+ isActive: false,
+ isVisible: false,
+ isPaused: false,
+ isRendered: true,
+ startTime: currentTime,
+ previous: {
+ modelUuid: modelUuid,
+ pointUuid: pointUuid,
+ actionUuid: action.actionUuid
+ },
+ current: {
+ modelUuid: modelUuid,
+ pointUuid: pointUuid,
+ actionUuid: action.actionUuid
+ },
+ };
- addMaterial(newMaterial);
- return newMaterial;
- }
- return null;
+ addMaterial(newMaterial);
+ return newMaterial;
}, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productUuid]);
useEffect(() => {
@@ -112,12 +106,16 @@ export function useRetrieveHandler() {
getModelUuidByActionUuid(selectedProduct.productUuid, retrieval.action.actionUuid) ?? ''
);
- if (!storageUnit || storageUnit.currentLoad <= 0) {
+ if (!storageUnit) {
completedActions.push(actionUuid);
hasChanges = true;
return;
}
+ if (storageUnit.currentLoad <= 0) {
+ return;
+ }
+
if (retrieval.action.triggers.length === 0 || !retrieval.action.triggers[0]?.triggeredAsset) {
return;
}
@@ -170,9 +168,10 @@ export function useRetrieveHandler() {
if (retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid) {
const action = getActionByUuid(selectedProduct.productUuid, retrieval.action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid);
+ const storageAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (action && action.triggers.length > 0 && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid) {
- const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset.triggeredModel.modelUuid);
- if (model) {
+ const model = getEventByModelUuid(selectedProduct.productUuid, action?.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
+ if (model && storageAction) {
if (model.type === 'vehicle') {
const vehicle = getVehicleById(model.modelUuid);
if (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
@@ -180,7 +179,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
@@ -193,11 +192,11 @@ export function useRetrieveHandler() {
retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`);
}
}
- } else {
+ } else if (storageAction) {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
@@ -271,10 +270,15 @@ export function useRetrieveHandler() {
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) {
if (vehicle?.currentLoad < vehicle.point.action.loadCapacity) {
+ const triggeredAction = getActionByUuid(
+ selectedProduct.productUuid,
+ retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid || ''
+ );
+
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ triggeredAction as StorageAction
);
if (material) {
@@ -322,6 +326,9 @@ export function useRetrieveHandler() {
const triggeredModel = action.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid
? getEventByModelUuid(selectedProduct.productUuid, action.triggers[0].triggeredAsset.triggeredModel.modelUuid)
: null;
+
+ const storageAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
+
if (triggeredModel?.type === 'vehicle') {
const model = getVehicleById(triggeredModel.modelUuid);
if (model && !model.isActive && model.state === 'idle' && model.isPicking && model.currentLoad < model.point.action.loadCapacity) {
@@ -333,7 +340,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
@@ -359,7 +366,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
@@ -385,7 +392,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
@@ -417,7 +424,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
@@ -438,7 +445,7 @@ export function useRetrieveHandler() {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ storageAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
@@ -473,6 +480,7 @@ export function useRetrieveHandler() {
addCurrentActionToCrane(crane.modelUuid, action.actionUuid, material.materialType, material.materialId);
addCurrentMaterialToCrane(crane.modelUuid, material.materialType, material.materialId);
cranePickupLockRef.current.set(crane.modelUuid, true);
+ monitoredHumansRef.current.delete(human.modelUuid);
}, action.triggers[0].triggeredAsset.triggeredAction?.actionUuid)
}
monitoredHumansRef.current.add(human.modelUuid);
@@ -484,10 +492,15 @@ export function useRetrieveHandler() {
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) {
+ const triggeredAction = getActionByUuid(
+ selectedProduct.productUuid,
+ action?.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || ''
+ );
+
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
- storageUnit.point.action
+ triggeredAction as StorageAction
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts
index 99d8225..ec02943 100644
--- a/app/src/modules/simulation/actions/useActionHandler.ts
+++ b/app/src/modules/simulation/actions/useActionHandler.ts
@@ -41,7 +41,7 @@ export function useActionHandler() {
case 'store': case 'retrieve':
handleStorageAction(action as StorageAction, materialId as string);
break;
- case 'worker': case 'assembly':
+ case 'worker': case 'manufacturer':
handleHumanAction(action as HumanAction, materialId as string);
break;
case 'pickAndDrop':
diff --git a/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx
index 796a897..20788fa 100644
--- a/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx
+++ b/app/src/modules/simulation/crane/instances/animator/materialAnimator.tsx
@@ -13,12 +13,12 @@ export default function MaterialAnimator({ crane }: Readonly(false);
useEffect(() => {
- if (crane.isCarrying) {
+ if (crane.isCarrying && (crane.currentPhase === 'pickup-drop' || crane.currentPhase === 'dropping')) {
setIsRendered(true);
} else {
setIsRendered(false);
}
- }, [crane.isCarrying]);
+ }, [crane.isCarrying, crane.currentPhase]);
useFrame(() => {
const craneModel = scene.getObjectByProperty('uuid', crane.modelUuid);
diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx
index 96455b2..2af3223 100644
--- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx
+++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx
@@ -4,7 +4,6 @@ import { useFrame, useThree } from '@react-three/fiber';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
-import { dragAction } from '@use-gesture/react';
function PillarJibAnimator({
crane,
@@ -56,7 +55,6 @@ function PillarJibAnimator({
if (crane.currentPhase === 'init-pickup') {
if (crane.currentMaterials.length > 0) {
const materials = scene.getObjectsByProperty('uuid', crane.currentMaterials[0].materialId);
- console.log('materials: ', materials);
const material = materials.find((material) => material.visible === true);
if (material) {
const materialWorld = new THREE.Vector3();
diff --git a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx
index 11c06a4..4e18eb2 100644
--- a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx
+++ b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx
@@ -35,17 +35,15 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) {
if (!crane.isActive && crane.currentPhase === 'init' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length) {
setCurrentPhase(crane.modelUuid, 'init-pickup');
} else if (crane.currentPhase === 'picking' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length && !crane.isCarrying) {
- if (action.triggers.length > 0) {
- if (humanAsset?.animationState?.current === "working_standing" && humanAsset?.animationState?.isCompleted && humanId && humanAction && humanAction.actionType === 'operator') {
- setCurrentAnimation(humanId, 'idle', true, true, true);
- setIsCaryying(crane.modelUuid, true);
- setCurrentPhase(crane.modelUuid, 'pickup-drop');
- } else {
- setCurrentPhaseHuman(humanId, 'hooking');
- setHumanActive(humanId, true);
- setHumanState(humanId, 'running');
- setCurrentAnimation(humanId, 'working_standing', true, false, false);
- }
+ if (humanAsset?.animationState?.current === "working_standing" && humanAsset?.animationState?.isCompleted && humanId && humanAction && humanAction.actionType === 'operator') {
+ setCurrentAnimation(humanId, 'idle', true, true, true);
+ setIsCaryying(crane.modelUuid, true);
+ setCurrentPhase(crane.modelUuid, 'pickup-drop');
+ } else {
+ setCurrentPhaseHuman(humanId, 'hooking');
+ setHumanActive(humanId, true);
+ setHumanState(humanId, 'running');
+ setCurrentAnimation(humanId, 'working_standing', true, false, false);
}
} else if (crane.currentPhase === 'dropping' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length && crane.isCarrying && human.currentPhase === 'hooking') {
setCurrentPhaseHuman(humanId, 'loadPoint-unloadPoint');
diff --git a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx
index 736667b..21a85c3 100644
--- a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx
+++ b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx
@@ -144,8 +144,8 @@ function TriggerConnector() {
// Handle StorageUnit point
else if (event.type === "storageUnit" && '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}`,
@@ -155,7 +155,7 @@ function TriggerConnector() {
});
}
});
- }
+ });
}
// Handle Human point
else if (event.type === "human" && 'point' in event) {
diff --git a/app/src/modules/simulation/human/eventManager/useHumanEventManager.ts b/app/src/modules/simulation/human/eventManager/useHumanEventManager.ts
index 1dec0d9..4a41f6f 100644
--- a/app/src/modules/simulation/human/eventManager/useHumanEventManager.ts
+++ b/app/src/modules/simulation/human/eventManager/useHumanEventManager.ts
@@ -25,7 +25,7 @@ export function useHumanEventManager() {
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
const human = getHumanById(humanId);
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
- if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
+ if (!human || !action || (action.actionType !== 'manufacturer' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
if (!state) {
@@ -42,8 +42,8 @@ export function useHumanEventManager() {
existingAction.isMonitored = true;
existingAction.isCompleted = false;
}
- } else if (existingAction.actionType === 'assembly') {
- if (currentCount < existingAction.maxAssemblyCount) {
+ } else if (existingAction.actionType === 'manufacturer') {
+ if (currentCount < existingAction.maxManufactureCount) {
existingAction.callback = callback;
existingAction.isMonitored = true;
existingAction.isCompleted = false;
@@ -57,7 +57,7 @@ export function useHumanEventManager() {
actionUuid,
actionName: action.actionName,
maxLoadCount: action.loadCount ?? 0,
- maxAssemblyCount: action.assemblyCount ?? 0,
+ maxManufactureCount: action.manufactureCount ?? 0,
count: 0,
isMonitored: true,
isCompleted: false,
@@ -101,11 +101,11 @@ export function useHumanEventManager() {
if (currentAction.actionType === 'worker' || currentAction.actionType === 'operator') {
if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
conditionMet = true;
- } else if (action.actionType === 'assembly') {
+ } else if (action.actionType === 'manufacturer') {
conditionMet = true;
}
- } else if (currentAction.actionType === 'assembly') {
- if (action.actionType === 'assembly') {
+ } else if (currentAction.actionType === 'manufacturer') {
+ if (action.actionType === 'manufacturer') {
conditionMet = true;
} else if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
conditionMet = true;
@@ -122,7 +122,7 @@ export function useHumanEventManager() {
action.count = (action.count ?? 0) + 1;
action.isMonitored = false;
if (((action.actionType === 'worker' || action.actionType === 'operator') && action.count >= action.maxLoadCount) ||
- (action.actionType === 'assembly' && action.count >= action.maxAssemblyCount)) {
+ (action.actionType === 'manufacturer' && action.count >= action.maxManufactureCount)) {
action.isCompleted = true;
}
humanState.isCooldown = true;
diff --git a/app/src/modules/simulation/human/instances/animator/assemblerAnimator.tsx b/app/src/modules/simulation/human/instances/animator/manufacturerAnimator.tsx
similarity index 93%
rename from app/src/modules/simulation/human/instances/animator/assemblerAnimator.tsx
rename to app/src/modules/simulation/human/instances/animator/manufacturerAnimator.tsx
index d891d8f..45d7658 100644
--- a/app/src/modules/simulation/human/instances/animator/assemblerAnimator.tsx
+++ b/app/src/modules/simulation/human/instances/animator/manufacturerAnimator.tsx
@@ -6,14 +6,14 @@ import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useRese
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
-interface AssemblerAnimatorProps {
+interface ManufacturerAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
human: HumanStatus;
}
-function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly) {
+function ManufacturerAnimator({ path, handleCallBack, human, reset }: Readonly) {
const { humanStore, assetStore, productStore } = useSceneContext();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
@@ -27,16 +27,16 @@ function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly(0);
const completedRef = useRef(false);
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
- const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.assemblyPoint?.rotation || [0, 0, 0]);
+ const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.manufacturePoint?.rotation || [0, 0, 0]);
const [restRotation, setRestingRotation] = useState(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (!human.currentAction?.actionUuid) return;
- if (human.currentPhase === 'init-assembly' && path.length > 0) {
+ if (human.currentPhase === 'init-manufacture' && path.length > 0) {
setCurrentPath(path);
- setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null);
+ setObjectRotation((action as HumanAction)?.manufacturePoint?.rotation ?? null);
}
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
@@ -169,4 +169,4 @@ function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly {
return (
<>
- {hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
+ {hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && human.currentPhase !== 'drop-pickup') && (
0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
+ if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && 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 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
+ if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && human.currentPhase !== 'drop-pickup')) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
diff --git a/app/src/modules/simulation/human/instances/instance/actions/assemberInstance.tsx b/app/src/modules/simulation/human/instances/instance/actions/manufacturerInstance.tsx
similarity index 89%
rename from app/src/modules/simulation/human/instances/instance/actions/assemberInstance.tsx
rename to app/src/modules/simulation/human/instances/instance/actions/manufacturerInstance.tsx
index 881a2ec..ae74a14 100644
--- a/app/src/modules/simulation/human/instances/instance/actions/assemberInstance.tsx
+++ b/app/src/modules/simulation/human/instances/instance/actions/manufacturerInstance.tsx
@@ -8,9 +8,9 @@ import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTrigge
import { useSceneContext } from '../../../../../scene/sceneContext';
import { useProductContext } from '../../../../products/productContext';
-import AssemblerAnimator from '../../animator/assemblerAnimator';
+import ManufacturerAnimator from '../../animator/manufacturerAnimator';
-function AssemblerInstance({ human }: { human: HumanStatus }) {
+function ManufacturerInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
@@ -99,24 +99,24 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
if (isPlaying) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
- if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
+ if (!action || !(action as HumanAction).manufacturePoint || (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]);
+ const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.manufacturePoint?.position || [0, 0, 0]);
setPath(toPickupPath);
setHumanState(human.modelUuid, 'idle');
- setCurrentPhase(human.modelUuid, 'init-assembly');
+ setCurrentPhase(human.modelUuid, 'init-manufacture');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
- humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
+ humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');
} 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(human.modelUuid, 'assembling');
+ setCurrentPhase(human.modelUuid, 'manufacturing');
setHumanActive(human.modelUuid, true);
processStartTimeRef.current = performance.now();
@@ -127,16 +127,16 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
hasLoggedCompleted.current = false;
if (!processAnimationIdRef.current) {
- processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
+ processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
}
}
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
- if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
+ if ((action as HumanAction).manufacturePoint && human.currentPhase === 'manufacturing') {
setHumanState(human.modelUuid, 'idle');
setCurrentPhase(human.modelUuid, 'waiting');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
- humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
+ humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');
decrementHumanLoad(human.modelUuid, 1);
const material = removeLastMaterial(human.modelUuid);
@@ -150,7 +150,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
}
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
- const trackAssemblyProcess = useCallback(() => {
+ const trackManufactureProcess = useCallback(() => {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const now = performance.now();
@@ -163,7 +163,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
if (!lastPauseTimeRef.current) {
lastPauseTimeRef.current = now;
}
- processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
+ processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
return;
} else if (lastPauseTimeRef.current) {
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
@@ -178,7 +178,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
if (human.currentMaterials.length > 0) {
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
}
- humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
+ humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in manufacture.`);
}
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
@@ -188,27 +188,27 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
cancelAnimationFrame(processAnimationIdRef.current);
processAnimationIdRef.current = null;
}
- humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
+ humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed manufacture process.`);
return;
}
- processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
+ processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
}, [human.modelUuid, human.currentMaterials]);
function handleCallBack() {
- if (human.currentPhase === 'init-assembly') {
+ if (human.currentPhase === 'init-manufacture') {
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');
+ humanStatus(human.modelUuid, 'Reached manufacture point, waiting for material');
setPath([]);
}
}
return (
<>
- {
+ setHumanState(human.modelUuid, 'running');
+ setHumanActive(human.modelUuid, true);
+ setCurrentAnimation(human.modelUuid, 'working_standing', true, false, false);
+ }, 1)
}
} else {
reset()
diff --git a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx
index c5645af..078f203 100644
--- a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx
+++ b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx
@@ -14,7 +14,7 @@ function WorkerInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
- const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
+ const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore, humanEventManagerRef } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore();
@@ -108,7 +108,7 @@ function WorkerInstance({ human }: { human: HumanStatus }) {
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);
@@ -129,20 +129,47 @@ function WorkerInstance({ human }: { human: HumanStatus }) {
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (human.currentMaterials.length > 0 && human.currentLoad > 0 && humanAsset?.animationState?.current !== 'pickup') {
- if (human.currentMaterials[0]?.materialId) {
- setIsVisible(human.currentMaterials[0]?.materialId, false);
- }
- setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
+ setTimeout(() => {
+ if (human.currentMaterials[0]?.materialId) {
+ setIsVisible(human.currentMaterials[0]?.materialId, false);
+ }
+ humanStatus(human.modelUuid, 'Started to pickup in pickup point');
+ setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
+ }, 1)
}
} 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(human.modelUuid, 'drop-pickup');
- setHumanState(human.modelUuid, 'running');
- setHumanActive(human.modelUuid, true);
- setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
- humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
+ // const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
+ // setPath(dropToPickup);
+ // setCurrentPhase(human.modelUuid, 'drop-pickup');
+ // setHumanState(human.modelUuid, 'running');
+ // setHumanActive(human.modelUuid, true);
+ // setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
+ if (humanEventManagerRef.current) {
+ let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === human.modelUuid);
+ if (state) {
+ const existingAction = state.actionQueue.find(a => a.actionUuid === action.actionUuid);
+ if (existingAction) {
+ const currentCount = existingAction.count ?? 0;
+ if (existingAction.actionType === 'worker') {
+ if (currentCount < existingAction.maxLoadCount) {
+ const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
+ setPath(dropToPickup);
+ setCurrentPhase(human.modelUuid, 'drop-pickup');
+ setHumanState(human.modelUuid, 'running');
+ setHumanActive(human.modelUuid, true);
+ setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
+ } else {
+ setCurrentPhase(human.modelUuid, 'picking');
+ setHumanState(human.modelUuid, 'idle');
+ setHumanActive(human.modelUuid, false);
+ setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
+ humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
+ }
+ }
+ }
+ }
+ }
}
}
} else {
@@ -233,15 +260,13 @@ function WorkerInstance({ human }: { human: HumanStatus }) {
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
- if (model.point.action.actionType === 'store') {
- loopMaterialDropToStorage(
- human.modelUuid,
- human.currentLoad,
- model.modelUuid,
- model.point.action.storageCapacity,
- (action as HumanAction)
- );
- }
+ loopMaterialDropToStorage(
+ human.modelUuid,
+ human.currentLoad,
+ model.modelUuid,
+ model.storageCapacity,
+ (action as HumanAction)
+ );
} else {
requestAnimationFrame(checkAnimation);
}
diff --git a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx
index 1532e5c..593e82a 100644
--- a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx
+++ b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx
@@ -4,7 +4,7 @@ import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import MaterialAnimator from '../animator/materialAnimator';
-import AssemblerInstance from './actions/assemberInstance';
+import ManufacturerInstance from './actions/manufacturerInstance';
import WorkerInstance from './actions/workerInstance';
import OperatorInstance from './actions/operatorInstance';
@@ -84,8 +84,8 @@ function HumanInstance({ human }: { human: HumanStatus }) {
{action && action.actionType === 'worker' &&
}
- {action && action.actionType === 'assembly' &&
-
+ {action && action.actionType === 'manufacturer' &&
+
}
{action && action.actionType === 'operator' &&
diff --git a/app/src/modules/simulation/human/instances/instance/humanUi.tsx b/app/src/modules/simulation/human/instances/instance/humanUi.tsx
index 043811a..abaceb5 100644
--- a/app/src/modules/simulation/human/instances/instance/humanUi.tsx
+++ b/app/src/modules/simulation/human/instances/instance/humanUi.tsx
@@ -10,16 +10,16 @@ import { useVersionContext } from '../../../../builder/version/versionContext';
import { useParams } from 'react-router-dom';
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
-import assembly from "../../../../../assets/gltf-glb/ui/human-ui-assembly.glb";
+import manufacture from "../../../../../assets/gltf-glb/ui/human-ui-manufacture.glb";
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
function HumanUi() {
const { scene: startScene } = useGLTF(startPoint) as any;
const { scene: endScene } = useGLTF(startEnd) as any;
- const { scene: assemblyScene } = useGLTF(assembly) as any;
+ const { scene: manufactureScene } = useGLTF(manufacture) as any;
const startMarker = useRef(null);
const endMarker = useRef(null);
- const assemblyMarker = useRef(null);
+ const manufactureMarker = useRef(null);
const outerGroup = useRef(null);
const prevMousePos = useRef({ x: 0, y: 0 });
const { controls, raycaster, camera } = useThree();
@@ -31,9 +31,9 @@ function HumanUi() {
const { updateEvent, 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 [manufacturePosition, setManufacturePosition] = 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 [manufactureRotation, setManufactureRotation] = useState<[number, number, number]>([0, 0, 0]);
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
const { isDragging, setIsDragging } = useIsDragging();
const { isRotating, setIsRotating } = useIsRotating();
@@ -51,7 +51,7 @@ function HumanUi() {
const { projectId } = useParams();
const currentAction = getActionByUuid(selectedProduct.productUuid, selectedAction.actionId || '');
- const isAssembly = currentAction?.actionType === 'assembly';
+ const isManufacture = currentAction?.actionType === 'manufacturer';
const updateBackend = (
productName: string,
@@ -90,15 +90,15 @@ function HumanUi() {
const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId);
if (!action) return;
- if (isAssembly) {
- if (action.assemblyPoint?.position && outerGroup.current) {
- const worldPos = new Vector3(...action.assemblyPoint.position);
+ if (isManufacture) {
+ if (action.manufacturePoint?.position && outerGroup.current) {
+ const worldPos = new Vector3(...action.manufacturePoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
- setAssemblyPosition([localPosition.x, 1, localPosition.z]);
- setAssemblyRotation(action.assemblyPoint.rotation || [0, 0, 0]);
+ setManufacturePosition([localPosition.x, 1, localPosition.z]);
+ setManufactureRotation(action.manufacturePoint.rotation || [0, 0, 0]);
} else {
- setAssemblyPosition([0, 1, 0]);
- setAssemblyRotation([0, 0, 0]);
+ setManufacturePosition([0, 1, 0]);
+ setManufactureRotation([0, 0, 0]);
}
} else {
if (action.pickUpPoint?.position && outerGroup.current) {
@@ -125,8 +125,8 @@ function HumanUi() {
const handlePointerDown = (
e: any,
- state: "start" | "end" | "assembly",
- rotation: "start" | "end" | "assembly"
+ state: "start" | "end" | "manufacture",
+ rotation: "start" | "end" | "manufacture"
) => {
e.stopPropagation();
const intersection = new Vector3();
@@ -153,7 +153,7 @@ function HumanUi() {
const marker =
state === "start" ? startMarker.current :
state === "end" ? endMarker.current :
- assemblyMarker.current;
+ manufactureMarker.current;
if (marker && localPoint) {
const markerPos = new Vector3().copy(marker.position);
dragOffset.current.copy(markerPos.sub(localPoint));
@@ -176,17 +176,17 @@ function HumanUi() {
const updatedActions = selectedHuman.point.actions.map(action => {
if (action.actionUuid !== currentAction.actionUuid) return action;
- if (isAssembly) {
- if (!assemblyMarker.current || !outerGroup.current) return action;
+ if (isManufacture) {
+ if (!manufactureMarker.current || !outerGroup.current) return action;
- const worldPosAssembly = new Vector3(...assemblyPosition);
- const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());
+ const worldPosManufacture = new Vector3(...manufacturePosition);
+ const globalManufacturePosition = outerGroup.current.localToWorld(worldPosManufacture.clone());
return {
...action,
- assemblyPoint: {
- position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
- rotation: assemblyRotation
+ manufacturePoint: {
+ position: [globalManufacturePosition.x, globalManufacturePosition.y, globalManufacturePosition.z] as [number, number, number],
+ rotation: manufactureRotation
},
};
} else {
@@ -246,8 +246,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]);
+ } else if (isDragging === "manufacture") {
+ setManufacturePosition([localPoint.x, 1, localPoint.z]);
}
});
@@ -260,7 +260,7 @@ function HumanUi() {
const marker =
isRotating === "start" ? startMarker.current :
isRotating === "end" ? endMarker.current :
- assemblyMarker.current;
+ manufactureMarker.current;
if (marker) {
const rotationSpeed = 10;
@@ -279,7 +279,7 @@ function HumanUi() {
marker.rotation.z,
]);
} else {
- setAssemblyRotation([
+ setManufactureRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
@@ -303,7 +303,7 @@ function HumanUi() {
return () => {
window.removeEventListener("pointerup", handleGlobalPointerUp);
};
- }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyPosition, assemblyRotation]);
+ }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, manufacturePosition, manufactureRotation]);
return (
<>
@@ -313,16 +313,16 @@ function HumanUi() {
ref={outerGroup}
rotation={[0, Math.PI, 0]}
>
- {isAssembly ? (
+ {isManufacture ? (
({
- materialType: storageAction.materialType || 'Default material',
+ if (event.storageCount > 0) {
+ const materials = Array.from({ length: event.storageCount }, () => ({
+ materialType: event.materialType || 'Default material',
materialId: THREE.MathUtils.generateUUID()
}));
setCurrentMaterials(event.modelUuid, materials);
- updateCurrentLoad(event.modelUuid, storageAction.storageCapacity);
+ updateCurrentLoad(event.modelUuid, event.storageCount);
} else {
setCurrentMaterials(event.modelUuid, []);
updateCurrentLoad(event.modelUuid, 0);
diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
index 8eb1af9..d7a5058 100644
--- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
@@ -34,7 +34,7 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone
const { armBotStore, productStore, materialStore } = useSceneContext();
const { getArmBotById } = armBotStore();
const { getMaterialById, getMaterialPosition } = materialStore();
- const { getEventByModelUuid, getActionByUuid, getPointByUuid } = productStore();
+ const { getEventByModelUuid, getActionByUuid, getPointByUuid, getTriggeringModels } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { scene } = useThree();
@@ -176,7 +176,8 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone
if (armbotStatus && currentMaterial && currentAction && (currentPhase === 'rest-to-start' || currentPhase === 'start-to-end' || currentPhase === 'end-to-rest')) {
const materialData = getMaterialById(currentMaterial);
if (materialData) {
- const prevModel = getEventByModelUuid(selectedProduct.productUuid, materialData.current.modelUuid);
+ const triggeringModel = getTriggeringModels(selectedProduct.productUuid, currentAction.actionUuid);
+ const prevModel = triggeringModel[0] || null;
const nextModel = getEventByModelUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || '');
const nextPoint = getPointByUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || '', currentAction?.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '');
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index e1595b6..ee196f5 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -42,11 +42,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
const lastRemoved = useRef<{ type: string, materialId: string, modelId: string } | null>(null);
- function firstFrame() {
- startTime = performance.now();
- step();
- }
-
const action = getActionByUuid(selectedProduct.productUuid, armBot.currentAction?.actionUuid || '');
const handlePickUpTrigger = () => {
@@ -109,68 +104,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
}
}
- function step() {
- if (isPausedRef.current) {
- if (!pauseTimeRef.current) {
- pauseTimeRef.current = performance.now();
- }
- requestAnimationFrame(() => step());
- return;
- }
- if (pauseTimeRef.current) {
- const pauseDuration = performance.now() - pauseTimeRef.current;
- startTime += pauseDuration;
- pauseTimeRef.current = null;
- }
- const elapsedTime = performance.now() - startTime;
- if (elapsedTime < 1000) {
- // Wait until 1500ms has passed
- requestAnimationFrame(step);
- return;
- }
- if (currentPhase === "picking") {
- setArmBotActive(armBot.modelUuid, true);
- setArmBotState(armBot.modelUuid, "running");
- setCurrentPhase("start-to-end");
- startTime = 0
- if (!action) return;
- const startPoint = (action as RoboticArmAction).process.startPoint;
- const endPoint = (action as RoboticArmAction).process.endPoint;
- if (startPoint && endPoint) {
- let curve = createCurveBetweenTwoPoints(
- new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]),
- new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]));
- if (curve) {
- logStatus(armBot.modelUuid, "picking the object");
- setPath(curve.points.map(point => [point.x, point.y, point.z]))
-
- handlePickUpTrigger();
-
- }
- }
- logStatus(armBot.modelUuid, "Moving armBot from start point to end position.")
- } else if (currentPhase === "dropping") {
- setArmBotActive(armBot.modelUuid, true);
- setArmBotState(armBot.modelUuid, "running");
- setCurrentPhase("end-to-rest");
- startTime = 0;
- if (!action) return;
- const endPoint = (action as RoboticArmAction).process.endPoint;
- if (endPoint) {
-
- let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition);
- if (curve) {
- logStatus(armBot.modelUuid, "dropping the object");
- setPath(curve.points.map(point => [point.x, point.y, point.z]));
-
- handleDropTrigger();
-
- }
- }
- logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
- }
- }
-
useEffect(() => {
isPausedRef.current = isPaused;
}, [isPaused]);
@@ -268,7 +201,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
useEffect(() => {
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
if (!isReset && isPlaying) {
- //Moving armBot from initial point to rest position.
if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") {
if (targetBones) {
setArmBotActive(armBot.modelUuid, true)
@@ -280,13 +212,9 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
}
}
logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
- }
- //Waiting for trigger.
- else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
+ } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
- }
- //Moving to pickup point
- else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
+ } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
if (armBot.currentAction) {
setArmBotActive(armBot.modelUuid, true);
@@ -302,14 +230,45 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
}
}
logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.")
- }
- // Moving to Pick to Drop position
- else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
- requestAnimationFrame(firstFrame);
- }
- //Moving to drop point to restPosition
- else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
- requestAnimationFrame(firstFrame);
+ } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
+ setTimeout(() => {
+ setArmBotActive(armBot.modelUuid, true);
+ setArmBotState(armBot.modelUuid, "running");
+ setCurrentPhase("start-to-end");
+ startTime = 0
+ if (!action) return;
+ const startPoint = (action as RoboticArmAction).process.startPoint;
+ const endPoint = (action as RoboticArmAction).process.endPoint;
+ if (startPoint && endPoint) {
+ let curve = createCurveBetweenTwoPoints(new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]));
+ if (curve) {
+ logStatus(armBot.modelUuid, "picking the object");
+ setPath(curve.points.map(point => [point.x, point.y, point.z]))
+ handlePickUpTrigger();
+ }
+ }
+ logStatus(armBot.modelUuid, "Moving armBot from start point to end position.")
+ }, 100)
+ } else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
+ setTimeout(() => {
+ setArmBotActive(armBot.modelUuid, true);
+ setArmBotState(armBot.modelUuid, "running");
+ setCurrentPhase("end-to-rest");
+ startTime = 0;
+ if (!action) return;
+ const endPoint = (action as RoboticArmAction).process.endPoint;
+ if (endPoint) {
+ let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition);
+ if (curve) {
+ logStatus(armBot.modelUuid, "dropping the object");
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+
+ handleDropTrigger();
+
+ }
+ }
+ logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
+ }, 100)
}
} else {
logStatus(armBot.modelUuid, "Simulation Play Exited")
@@ -348,7 +307,7 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
setCurrentPhase("rest");
setPath([])
}
- else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") {
+ else if (armBot.state == "running" && currentPhase == "rest-to-start") {
logStatus(armBot.modelUuid, "Callback triggered: pick.");
setArmBotActive(armBot.modelUuid, false)
setArmBotState(armBot.modelUuid, "running")
diff --git a/app/src/modules/simulation/simulator/functions/determineExecutionOrder.ts b/app/src/modules/simulation/simulator/functions/determineExecutionOrder.ts
index 09be2f2..f06992b 100644
--- a/app/src/modules/simulation/simulator/functions/determineExecutionOrder.ts
+++ b/app/src/modules/simulation/simulator/functions/determineExecutionOrder.ts
@@ -1,12 +1,13 @@
import { extractTriggersFromPoint } from "./extractTriggersFromPoint";
-export function determineExecutionOrder(products: productsSchema): PointsScheme[] {
+export function determineExecutionOrder(products: productsSchema): Action[] {
// Create maps for all events and points
const eventMap = new Map();
const pointMap = new Map();
const allPoints: PointsScheme[] = [];
+ const spawnActions: Action[] = [];
- // First pass: collect all points
+ // First pass: collect all points and identify spawn actions
products.forEach(product => {
product.eventDatas.forEach(event => {
eventMap.set(event.modelUuid, event);
@@ -15,6 +16,11 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
event.points.forEach(point => {
pointMap.set(point.uuid, point);
allPoints.push(point);
+
+ // Check for spawn actions in conveyors
+ if (point.action.actionType === 'spawn') {
+ spawnActions.push(point.action);
+ }
});
} else if (event.type === 'vehicle' ||
event.type === 'machine' ||
@@ -25,6 +31,16 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
) {
pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point);
+
+ // Check for spawn actions in storage units and other types
+ if (event.type === 'storageUnit') {
+ const storagePoint = event.point as StoragePointSchema;
+ storagePoint.actions.forEach(action => {
+ if (action.actionType === 'retrieve') {
+ spawnActions.push(action);
+ }
+ });
+ }
}
});
});
@@ -33,11 +49,19 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
const graph = new Map();
const reverseGraph = new Map();
const allTriggeredPoints = new Set();
+ const actionMap = new Map(); // Map point UUID to its primary action
allPoints.forEach(point => {
const triggers = extractTriggersFromPoint(point);
const dependencies: string[] = [];
+ // Store the primary action for this point
+ if ('action' in point) {
+ actionMap.set(point.uuid, point.action);
+ } else if ('actions' in point && point.actions.length > 0) {
+ actionMap.set(point.uuid, point.actions[0]); // Use first action as primary
+ }
+
triggers.forEach(trigger => {
const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
if (targetUuid && pointMap.has(targetUuid)) {
@@ -58,15 +82,12 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
const rootPoints = allPoints
.filter(point => !allTriggeredPoints.has(point.uuid))
.filter(point => {
- // Only include roots that actually have triggers pointing FROM them
const triggers = extractTriggersFromPoint(point);
return triggers.some(t => t.triggeredAsset?.triggeredPoint?.pointUuid);
});
// If no root points found but we have triggered points, find the earliest triggers
if (rootPoints.length === 0 && allTriggeredPoints.size > 0) {
- // This handles cases where we have circular dependencies
- // but still want to include the triggered points
const minTriggerCount = Math.min(
...Array.from(allTriggeredPoints)
.map(uuid => (graph.get(uuid) || []).length)
@@ -105,11 +126,18 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
// Start processing from root points
rootPoints.forEach(root => visit(root.uuid));
- // Convert UUIDs back to points and filter out untriggered points
- const triggeredPoints = order
- .map(uuid => pointMap.get(uuid)!)
- .filter(point => allTriggeredPoints.has(point.uuid) ||
- rootPoints.some(root => root.uuid === point.uuid));
+ // Convert UUIDs back to actions
+ const triggeredActions = order
+ .map(uuid => actionMap.get(uuid))
+ .filter((action): action is Action => action !== undefined);
- return triggeredPoints;
+ // Combine triggered actions with ALL spawn actions
+ const allExecutionActions = [...triggeredActions, ...spawnActions];
+
+ // Remove duplicate actions while preserving order
+ const uniqueActions = allExecutionActions.filter((action, index, array) =>
+ array.findIndex(a => a.actionUuid === action.actionUuid) === index
+ );
+
+ return uniqueActions;
}
\ No newline at end of file
diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx
index 869ffa9..21bf97a 100644
--- a/app/src/modules/simulation/simulator/simulator.tsx
+++ b/app/src/modules/simulation/simulator/simulator.tsx
@@ -21,12 +21,13 @@ function Simulator() {
if (!product) return;
const executionOrder = determineExecutionOrder([product]);
- executionOrder.forEach(point => {
- const action = 'actions' in point ? point.actions[0] : point.action;
+
+ executionOrder.forEach(action => {
handleAction(action);
});
}, [products, isPlaying, isReset, selectedProduct]);
+
return (
<>
diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
index 9660a6d..82dc4bf 100644
--- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
+++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts
@@ -106,6 +106,7 @@ export function useTriggerHandler() {
// Handle current action using Event Manager
addVehicleToMonitor(vehicle.modelUuid, () => {
+ setIsVisible(materialId, false);
handleAction(action, materialId);
})
}
@@ -392,7 +393,7 @@ export function useTriggerHandler() {
}
}
} else if (toEvent?.type === 'crane') {
- // Transfer to Human
+ // Transfer to Crane
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
@@ -527,24 +528,11 @@ export function useTriggerHandler() {
if (action && armBot) {
- if (armBot.isActive === false && armBot.state === 'idle') {
-
- // Handle current action from arm bot
+ addArmBotToMonitor(armBot.modelUuid, () => {
setIsVisible(materialId, false);
handleAction(action, materialId);
-
- } else {
-
- addArmBotToMonitor(armBot.modelUuid,
- () => {
- setIsVisible(materialId, false);
-
- handleAction(action, materialId);
- }
- )
-
- }
+ })
}
}
}
@@ -575,7 +563,7 @@ export function useTriggerHandler() {
if (action && storageUnit) {
- if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) {
+ if (storageUnit.currentLoad < storageUnit.storageCapacity) {
// Handle current action from vehicle
handleAction(action, materialId);
@@ -622,7 +610,7 @@ export function useTriggerHandler() {
}
}
} else if (toEvent?.type === 'crane') {
- // Vehicle to Human
+ // Vehicle to Crane
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
@@ -1135,7 +1123,7 @@ export function useTriggerHandler() {
if (action && storageUnit) {
- if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) {
+ if (storageUnit.currentLoad < storageUnit.storageCapacity) {
// Handle current action from vehicle
handleAction(action, materialId);
@@ -1353,7 +1341,7 @@ export function useTriggerHandler() {
handleAction(action, material.materialId);
}
- } else if (material && action.actionType === 'assembly') {
+ } else if (material && action.actionType === 'manufacturer') {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
@@ -1782,7 +1770,7 @@ export function useTriggerHandler() {
if (action && storageUnit) {
- if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) {
+ if (storageUnit.currentLoad < storageUnit.storageCapacity) {
// Handle current action from vehicle
handleAction(action, materialId);
@@ -1801,25 +1789,25 @@ export function useTriggerHandler() {
}
} else if (fromEvent?.type === 'crane') {
if (toEvent?.type === 'transfer') {
- // Crane Unit to Transfer
+ // Crane to Transfer
} else if (toEvent?.type === 'vehicle') {
- // Crane Unit to Vehicle
+ // Crane to Vehicle
} else if (toEvent?.type === 'machine') {
- // Crane Unit to Machine
+ // Crane to Machine
} else if (toEvent?.type === 'roboticArm') {
- // Crane Unit to Robotic Arm
+ // Crane to Robotic Arm
} else if (toEvent?.type === 'storageUnit') {
- // Crane Unit to Storage Unit
+ // Crane to Storage Unit
} else if (toEvent?.type === 'human') {
- // Crane Unit to Human
+ // Crane to Human
} else if (toEvent?.type === 'crane') {
- // Crane Unit to Human
+ // Crane to Human
}
}
diff --git a/app/src/modules/simulation/ui3d/StorageContentUi.tsx b/app/src/modules/simulation/ui3d/StorageContentUi.tsx
index 38f62dc..7d02932 100644
--- a/app/src/modules/simulation/ui3d/StorageContentUi.tsx
+++ b/app/src/modules/simulation/ui3d/StorageContentUi.tsx
@@ -31,7 +31,7 @@ const StorageContentUi: React.FC = ({ storageUnit }) => {
status={storageUnit.state}
count={storageUnit.currentLoad}
enableStatue={false}
- totalCapacity={storageUnit.point.action.storageCapacity}
+ totalCapacity={storageUnit.storageCapacity}
/>