Merge branch 'main' into rtViz

This commit is contained in:
2025-03-27 18:10:13 +05:30
51 changed files with 2815 additions and 14707 deletions

View File

@@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
import { useActiveTool, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react";
import * as THREE from "three";
@@ -11,24 +11,27 @@ import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFl
import DeleteFloorItems from "../geomentries/assets/deleteFloorItems";
import loadInitialFloorItems from "../../scene/IntialLoad/loadInitialFloorItems";
import addAssetModel from "../geomentries/assets/addAssetModel";
// import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore";
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url));
// const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane }: any) => {
const state: Types.ThreeState = useThree();
const { raycaster, camera, controls, pointer }: any = state;
const { renderDistance, setRenderDistance } = useRenderDistance();
const { toggleView, setToggleView } = useToggleView();
const { raycaster, controls }: any = state;
const { renderDistance } = useRenderDistance();
const { toggleView } = useToggleView();
const { floorItems, setFloorItems } = useFloorItems();
const { camMode, setCamMode } = useCamMode();
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode, setTransformMode } = useTransformMode();
const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem();
const { activeTool, setActiveTool } = useActiveTool();
const { camMode } = useCamMode();
const { deleteModels } = useDeleteModels();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setselectedFloorItem } = useselectedFloorItem();
const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
@@ -38,32 +41,57 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
// Load initial floor items
const email = localStorage.getItem('email');
const organization = (email!.split("@")[1]).split(".")[0];
// const email = localStorage.getItem('email');
// const organization = (email!.split("@")[1]).split(".")[0];
let totalAssets = 0;
let loadedAssets = 0;
// getFloorItems(organization).then((data) => {
// gltfLoaderWorker.postMessage({ FloorItems: data })
// })
const updateLoadingProgress = (progress: number) => {
if (progress < 100) {
setLoadingProgress(progress);
} else if (progress === 100) {
setTimeout(() => {
setLoadingProgress(100);
setTimeout(() => {
setLoadingProgress(0);
}, 1500);
}, 1000);
}
};
// gltfLoaderWorker.onmessage = async (event) => {
// if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
// const blobUrl = URL.createObjectURL(event.data.modelBlob);
getFloorItems(organization).then((data) => {
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
);
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
updateLoadingProgress(100);
return;
}
gltfLoaderWorker.postMessage({ floorItems: data });
});
// loader.load(blobUrl, (gltf) => {
// URL.revokeObjectURL(blobUrl);
// THREE.Cache.remove(blobUrl);
// THREE.Cache.add(event.data.modelID, gltf);
// });
gltfLoaderWorker.onmessage = async (event) => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
const blobUrl = URL.createObjectURL(event.data.modelBlob);
// } else if (event.data.message === "done") {
// loadInitialFloorItems(itemsGroup, setFloorItems);
// }
// }
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(event.data.modelID, gltf);
loadedAssets++;
const progress = Math.round((loadedAssets / totalAssets) * 100);
updateLoadingProgress(progress);
loadInitialFloorItems(itemsGroup, setFloorItems);
if (loadedAssets === totalAssets) {
loadInitialFloorItems(itemsGroup, setFloorItems);
updateLoadingProgress(100);
}
});
}
};
}, []);
useEffect(() => {
@@ -253,12 +281,20 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
event.preventDefault();
};
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("dblclick", onDblClick);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
if (activeModule === "builder") {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("dblclick", onDblClick);
canvasElement.addEventListener("drop", onDrop);
canvasElement.addEventListener("dragover", onDragOver);
} else {
if (controls) {
const target = controls.getTarget(new THREE.Vector3());
controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
}
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
@@ -268,7 +304,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool]);
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
useFrame(() => {
if (controls)

View File

@@ -37,6 +37,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
<div className="assetPreview">
<div className="image-preview">
<img src={assetImage} alt="" />
{/* Add canvas here */}
</div>
<div className="asset-details-preview">

View File

@@ -8,24 +8,51 @@ import {
} from "../../components/icons/marketPlaceIcons";
import assetImage from "../../assets/image/image.png";
const Card: React.FC = () => {
interface CardProps {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
onSelectCard: (cardData: {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
}) => void;
}
const Card: React.FC<CardProps> = ({
assetName,
uploadedOn,
price,
rating,
views,
onSelectCard,
}) => {
const handleCardSelect = () => {
onSelectCard({ assetName, uploadedOn, price, rating, views });
};
return (
<div className="card-container">
<div className="icon">
<DownloadIcon />
</div>
<div className="image-container">
<img src={assetImage} alt="" />
<img src={assetImage} alt={assetName} />
</div>
<div className="assets-container">
<div className="name-container">
<div className="asstes-container">Asset name</div>
<div className="assets-date">Uploaded on-12 Jan 23</div>
<div className="assets-name">{assetName}</div>
<div className="assets-date">{uploadedOn}</div>
</div>
<div className="details">
<div className="content">
<EyeIconBig />
1.5k
{views}
</div>
<div className="content">
<CommentsIcon />
@@ -39,17 +66,17 @@ const Card: React.FC = () => {
</div>
<div className="stars-container">
<div className="stars-wrapper">
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
{[...Array(5)].map((_, index) => (
<StarsIconSmall key={index} />
))}
</div>
<div className="units">
36,500/<span>unit</span>
{price}/<span>unit</span>
</div>
</div>
<div className="buy-now-button">Buy now</div>
<div className="buy-now-button" onClick={handleCardSelect}>
Buy now
</div>
</div>
);
};

View File

@@ -1,15 +1,149 @@
import React from "react";
import React, { useState } from "react";
import Card from "./Card";
import AssetPreview from "./AssetPreview";
import RenderOverlay from "../../components/templates/Overlay";
const CardsContainer: React.FC = () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const array = [
{
id: 1,
name: "Asset 1",
uploadedOn: "12 Jan 23",
price: 36500,
rating: 4.5,
views: 500,
},
{
id: 2,
name: "Asset 2",
uploadedOn: "14 Jan 23",
price: 45000,
rating: 4.0,
views: 500,
},
{
id: 3,
name: "Asset 3",
uploadedOn: "15 Jan 23",
price: 52000,
rating: 4.8,
views: 500,
},
{
id: 4,
name: "Asset 4",
uploadedOn: "18 Jan 23",
price: 37000,
rating: 3.9,
views: 500,
},
{
id: 5,
name: "Asset 5",
uploadedOn: "20 Jan 23",
price: 60000,
rating: 5.0,
views: 500,
},
{
id: 6,
name: "Asset 6",
uploadedOn: "22 Jan 23",
price: 46000,
rating: 4.2,
views: 500,
},
{
id: 7,
name: "Asset 7",
uploadedOn: "25 Jan 23",
price: 38000,
rating: 4.3,
views: 500,
},
{
id: 8,
name: "Asset 8",
uploadedOn: "27 Jan 23",
price: 41000,
rating: 4.1,
views: 500,
},
{
id: 9,
name: "Asset 9",
uploadedOn: "30 Jan 23",
price: 55000,
rating: 4.6,
views: 500,
},
{
id: 10,
name: "Asset 10",
uploadedOn: "2 Feb 23",
price: 49000,
rating: 4.4,
views: 500,
},
{
id: 11,
name: "Asset 11",
uploadedOn: "5 Feb 23",
price: 62000,
rating: 5.0,
views: 500,
},
{
id: 12,
name: "Asset 12",
uploadedOn: "7 Feb 23",
price: 53000,
rating: 4.7,
views: 500,
},
];
const [selectedCard, setSelectedCard] = useState<{
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
} | null>(null);
const handleCardSelect = (cardData: {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
}) => {
setSelectedCard(cardData);
};
return (
<div className="cards-container-container">
<div className="header">Products You May Like</div>
<div className="cards-wrapper-container">
{array.map((index) => (
<Card key={index} />
{array.map((asset) => (
<Card
key={asset.id}
assetName={asset.name}
uploadedOn={asset.uploadedOn}
price={asset.price}
rating={asset.rating}
views={asset.views}
onSelectCard={handleCardSelect}
/>
))}
{/* <RenderOverlay> */}
{selectedCard && (
<AssetPreview
selectedCard={selectedCard}
setSelectedCard={setSelectedCard}
/>
)}
{/* </RenderOverlay> */}
</div>
</div>
);

View File

@@ -1,18 +1,18 @@
import React from "react";
import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer";
const MarketPlace = () => {
return (
<div className="marketplace-wrapper">
<div className="marketplace-container">
<div className="marketPlace">
<FilterSearch />
<CardsContainer />
</div>
</div>
</div>
);
};
export default MarketPlace;
import React from "react";
import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer";
const MarketPlace = () => {
return (
<div className="marketplace-wrapper">
<div className="marketplace-container">
<div className="marketPlace">
<FilterSearch />
<CardsContainer />
</div>
</div>
</div>
);
};
export default MarketPlace;

View File

@@ -13,6 +13,7 @@ import DuplicationControls from "./duplicationControls";
import CopyPasteControls from "./copyPasteControls";
import MoveControls from "./moveControls";
import RotateControls from "./rotateControls";
import useModuleStore from "../../../../store/useModuleStore";
const SelectionControls: React.FC = () => {
const { camera, controls, gl, scene, pointer } = useThree();
@@ -27,6 +28,7 @@ const SelectionControls: React.FC = () => {
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
const boundingBoxRef = useRef<THREE.Mesh>();
const { floorItems, setFloorItems } = useFloorItems();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
@@ -97,13 +99,16 @@ const SelectionControls: React.FC = () => {
clearSelection();
}
if (!toggleView) {
if (!toggleView && activeModule === "builder") {
helper.enabled = true;
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("contextmenu", onContextMenu);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
} else {
helper.enabled = false;
helper.dispose();
}
return () => {
@@ -115,7 +120,13 @@ const SelectionControls: React.FC = () => {
helper.enabled = false;
helper.dispose();
};
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]);
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule]);
useEffect(() => {
if (activeModule !== "builder") {
clearSelection();
}
}, [activeModule]);
useFrame(() => {
if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {

View File

@@ -15,13 +15,10 @@ import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr";
import SelectionControls from "./controls/selection/selectionControls";
import MeasurementTool from "./tools/measurementTool";
import Simulation from "../simulation/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import Throughput from "../../components/layout/3D-cards/cards/Throughput";
import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets";
// import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
export default function Scene() {
@@ -30,7 +27,6 @@ export default function Scene() {
{ name: "backward", keys: ["ArrowDown", "s", "S"] },
{ name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "right", keys: ["ArrowRight", "d", "D"] },
// { name: "jump", keys: ["Space"] },
], [])

View File

@@ -3,7 +3,6 @@ import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { useEffect } from 'react';
interface Path {
modeluuid: string;
modelName: string;
@@ -11,8 +10,9 @@ interface Path {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; type: string; isUsed: boolean }[] | [];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
@@ -32,8 +32,8 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
const point2Position = new THREE.Vector3(0, 1.25, -3.3);
const point1UUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID();
const middlePointUUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID();
const newPath: Path = {
modeluuid: item.modeluuid,
@@ -43,22 +43,25 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
uuid: point1UUID,
position: [point1Position.x, point1Position.y, point1Position.z],
rotation: [0, 0, 0],
actions: [{ uuid: THREE.MathUtils.generateUUID(), type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] },
},
{
uuid: middlePointUUID,
position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z],
rotation: [0, 0, 0],
actions: [{ uuid: THREE.MathUtils.generateUUID(), type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] },
},
{
uuid: point2UUID,
position: [point2Position.x, point2Position.y, point2Position.z],
rotation: [0, 0, 0],
actions: [{ uuid: THREE.MathUtils.generateUUID(), type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }],
triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] },
},
],
pathPosition: [...item.position],
@@ -76,4 +79,4 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
return null;
}
export default Behaviour;
export default Behaviour;

View File

@@ -2,21 +2,100 @@ import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import * as THREE from 'three';
import { QuadraticBezierLine } from '@react-three/drei';
import { useConnections, useIsConnecting, useSimulationPaths } from '../../../store/store';
import { useIsConnecting, useSimulationPaths } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
const { activeModule } = useModuleStore();
const { activeModule } = useModuleStore();
const { gl, raycaster, scene, pointer, camera } = useThree();
const { connections, setConnections, addConnection } = useConnections();
const { isConnecting, setIsConnecting } = useIsConnecting();
const { setIsConnecting } = useIsConnecting();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const [firstSelected, setFirstSelected] = useState<{ pathUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null);
const [firstSelected, setFirstSelected] = useState<{
pathUUID: string;
sphereUUID: string;
position: THREE.Vector3;
isCorner: boolean;
} | null>(null);
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null);
const [hoveredSphere, setHoveredSphere] = useState<{ sphereUUID: string, position: THREE.Vector3 } | null>(null);
const [helperlineColor, setHelperLineColor] = useState<string>('red');
const updatePathConnections = (
fromPathUUID: string,
fromPointUUID: string,
toPathUUID: string,
toPointUUID: string
) => {
const updatedPaths = simulationPaths.map(path => {
if (path.modeluuid === fromPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === fromPointUUID) {
const newTarget = {
pathUUID: toPathUUID,
pointUUID: toPointUUID
};
const existingTargets = point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === newTarget.pathUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, newTarget]
}
};
}
}
return point;
})
};
}
else if (path.modeluuid === toPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === toPointUUID) {
const reverseTarget = {
pathUUID: fromPathUUID,
pointUUID: fromPointUUID
};
const existingTargets = point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === reverseTarget.pathUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, reverseTarget]
}
};
}
}
return point;
})
};
}
return path;
});
setSimulationPaths(updatedPaths);
};
const handleAddConnection = (fromPathUUID: string, fromUUID: string, toPathUUID: string, toUUID: string) => {
updatePathConnections(fromPathUUID, fromUUID, toPathUUID, toUUID);
setFirstSelected(null);
setCurrentLine(null);
setIsConnecting(false);
};
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
@@ -47,7 +126,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
if (intersects.length > 0) {
const intersected = intersects[0].object;
if (intersected.name.includes("event-sphere")) {
if (intersected.name.includes("action-sphere")) {
const pathUUID = intersected.userData.path.modeluuid;
const sphereUUID = intersected.uuid;
const worldPosition = new THREE.Vector3();
@@ -59,9 +138,12 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
);
if (pathUUID) {
const isAlreadyConnected = connections.some((connection) =>
connection.fromUUID === sphereUUID ||
connection.toConnections.some(conn => conn.toUUID === sphereUUID)
// Check if sphere is already connected
const isAlreadyConnected = simulationPaths.some(path =>
path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
);
if (isAlreadyConnected) {
@@ -89,16 +171,12 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return;
}
addConnection({
fromPathUUID: firstSelected.pathUUID,
fromUUID: firstSelected.sphereUUID,
toConnections: [{ toPathUUID: pathUUID, toUUID: sphereUUID }]
});
setFirstSelected(null);
setCurrentLine(null);
setIsConnecting(false);
setHoveredSphere(null);
handleAddConnection(
firstSelected.pathUUID,
firstSelected.sphereUUID,
pathUUID,
sphereUUID
);
}
}
}
@@ -106,7 +184,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
setFirstSelected(null);
setCurrentLine(null);
setIsConnecting(false);
setHoveredSphere(null);
}
};
@@ -119,7 +196,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
setFirstSelected(null);
setCurrentLine(null);
setIsConnecting(false);
setHoveredSphere(null);
}
return () => {
@@ -128,7 +204,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [camera, scene, raycaster, firstSelected, connections]);
}, [camera, scene, raycaster, firstSelected, simulationPaths]);
useFrame(() => {
if (firstSelected) {
@@ -153,7 +229,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
obj.object.name.includes("event-sphere")
obj.object.name.includes("action-sphere")
);
if (sphereIntersects.length > 0) {
@@ -168,9 +244,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
sphereUUID === sphere.userData.path.points[sphere.userData.path.points.length - 1].uuid
);
const isAlreadyConnected = connections.some((connection) =>
connection.fromUUID === sphereUUID ||
connection.toConnections.some(conn => conn.toUUID === sphereUUID)
const isAlreadyConnected = simulationPaths.some(path =>
path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
);
if (
@@ -186,10 +264,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
if (snappedSphere) {
setHoveredSphere(snappedSphere);
point = snappedSphere.position;
} else {
setHoveredSphere(null);
}
if (point) {
@@ -224,49 +299,48 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
});
useEffect(() => {
console.log('connections: ', connections);
}, [connections]);
// Render connections from simulationPaths
return (
<>
{connections.map((connection, index) => {
const fromSphere = scene.getObjectByProperty('uuid', connection.fromUUID);
const toSphere = scene.getObjectByProperty('uuid', connection.toConnections[0].toUUID);
{simulationPaths.flatMap(path =>
path.points.flatMap(point =>
point.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
return (
<QuadraticBezierLine
key={index}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
lineWidth={4}
dashed
dashSize={1}
dashScale={20}
userData={connection}
/>
);
}
return null;
})}
return (
<QuadraticBezierLine
key={`${point.uuid}-${target.pointUUID}-${index}`}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
lineWidth={4}
dashed
dashSize={1}
dashScale={20}
/>
);
}
return null;
})
)
)}
{currentLine && (
<QuadraticBezierLine

View File

@@ -12,8 +12,9 @@ interface Path {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; type: string; isUsed: boolean }[] | [];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
@@ -26,7 +27,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere();
const { setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { isConnecting, setIsConnecting } = useIsConnecting();
const { isConnecting } = useIsConnecting();
const { camera } = useThree();
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
@@ -105,6 +106,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
setSelectedPath(null);
@@ -117,7 +119,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
uuid={point.uuid}
position={point.position}
args={[0.15, 32, 32]}
name='event-sphere'
name='action-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting) return;

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from 'react';
import { useConnections, useFloorItems, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store';
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import Behaviour from './behaviour/behaviour';
@@ -11,7 +11,6 @@ function Simulation() {
const { activeModule } = useModuleStore();
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>;
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { connections, setConnections, addConnection, removeConnection } = useConnections();
const [processes, setProcesses] = useState([]);
useEffect(() => {
@@ -32,9 +31,9 @@ function Simulation() {
return (
<>
<Behaviour setSimulationPaths={setSimulationPaths} />
{activeModule === 'simulation' && (
<>
<Behaviour setSimulationPaths={setSimulationPaths} />
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
</>

View File

@@ -1,5 +1,5 @@
import { useMemo, useState } from 'react';
import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation } from '../../store/store';
import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store';
import * as THREE from 'three';
import useModuleStore from '../../store/useModuleStore';
@@ -10,19 +10,31 @@ function SimulationUI() {
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath();
const [activeButton, setActiveButton] = useState<string | null>(null);
const handleAddAction = () => {
if (!selectedActionSphere) return;
const newAction = { uuid: THREE.MathUtils.generateUUID(), type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false };
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: [...point.actions, newAction] }
: point
),
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`, // Assign action name based on index
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
@@ -137,15 +149,22 @@ function SimulationUI() {
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
const newTrigger = { uuid: THREE.MathUtils.generateUUID(), type: '', isUsed: false };
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: [...point.triggers, newTrigger] }
: point
),
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
type: '',
isUsed: false
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
@@ -239,10 +258,15 @@ function SimulationUI() {
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
const createPath = () => {
setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null);
setDrawMaterialPath(!drawMaterialPath);
}
return (
<>
{activeModule === "simulation" && (
<div style={{ zIndex: 10, position: "relative", width: '260px' }}>
<div style={{ zIndex: 10, position: "fixed", width: '260px' }}>
{!ToggleView && (
<>
<button
@@ -257,6 +281,10 @@ function SimulationUI() {
{startSimulation ? 'Stop Simulation' : 'Start Simulation'}
</button>
<div style={{ zIndex: "10", position: "relative" }}>
{!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>}
</div>
{selectedPath && (
<div style={{ marginTop: "10px" }}>
<label>Path Speed:</label>

View File

@@ -2,7 +2,7 @@ import * as THREE from 'three';
import { useState, useEffect, useRef, useMemo } from "react";
import { useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three-stdlib";
import crate from "../../../../assets/models/gltf-glb/crate_box.glb";
import crate from "../../../../assets/gltf-glb/crate_box.glb";
import { useOrganization } from '../../../../store/store';
import { useControls } from 'leva';
@@ -47,7 +47,7 @@ export default function PathFlow({ path, connections }: PathFlowProps) {
};
useFrame(() => {
if (organization !== 'hexrfactory' || isStopped || !path) return;
if (isStopped || !path) return;
const now = performance.now();