Merge remote-tracking branch 'origin/main' into ui

This commit is contained in:
2025-03-28 14:19:43 +05:30
49 changed files with 3088 additions and 1862 deletions

2033
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,12 @@
"@react-three/drei": "^9.113.0", "@react-three/drei": "^9.113.0",
"@react-three/fiber": "^8.17.7", "@react-three/fiber": "^8.17.7",
"@react-three/postprocessing": "^2.16.3", "@react-three/postprocessing": "^2.16.3",
"@recast-navigation/three": "^0.39.0",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"@turf/turf": "^7.1.0", "@turf/helpers": "^7.2.0",
"@turf/turf": "^7.2.0",
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
"@types/react": "^18.3.5", "@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",

View File

@@ -75,7 +75,7 @@ export function CommentsIcon() {
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<g clip-path="url(#clip0_2926_17620)"> <g clipPath="url(#clip0_2926_17620)">
<path <path
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"

View File

@@ -11,7 +11,7 @@ const CardsScene = () => {
<Canvas> <Canvas>
{/* 3d-cards */} {/* 3d-cards */}
{/* <Throughput /> */} <Throughput />
{/* <ReturnOfInvestment /> */} {/* <ReturnOfInvestment /> */}
{/* <ProductionCapacity /> */} {/* <ProductionCapacity /> */}
{/* <StateWorking /> */} {/* <StateWorking /> */}

View File

@@ -108,7 +108,7 @@ const SideBarRight: React.FC = () => {
{subModule === "mechanics" && !selectedActionSphere && ( {subModule === "mechanics" && !selectedActionSphere && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<MachineMechanics /> {/* <MachineMechanics /> */}
</div> </div>
</div> </div>
)} )}

View File

@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react"; import React, { useRef, useState, useMemo, useEffect } from "react";
import { import {
AddIcon, AddIcon,
InfoIcon, InfoIcon,
@@ -11,80 +11,332 @@ import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { handleResize } from "../../../../functions/handleResizePannel"; import { handleResize } from "../../../../functions/handleResizePannel";
import EyeDropInput from "../../../ui/inputs/EyeDropInput"; import EyeDropInput from "../../../ui/inputs/EyeDropInput";
import { useSelectedActionSphere } from "../../../../store/store"; import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
import * as THREE from 'three';
import InputToggle from "../../../ui/inputs/InputToggle";
const MachineMechanics: React.FC = () => { const MachineMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere(); const { selectedActionSphere } = useSelectedActionSphere();
console.log("selectedActionSphere: ", selectedActionSphere); const { selectedPath, setSelectedPath } = useSelectedPath();
const [actionList, setActionList] = useState<string[]>([]); const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const [triggerList, setTriggerList] = useState<string[]>([]);
const [selectedItem, setSelectedItem] = useState<{
type: "action" | "trigger";
name: string;
} | null>(null);
const actionsContainerRef = useRef<HTMLDivElement>(null); const actionsContainerRef = useRef<HTMLDivElement>(null);
const triggersContainerRef = useRef<HTMLDivElement>(null); const triggersContainerRef = useRef<HTMLDivElement>(null);
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
const handleAddAction = () => { const handleAddAction = () => {
setActionList([...actionList, `Action ${actionList.length + 1}`]); if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
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}`,
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
};
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? {
...action,
type: actionType,
// Reset dependent fields when type changes
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
}
: action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Update the selected item to reflect changes
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
const updatedAction = updatedPaths
.flatMap(path => path.points)
.find(p => p.uuid === selectedActionSphere.point.uuid)
?.actions.find(a => a.uuid === uuid);
if (updatedAction) {
setSelectedItem({
type: "action",
item: updatedAction
});
}
}
};
// Modified handleMaterialSelect to ensure it only applies to relevant action types
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid &&
(action.type === 'Spawn' || action.type === 'Swap')
? { ...action, material }
: action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Update selected item if it's the current action
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
material
}
});
}
};
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleSpeedChange = (speed: number) => {
if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
}; };
const handleAddTrigger = () => { const handleAddTrigger = () => {
setTriggerList([...triggerList, `Trigger ${triggerList.length + 1}`]); if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
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}`,
type: '',
isUsed: false
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
}; };
const handleRemoveAction = (index: number) => { const handleDeleteTrigger = (uuid: string) => {
setActionList(actionList.filter((_, i) => i !== index)); if (!selectedActionSphere) return;
if (
selectedItem?.type === "action" && const updatedPaths = simulationPaths.map((path) => ({
selectedItem.name === actionList[index] ...path,
) { points: path.points.map((point) =>
setSelectedItem(null); point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
// Update the toggle handlers to immediately update the selected item
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
} }
}; };
const handleRemoveTrigger = (index: number) => { // Do the same for trigger toggle
setTriggerList(triggerList.filter((_, i) => i !== index)); const handleTriggerToggle = (uuid: string) => {
if ( if (!selectedActionSphere) return;
selectedItem?.type === "trigger" &&
selectedItem.name === triggerList[index] const updatedPaths = simulationPaths.map((path) => ({
) { ...path,
setSelectedItem(null); points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
} }
}; };
const handleSelectItem = (type: "action" | "trigger", name: string) => { const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
setSelectedItem({ type, name });
};
const [processes, setProcesses] = useState<string[]>([]); useEffect(() => {
const [activeProcess, setActiveProcesses] = useState<string>(); setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
}, [selectedActionSphere]);
const handleSelect = (option: string) => {
setActiveProcesses(option); // Update the active option state
};
const handleAddProcess = () => {
const newProcess = `Process ${processes.length + 1}`; // Generate new process name dynamically
setProcesses((prevProcesses) => [...prevProcesses, newProcess]); // Update the state with the new process
};
return ( return (
<div className="machine-mechanics-container"> <div className="machine-mechanics-container">
<div className="machine-mechanics-header"> <div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "path name not found"} {selectedActionSphere?.path?.modelName || "point name not found"}
</div> </div>
{/* <div className="process-list-container">
<div className="label">Process:</div>
<RegularDropDown
header={activeProcess || "add process ->"}
options={processes}
onSelect={handleSelect}
/>
<div className="add-new-process" onClick={handleAddProcess}>
<AddIcon />
</div>
</div> */}
<div className="machine-mechanics-content-container"> <div className="machine-mechanics-content-container">
<div className="actions"> <div className="actions">
<div className="header"> <div className="header">
@@ -99,25 +351,24 @@ const MachineMechanics: React.FC = () => {
style={{ height: "120px" }} style={{ height: "120px" }}
> >
<div className="list-container"> <div className="list-container">
{actionList.map((action, index) => ( {selectedPoint?.actions.map((action) => (
<div <div
key={index} key={action.uuid}
className={`list-item ${ className={`list-item ${selectedItem?.type === "action" &&
selectedItem?.type === "action" && selectedItem.item?.uuid === action.uuid
selectedItem.name === action ? "active"
? "active" : ""
: "" }`}
}`}
> >
<div <div
className="value" className="value"
onClick={() => handleSelectItem("action", action)} onClick={() => setSelectedItem({ type: "action", item: action })}
> >
<RenameInput value={action} /> <RenameInput value={action.name} />
</div> </div>
<div <div
className="remove-button" className="remove-button"
onClick={() => handleRemoveAction(index)} onClick={() => handleDeleteAction(action.uuid)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </div>
@@ -146,25 +397,24 @@ const MachineMechanics: React.FC = () => {
style={{ height: "120px" }} style={{ height: "120px" }}
> >
<div className="list-container"> <div className="list-container">
{triggerList.map((trigger, index) => ( {selectedPoint?.triggers.map((trigger) => (
<div <div
key={index} key={trigger.uuid}
className={`list-item ${ className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem?.type === "trigger" && selectedItem.item?.uuid === trigger.uuid
selectedItem.name === trigger ? "active"
? "active" : ""
: "" }`}
}`}
> >
<div <div
className="value" className="value"
onClick={() => handleSelectItem("trigger", trigger)} onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
> >
<RenameInput value={trigger} /> <RenameInput value={trigger.name} />
</div> </div>
<div <div
className="remove-button" className="remove-button"
onClick={() => handleRemoveTrigger(index)} onClick={() => handleDeleteTrigger(trigger.uuid)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </div>
@@ -183,28 +433,101 @@ const MachineMechanics: React.FC = () => {
<div className="selected-properties-container"> <div className="selected-properties-container">
{selectedItem && ( {selectedItem && (
<> <>
<div className="properties-header">{selectedItem.name}</div> <div className="properties-header">{selectedItem.item.name}</div>
<LabledDropdown
defaultOption="On-hit" {selectedItem.type === "action" && (
options={["On-hit", "Buffer"]} <>
/> <InputToggle
<InputWithDropDown inputKey="enableTrigger"
label="Speed" label="Enable Trigger"
value="" value={selectedItem.item.isUsed}
activeOption=".mm" onClick={() => handleActionToggle(selectedItem.item.uuid)}
onChange={() => {}} />
/> <LabledDropdown
<EyeDropInput /> defaultOption={selectedItem.item.type}
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
/>
{/* Only show material dropdown for Spawn/Swap actions */}
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
<LabledDropdown
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
defaultOption={selectedItem.item.material}
options={["Inherit", "Crate", "Box"]}
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
/>
)}
{/* Only show delay input for Delay actions */}
{selectedItem.item.type === 'Delay' && (
<InputWithDropDown
label="Delay Time"
value={selectedItem.item.delay === 'Inherit'
? undefined
: selectedItem.item.delay}
onChange={(value) => {
const numValue = parseInt(value);
handleDelayChange(
selectedItem.item.uuid,
!value ? 'Inherit' : numValue
);
}}
/>
)}
{/* Only show spawn interval for Spawn actions */}
{selectedItem.item.type === 'Spawn' && (
<InputWithDropDown
label="Spawn Interval"
min={0}
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
onChange={(value) => {
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
}}
/>
)}
</>
)}
{selectedItem.type === "trigger" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type || "Select Trigger Type"}
options={["On-Hit", "Buffer"]}
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
/>
</>
)}
</> </>
)} )}
{selectedPath && !selectedItem && (
<div className="speed-control">
<InputWithDropDown
label="Path Speed"
value={selectedPath.path.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
</div>
)}
</div> </div>
<div className="footer"> <div className="footer">
<InfoIcon /> <InfoIcon />
By Selecting Path, you can create Object Triggers. By selecting points, you can create events and triggers.
</div> </div>
</div> </div>
</div> </div>
); );
}; };
export default MachineMechanics; export default MachineMechanics;

View File

@@ -1,115 +1,115 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
import { AddIcon } from '../../../../icons/ExportCommonIcons' import { AddIcon } from '../../../../icons/ExportCommonIcons'
import RegularDropDown from '../../../../ui/inputs/RegularDropDown' import RegularDropDown from '../../../../ui/inputs/RegularDropDown'
import useChartStore from '../../../../../store/useChartStore' import useChartStore from '../../../../../store/useChartStore'
import axios from 'axios' import axios from 'axios'
type Props = {} type Props = {}
const LineGrapInput = (props: Props) => { const LineGrapInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({}) const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({}) const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
const [selectedOption, setSelectedOption] = useState('1h') const [selectedOption, setSelectedOption] = useState('1h')
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
const handleSelectDuration = (option: string) => { const handleSelectDuration = (option: string) => {
updateDuration(option); // Normalize for key matching updateDuration(option); // Normalize for key matching
}; };
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get('http://192.168.0.192:5010/getinput');
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log('dropdown data:', response.data);
setDropDownData(response.data) setDropDownData(response.data)
} else { } else {
console.log('Unexpected response:', response); console.log('Unexpected response:', response);
} }
} catch (error) { } catch (error) {
console.error('There was an error!', error); console.error('There was an error!', error);
} }
}; };
fetchZoneData(); fetchZoneData();
}, []); }, []);
useEffect(() => { useEffect(() => {
console.log(selections); console.log(selections);
}, [selections]) }, [selections])
const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => { const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => {
setSelections(prev => { setSelections(prev => {
if (selectedData === null) { if (selectedData === null) {
const newSelections = { ...prev }; const newSelections = { ...prev };
delete newSelections[inputKey]; delete newSelections[inputKey];
return newSelections; return newSelections;
} else { } else {
return { return {
...prev, ...prev,
[inputKey]: selectedData [inputKey]: selectedData
}; };
} }
}); });
}; };
interface Measurement { interface Measurement {
name: string; name: string;
fields: string; fields: string;
} }
interface InputData { interface InputData {
[key: string]: Measurement; [key: string]: Measurement;
} }
const extractMeasurements = (input: InputData): Measurement[] => { const extractMeasurements = (input: InputData): Measurement[] => {
return Object.values(input); return Object.values(input);
}; };
useEffect(() => { useEffect(() => {
const measurementsData = extractMeasurements(selections); const measurementsData = extractMeasurements(selections);
setMeasurements(measurementsData); setMeasurements(measurementsData);
}, [selections]); }, [selections]);
return ( return (
<> <>
<div className="inputs-wrapper"> <div className="inputs-wrapper">
{[...Array(6)].map((_, index) => { {[...Array(6)].map((_, index) => {
const inputKey = `input${index + 1}`; const inputKey = `input${index + 1}`;
return ( return (
<div key={index} className="datas"> <div key={index} className="datas">
<div className="datas__label">Input {index + 1}</div> <div className="datas__label">Input {index + 1}</div>
<div className="datas__class"> <div className="datas__class">
<MultiLevelDropdown <MultiLevelDropdown
data={dropDowndata} data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} selectedValue={selections[inputKey]}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />
</div> </div>
</div> </div>
</div> </div>
); );
})} })}
</div> </div>
<div> <div>
<div className="datas"> <div className="datas">
<div className="datas__label">duration</div> <div className="datas__label">duration</div>
<div className="datas__class"> <div className="datas__class">
<RegularDropDown <RegularDropDown
header={duration} header={duration}
options={["1h", "2h", "12h"]} options={["1h", "2h", "12h"]}
onSelect={handleSelectDuration} onSelect={handleSelectDuration}
search={false} search={false}
/> />
</div> </div>
</div> </div>
</div> </div>
</> </>
) )
} }
export default LineGrapInput export default LineGrapInput

View File

@@ -1,77 +1,77 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
import { AddIcon } from '../../../../icons/ExportCommonIcons' import { AddIcon } from '../../../../icons/ExportCommonIcons'
import axios from 'axios' import axios from 'axios'
type Props = {} type Props = {}
const PieChartInput = (props: Props) => { const PieChartInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({}) const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({}) const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get('http://192.168.0.192:5010/getinput');
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log('dropdown data:', response.data);
setDropDownData(response.data) setDropDownData(response.data)
} else { } else {
console.log('Unexpected response:', response); console.log('Unexpected response:', response);
} }
} catch (error) { } catch (error) {
console.error('There was an error!', error); console.error('There was an error!', error);
} }
}; };
fetchZoneData(); fetchZoneData();
}, []); }, []);
useEffect(() => {console.log(selections); useEffect(() => {console.log(selections);
},[selections]) },[selections])
const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => { const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => {
setSelections(prev => { setSelections(prev => {
if (selectedData === null) { if (selectedData === null) {
const newSelections = {...prev}; const newSelections = {...prev};
delete newSelections[inputKey]; delete newSelections[inputKey];
return newSelections; return newSelections;
} else { } else {
return { return {
...prev, ...prev,
[inputKey]: selectedData [inputKey]: selectedData
}; };
} }
}); });
}; };
return ( return (
<> <>
<div className="inputs-wrapper"> <div className="inputs-wrapper">
{[...Array(3)].map((_, index) => { {[...Array(3)].map((_, index) => {
const inputKey = `input${index+1}`; const inputKey = `input${index+1}`;
return ( return (
<div key={index} className="datas"> <div key={index} className="datas">
<div className="datas__label">Input {index+1}</div> <div className="datas__label">Input {index+1}</div>
<div className="datas__class"> <div className="datas__class">
<MultiLevelDropdown <MultiLevelDropdown
data={dropDowndata} data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} selectedValue={selections[inputKey]}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />
</div> </div>
</div> </div>
</div> </div>
); );
})} })}
</div> </div>
<div> <div>
</div> </div>
</> </>
) )
} }
export default PieChartInput export default PieChartInput

View File

@@ -7,7 +7,6 @@ interface LoadingPageProps {
} }
const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => { const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
// Ensure progress stays within 0-100 range
const validatedProgress = Math.min(100, Math.max(0, progress)); const validatedProgress = Math.min(100, Math.max(0, progress));
return ( return (

View File

@@ -1,6 +1,8 @@
import React, { useEffect, useRef, useState, useCallback } from "react"; import React, { useEffect, useRef, useState, useCallback } from "react";
import { Widget } from "../../../store/useWidgetStore"; import { Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@@ -154,33 +156,40 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
)} )}
{/* Zones Wrapper */} {/* Zones Wrapper */}
<div ref={containerRef} className="zones-wrapper"> {Object.keys(zonesData).length !== 0 ? (
{Object.keys(zonesData).map((zoneName, index) => ( <div ref={containerRef} className="zones-wrapper">
<div {Object.keys(zonesData).map((zoneName, index) => (
key={index} <div
className={`zone ${ key={index}
selectedZone.zoneName === zoneName ? "active" : "" className={`zone ${
}`} selectedZone.zoneName === zoneName ? "active" : ""
onClick={() => { }`}
console.log("zoneName: ", zoneName); onClick={() => {
setSelectedZone({ useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
zoneName, setSelectedZone({
activeSides: zonesData[zoneName].activeSides || [], zoneName,
panelOrder: zonesData[zoneName].panelOrder || [], activeSides: zonesData[zoneName].activeSides || [],
lockedPanels: zonesData[zoneName].lockedPanels || [], panelOrder: zonesData[zoneName].panelOrder || [],
widgets: zonesData[zoneName].widgets || [], lockedPanels: zonesData[zoneName].lockedPanels || [],
zoneId: zonesData[zoneName]?.zoneId || "", widgets: zonesData[zoneName].widgets || [],
zoneViewPortTarget: zoneId: zonesData[zoneName]?.zoneId || "",
zonesData[zoneName].zoneViewPortTarget || [], zoneViewPortTarget:
zoneViewPortPosition: zonesData[zoneName].zoneViewPortTarget || [],
zonesData[zoneName].zoneViewPortPosition || [], zoneViewPortPosition:
}); zonesData[zoneName].zoneViewPortPosition || [],
}} });
> }}
{zoneName} >
</div> {zoneName}
))} </div>
</div> ))}
</div>
) : (
<div className="no-zone">
<InfoIcon />
No zones? Create one!
</div>
)}
{/* Right Arrow */} {/* Right Arrow */}
{showRightArrow && ( {showRightArrow && (

View File

@@ -1,89 +1,215 @@
// import { useState } from "react";
// import { useThree } from "@react-three/fiber";
// import * as THREE from "three";
// const DroppedObjects = () => { import { WalletIcon } from "../../icons/3dChartIcons";
// const { camera } = useThree(); // Now inside Canvas ✅ import { useEffect, useRef, useState } from "react";
// const [objects, setObjects] = useState<{ id: number; position: [number, number, number] }[]>([]); import { Line } from "react-chartjs-2";
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
// // Function to convert drop event into 3D position
// const handleDrop = (event: DragEvent) => {
// event.preventDefault();
// const data = event.dataTransfer?.getData("text/plain");
// if (!data) return;
// try {
// const cardData = JSON.parse(data);
// if (!cardData.className.includes("floating total-card")) {
// console.log("Drop rejected: Incorrect element.");
// return;
// }
// // Convert 2D drop position to 3D world coordinates
// const x = (event.clientX / window.innerWidth) * 2 - 1;
// const y = -(event.clientY / window.innerHeight) * 2 + 1;
// // Raycasting to determine the drop position in 3D
// const raycaster = new THREE.Raycaster();
// const mouseVector = new THREE.Vector2(x, y);
// raycaster.setFromCamera(mouseVector, camera);
// // Intersect with a ground plane (assume y = 0)
// const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// const intersection = new THREE.Vector3();
// raycaster.ray.intersectPlane(groundPlane, intersection);
// console.log("Spawn Object at:", intersection);
// // Add the dropped object to the scene state
// setObjects((prev) => [...prev, { id: Date.now(), position: [intersection.x, intersection.y, intersection.z] }]);
// } catch (error) {
// console.error("Invalid data:", error);
// }
// };
// return (
// <group>
// {/* Render dropped objects as green boxes */}
// {objects.map((obj) => (
// <mesh key={obj.id} position={obj.position}>
// <boxGeometry args={[1, 1, 1]} />
// <meshStandardMaterial color="green" />
// </mesh>
// ))}
// </group>
// );
// };
import { Html } from "@react-three/drei";
import { useDroppedObjectsStore } from "../../../store/store";
const DroppedObjects: React.FC = () => { const DroppedObjects: React.FC = () => {
const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store const zones = useDroppedObjectsStore((state) => state.zones);
console.log('objects: ', objects);
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
const [offset, setOffset] = useState<[number, number] | null>(null);
const positionRef = useRef<[number, number] | null>(null);
const animationRef = useRef<number | null>(null);
// useEffect(() => {
// const initialZones: Record<string, Zones> = {
// "Zone 1": {
// zoneName: "Zone 1",
// zoneId: "2e996073-546c-470c-8323-55bd3700c6aa",
// objects: [
// {
// header: "Todays Money",
// value: 53000, // ✅ Converted to number
// per: "+55%",
// className: "floating total-card",
// position: [146, 214], // ✅ No need for 'as' here
// },
// {
// header: "New Clients",
// value: 250, // ✅ Converted to number
// per: "+12%",
// className: "floating total-card",
// position: [344, 295],
// },
// ],
// },
// };
// useDroppedObjectsStore.setState({ zones: initialZones });
// }, []);
const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null; // No zone, nothing to render
const [zoneName, zone] = zoneEntries[0]; // Only render the first zone
function handlePointerDown(event: React.PointerEvent, index: number) {
const obj = zone.objects[index];
const offsetX = event.clientX - obj.position[1];
const offsetY = event.clientY - obj.position[0];
setDraggingIndex({ zone: zoneName, index });
setOffset([offsetY, offsetX]);
}
function handlePointerMove(event: React.PointerEvent) {
if (!draggingIndex || !offset) return;
const container = document.getElementById("real-time-vis-canvas");
if (!container) return;
const rect = container.getBoundingClientRect();
let newX = event.clientX - offset[1];
let newY = event.clientY - offset[0];
newX = Math.max(0, Math.min(rect.width - 50, newX));
newY = Math.max(0, Math.min(rect.height - 50, newY));
positionRef.current = [newY, newX];
if (!animationRef.current) {
animationRef.current = requestAnimationFrame(() => {
if (positionRef.current) {
updateObjectPosition(zoneName, draggingIndex.index, positionRef.current);
}
animationRef.current = null;
});
}
}
function handlePointerUp() {
setDraggingIndex(null);
setOffset(null);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
}
}
return ( return (
<> <div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
{objects.map((obj, index) => ( {zone.objects.map((obj, index) => (
<group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}> <div
<Html wrapperClass={obj.className}> key={`${zoneName}-${index}`}
<div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}> className={obj.className}
<div className="header">{obj.header}</div> style={{
<div className="data-values"> top: obj.position[0] + "px",
<div className="value">{obj.value}</div> left: obj.position[1] + "px",
<div className="per">{obj.per}</div> transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
}}
onPointerDown={(event) => handlePointerDown(event, index)}
>
{obj.className === "floating total-card" ? (
<div>
<div className="header-wrapper">
<div className="header">{obj.header}</div>
<div className="data-values">
<div className="value">{obj.value}</div>
<div className="per">{obj.per}</div>
</div>
</div>
<div className="icon">
<WalletIcon />
</div> </div>
</div> </div>
</Html> ) : obj.className === "warehouseThroughput floating" ? (
</group> <div>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<span>(+5) more</span> in 2025
</p>
</div>
<div className="lineGraph" style={{ height: "100%" }}>
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
</div>
</div>
) : obj.className === "fleetEfficiency floating" ? (
<div>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
<div
className="bar"
style={{ transform: `rotate(${obj.value}deg)` }}
></div>
</div>
</div>
</div>
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{obj.per}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>
</div>
</div>
) : null}
</div>
))} ))}
</> </div>
); );
}; };
export default DroppedObjects; export default DroppedObjects;
// import { Html } from "@react-three/drei";
// import { useDroppedObjectsStore } from "../../../store/store";
// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons";
// import SimpleCard from "../realTimeVis/floating/SimpleCard";
// const ICON_MAP: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
// WalletIcon,
// GlobeIcon,
// DocumentIcon,
// CartIcon
// };
// const DroppedObjects: React.FC = () => {
// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
//
// return (
// <>
// {objects.map((obj, index) => {
// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists
// return (
// <SimpleCard
// key={index}
// position={obj.position}
// header={obj.header}
// icon={IconComponent} // ✅ No need to look it up in ICON_MAP
// value={obj.value}
// per={obj.per}
// />
// );
// })}
// </>
// );
// };
// export default DroppedObjects;

View File

@@ -6,8 +6,10 @@ import { useSelectedZoneStore } from "../../../store/useZoneStore";
import DisplayZone from "./DisplayZone"; import DisplayZone from "./DisplayZone";
import Scene from "../../../modules/scene/scene"; import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import { useDroppedObjectsStore, useZones } from "../../../store/store";
import DroppedObjects from "./DroppedFloatingWidgets"; import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { useZones } from "../../../store/store";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@@ -80,36 +82,36 @@ const RealTimeVisulization: React.FC = () => {
}); });
}, [selectedZone]); }, [selectedZone]);
// const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
// console.log("Drop event fired! ✅");
// event.preventDefault();
// const data = event.dataTransfer.getData("text/plain");
// if (!data) {
// console.log("❌ No data received on drop!");
// return;
// }
// try {
// const droppedData = JSON.parse(data);
// console.log("✅ Dropped Data:", droppedData);
// console.log('droppedData: ', droppedData);
// setDroppedObjects((prev) => [...prev, droppedData]); // ✅ Add to state
// console.log(droppedObjects);
// } catch (error) {
// console.error("❌ Error parsing dropped data:", error);
// }
// };
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event const data = event.dataTransfer.getData("text/plain");
if (!data || !selectedZone.zoneName) return;
if (data) {
const droppedData = JSON.parse(data); const droppedData = JSON.parse(data);
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) return;
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top;
const newObject = {
...droppedData,
position: [relativeY, relativeX], // Y first because of top/left style
};
console.log("newObject: ", newObject);
// Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) {
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
} }
// Add the dropped object to the zone
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
}; };
return ( return (
<div <div
@@ -133,9 +135,10 @@ const RealTimeVisulization: React.FC = () => {
onDrop={(event) => handleDrop(event)} onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()} onDragOver={(event) => event.preventDefault()}
> >
<Scene /> <Scene />
</div> </div>
<DroppedObjects />
{activeModule === "visualization" && ( {activeModule === "visualization" && (
<> <>
<DisplayZone <DisplayZone

View File

@@ -47,12 +47,9 @@ export default function ZoneCentreTarget() {
const worldUp = new THREE.Vector3(0, 0, 1); const worldUp = new THREE.Vector3(0, 0, 1);
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize(); const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
const up = new THREE.Vector3().crossVectors(direction, right).normalize(); const up = new THREE.Vector3().crossVectors(direction, right).normalize();
const offsetPosition = up.clone().multiplyScalar(20); const offsetPosition = up.clone().multiplyScalar(20);
camPosition.add(offsetPosition); camPosition.add(offsetPosition);
const setCam = async () => { const setCam = async () => {
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true); controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
setTimeout(() => { setTimeout(() => {

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import React from "react";
interface InputToggleProps { interface InputToggleProps {
label: string; // Represents the toggle state (on/off) label: string; // Represents the toggle state (on/off)
@@ -7,23 +7,18 @@ interface InputToggleProps {
inputKey: string; inputKey: string;
} }
// Update InputToggle.tsx to be fully controlled
const InputToggle: React.FC<InputToggleProps> = ({ const InputToggle: React.FC<InputToggleProps> = ({
label, label,
onClick, onClick,
value = false, value = false,
inputKey, inputKey,
}) => { }) => {
const [activeValue, setActiveValue] = useState<boolean>(value); // Remove internal state and use the value prop directly
function handleOnClick() { function handleOnClick() {
setActiveValue(!activeValue);
if (onClick) onClick(); if (onClick) onClick();
} }
useEffect(() => {
setActiveValue(value);
}, [value]);
return ( return (
<div className="input-toggle-container"> <div className="input-toggle-container">
<label htmlFor={`toogle-input-${inputKey}`} className="label"> <label htmlFor={`toogle-input-${inputKey}`} className="label">
@@ -33,15 +28,16 @@ const InputToggle: React.FC<InputToggleProps> = ({
<div <div
className="check-box-style" className="check-box-style"
style={{ style={{
left: activeValue ? "50%" : "2px", left: value ? "50%" : "2px",
background: activeValue ? "" : "var(--text-disabled)", background: value ? "" : "var(--text-disabled)",
}} }}
></div> ></div>
<input <input
type="checkbox" type="checkbox"
name="" name=""
id={`toogle-input-${inputKey}`} id={`toogle-input-${inputKey}`}
defaultChecked={value} checked={value}
readOnly
/> />
</div> </div>
</div> </div>

View File

@@ -4,21 +4,27 @@ import RenameInput from "./RenameInput";
type InputWithDropDownProps = { type InputWithDropDownProps = {
label: string; label: string;
value: string; value: string;
min?: number
defaultValue?: string;
options?: string[]; // Array of dropdown options options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option activeOption?: string; // The currently active dropdown option
onClick?: () => void; onClick?: () => void;
onChange: (newValue: string) => void; onChange: (newValue: string) => void;
editableLabel?: boolean; editableLabel?: boolean;
placeholder?: string; // New placeholder prop
}; };
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label, label,
value, value,
min,
defaultValue,
options, options,
activeOption, activeOption,
onClick, onClick,
onChange, onChange,
editableLabel = false, editableLabel = false,
placeholder = "Inherit", // Default empty placeholder
}) => { }) => {
const separatedWords = label const separatedWords = label
.split(/(?=[A-Z])/) .split(/(?=[A-Z])/)
@@ -38,11 +44,13 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
)} )}
<div className="input default" id={separatedWords}> <div className="input default" id={separatedWords}>
<input <input
type="text" min={min}
type="number"
defaultValue={value} defaultValue={value}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}
placeholder={placeholder} // Added placeholder prop
/> />
{activeOption && ( {activeOption && (
@@ -73,4 +81,4 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
); );
}; };
export default InputWithDropDown; export default InputWithDropDown;

View File

@@ -1,29 +1,49 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import RegularDropDown from "./RegularDropDown"; import RegularDropDown from "./RegularDropDown";
type LabledDropdownProps = { type LabledDropdownProps = {
defaultOption: string; // Initial active option defaultOption: string; // Initial active option
options: string[]; // Array of dropdown options options: string[]; // Array of dropdown options
label?: string; // Customizable label text
onSelect?: (option: string) => void; // Callback when option is selected
className?: string; // Additional className for styling
disabled?: boolean; // Disable dropdown
search?: boolean; // Enable/disable search functionality
}; };
const LabledDropdown: React.FC<LabledDropdownProps> = ({ defaultOption, options }) => { const LabledDropdown: React.FC<LabledDropdownProps> = ({
const [activeOption, setActiveOption] = useState(defaultOption); // State for active option defaultOption,
options,
label = "Type",
onSelect,
className = "",
search = false
}) => {
const [activeOption, setActiveOption] = useState(defaultOption);
// Update active option if defaultOption changes
useEffect(() => {
setActiveOption(defaultOption);
}, [defaultOption]);
const handleSelect = (option: string) => { const handleSelect = (option: string) => {
setActiveOption(option); // Update the active option state setActiveOption(option);
if (onSelect) {
onSelect(option);
}
}; };
return ( return (
<div className="value-field-container"> <div className={`value-field-container ${className}`}>
<div className="label">Type</div> <div className="label">{label}</div>
<RegularDropDown <RegularDropDown
header={activeOption} // Display the current active option header={activeOption}
options={options} // Use the options from props options={options}
onSelect={handleSelect} // Handle option selection onSelect={handleSelect}
search = {false} search={search}
/> />
</div> </div>
); );
}; };
export default LabledDropdown; export default LabledDropdown;

View File

@@ -201,18 +201,16 @@ const LineGraphComponent = ({
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
useEffect(() => { useEffect(() => {
if ( measurements.length > 0 ) {
const socket = io("http://192.168.0.192:5010");
const socket = io("http://192.168.0.192:5010");
if ( measurements.length > 0 ) {
var inputes = { var inputes = {
measurements: measurements, measurements: measurements,
duration: duration, duration: duration,
interval: 1000, interval: 1000,
} }
console.log('graphHHHHHHHHHHHHHHHHHHHHHHHHHHHHH',inputes);
// Start stream // Start stream
const startStream = () => { const startStream = () => {
socket.emit("lineInput", inputes); socket.emit("lineInput", inputes);
@@ -226,23 +224,24 @@ const LineGraphComponent = ({
// Extract timestamps and values // Extract timestamps and values
const labels = responceData.time; const labels = responceData.time;
const datasets = data.measurements.map((measurement: any) => ({ const datasets = measurements.map((measurement: any) => {
label: `${measurement.name}.${measurement.fields}`, const key = `${measurement.name}.${measurement.fields}`;
data: responceData[`${measurement.name}.${measurement.fields}`]?.values || [], return {
backgroundColor: themeColor[0] || "#5c87df", label: key,
borderColor: themeColor[1] || "#ffffff", data: responceData[key]?.values ?? [], // Ensure it exists
borderWidth: 2, backgroundColor: themeColor[0] || "#5c87df",
// fill: false, borderColor: themeColor[1] || "#ffffff",
})); };
});
setChartData({ labels, datasets }); setChartData({ labels, datasets });
}); });
}
return () => { return () => {
socket.off("lineOutput"); socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts socket.emit("stop_stream"); // Stop streaming when component unmounts
}; };
}
}, [measurements, duration]); }, [measurements, duration]);
// useEffect(() => { // useEffect(() => {

View File

@@ -4,8 +4,24 @@ const FleetEfficiency = () => {
// Calculate the rotation angle for the progress bar // Calculate the rotation angle for the progress bar
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°) const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position
const cardData = JSON.stringify({
className: event.currentTarget.className,
position: [rect.top, rect.left], // Store position
value: rotationAngle, // Example value (you can change if dynamic)
per: progress,
});
console.log("Dragged Data:", cardData);
event.dataTransfer.setData("text/plain", cardData);
};
return ( return (
<div className="fleetEfficiency floating"> <div className="fleetEfficiency floating" draggable onDragStart={handleDragStart}>
<h2 className="header">Fleet Efficiency</h2> <h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer"> <div className="progressContainer">

View File

@@ -5,6 +5,7 @@ interface SimpleCardProps {
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
value: string; value: string;
per: string; // Percentage change per: string; // Percentage change
position?: [number, number]
} }
const SimpleCard: React.FC<SimpleCardProps> = ({ const SimpleCard: React.FC<SimpleCardProps> = ({
@@ -12,23 +13,30 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
icon: Icon, icon: Icon,
value, value,
per, per,
position = [0, 0],
}) => { }) => {
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => { const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position
const cardData = JSON.stringify({ const cardData = JSON.stringify({
header, header,
value, value,
per, per,
className: event.currentTarget.className, icon: Icon,
className: event.currentTarget.className,
position: [rect.top, rect.left], // ✅ Store position
}); });
console.log("Dragging Data:", cardData); // ✅ Debugging log
event.dataTransfer.setData("text/plain", cardData); event.dataTransfer.setData("text/plain", cardData);
}; };
return ( return (
<div className="floating total-card" draggable onDragStart={handleDragStart}> <div
className="floating total-card"
draggable
onDragStart={handleDragStart}
style={{ top: position[0], left: position[1] }} // No need for ?? 0 if position is guaranteed
>
<div className="header-wrapper"> <div className="header-wrapper">
<div className="header">{header}</div> <div className="header">{header}</div>
<div className="data-values"> <div className="data-values">

View File

@@ -111,8 +111,27 @@ const WarehouseThroughput = () => {
}, },
}; };
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get element position
const cardData = JSON.stringify({
header: "Warehouse Throughput", // Static header
value: "+5", // Example value (you can change if dynamic)
per: "2025", // Example percentage or date
icon: "📊", // Placeholder for an icon (if needed)
className: event.currentTarget.className,
position: [rect.top, rect.left], // ✅ Store initial position
lineGraphData, // ✅ Include chart data
lineGraphOptions, // ✅ Include chart options
});
event.dataTransfer.setData("text/plain", cardData);
// event.dataTransfer.effectAllowed = "move"; // Improve drag effect
};
return ( return (
<div className="warehouseThroughput floating" draggable> <div className="warehouseThroughput floating" draggable onDragStart={handleDragStart}>
<div className="header"> <div className="header">
<h2>Warehouse Throughput</h2> <h2>Warehouse Throughput</h2>
<p> <p>

View File

@@ -0,0 +1,51 @@
import PolygonGenerator from "./polygonGenerator";
import { useThree } from "@react-three/fiber";
import { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import PathNavigator from "./pathNavigator";
import NavMeshDetails from "./navMeshDetails";
const Agv = ({
lines,
plane,
}: {
lines: Types.RefLines;
plane: Types.RefMesh;
}) => {
let pathPoints = [
[
{ x: 8.477161935339709, y: 0, z: 17.41343083550102 },
{ x: 9.175416491482693, y: 0, z: -12.361001232663693 },
],
,
// [
// { x: 13.508213355232144, y: 0, z: -15.456970649652018 },
// { x: -30.464866520869617, y: 0, z: 9.779806557688929 },
// ],
[
{ x: 16.792040856420844, y: 0, z: 15.86281907549489 },
{ x: -42.77173264503395, y: 0, z: -15.821322764400804 },
],
];
let groupRef = useRef() as Types.RefGroup;
const [navMesh, setNavMesh] = useState();
return (
<>
<PolygonGenerator groupRef={groupRef} lines={lines} plane={plane} />
<NavMeshDetails
lines={lines}
setNavMesh={setNavMesh}
groupRef={groupRef}
plane={plane}
/>
{pathPoints.map((pair, i) => (
<PathNavigator navMesh={navMesh} selectedPoints={pair} key={i} />
))}
<group ref={groupRef} visible={false} name="Meshes"></group>
</>
);
};
export default Agv;

View File

@@ -0,0 +1,63 @@
import React, { useEffect, useState } from "react";
import { init as initRecastNavigation } from "@recast-navigation/core";
import { generateSoloNavMesh } from "@recast-navigation/generators";
import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
interface NavMeshDetailsProps {
setNavMesh: (navMesh: any) => void;
groupRef: React.MutableRefObject<THREE.Group | null>;
lines: Types.RefLines;
plane: Types.RefMesh;
}
export default function NavMeshDetails({
lines,
setNavMesh,
groupRef,
plane,
}: NavMeshDetailsProps) {
const { scene } = useThree();
useEffect(() => {
const initializeNavigation = async () => {
try {
await initRecastNavigation();
if (!groupRef.current || groupRef.current.children.length === 0) {
return;
}
const meshes = groupRef?.current?.children as THREE.Mesh[];
const [positions, indices] = getPositionsAndIndices(meshes);
const cs = 0.5;
const ch = 0.5;
const walkableRadius = 0.89;
const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs,
ch,
walkableRadius: Math.round(walkableRadius / ch),
});
if (!success || !navMesh) {
return;
}
setNavMesh(navMesh);
const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer);
} catch (error) {}
};
initializeNavigation();
}, [scene, groupRef, lines.current]);
return null;
}

View File

@@ -0,0 +1,98 @@
import React, { useEffect, useState, useRef } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { NavMeshQuery } from "@recast-navigation/core";
import { Line } from "@react-three/drei";
// Define interface for props
interface PathNavigatorProps {
navMesh: any;
selectedPoints: any;
}
export default function PathNavigator({
navMesh,
selectedPoints,
}: PathNavigatorProps) {
const [path, setPath] = useState<[number, number, number][]>([]);
const progressRef = useRef(0);
const meshRef = useRef<THREE.Mesh | null>(null);
useEffect(() => {
if (selectedPoints.length === 2 && navMesh) {
const [start, end] = selectedPoints;
if (!start || !end) return;
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: computedPath } = navMeshQuery.computePath(start, end);
if (computedPath.length > 0) {
setPath(computedPath.map(({ x, y, z }) => [x, y + 0.1, z]));
progressRef.current = 0;
}
}
}, [selectedPoints, navMesh]);
useFrame((_, delta) => {
if (path.length > 1 && meshRef.current) {
const speed = 3;
progressRef.current += delta * speed;
let totalDistance = 0;
const distances: number[] = [];
for (let i = 0; i < path.length - 1; i++) {
const start = new THREE.Vector3(...path[i]);
const end = new THREE.Vector3(...path[i + 1]);
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
let coveredDistance = progressRef.current;
let accumulatedDistance = 0;
let index = 0;
while (
index < distances.length &&
coveredDistance > accumulatedDistance + distances[index]
) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...path[index]);
const end = new THREE.Vector3(...path[index + 1]);
const segmentDistance = distances[index];
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t); // Use clone() to avoid mutating the original vector
meshRef.current.position.copy(position);
const direction = new THREE.Vector3()
.subVectors(end, start)
.normalize();
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 0, 1),
direction
);
meshRef.current.quaternion.slerp(targetQuaternion, 0.1);
} else {
progressRef.current = totalDistance;
}
}
});
return (
<>
{path.length > 0 && <Line points={path} color="blue" lineWidth={3} />}
{path.length > 0 && (
<mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshNormalMaterial />
</mesh>
)}
</>
);
}

View File

@@ -0,0 +1,146 @@
import * as THREE from "three";
import { useEffect, useState } from "react";
import * as turf from "@turf/turf";
import * as Types from "../../../types/world/worldTypes";
import arrayLinesToObject from "../geomentries/lines/lineConvertions/arrayLinesToObject";
interface PolygonGeneratorProps {
groupRef: React.MutableRefObject<THREE.Group | null>;
lines: Types.RefLines;
plane: Types.RefMesh;
}
export default function PolygonGenerator({
groupRef,
lines,
plane,
}: PolygonGeneratorProps) {
// const [rooms, setRooms] = useState<THREE.Vector3[][]>([]);
useEffect(() => {
if (groupRef.current && plane.current) {
groupRef.current.add(plane.current.clone());
}
}, [groupRef, plane]);
useEffect(() => {
let allLines = arrayLinesToObject(lines.current);
const wallLines = allLines?.filter((line) => line?.type === "WallLine");
const aisleLines = allLines?.filter((line) => line?.type === "AisleLine");
const wallPoints = wallLines
.map((pair) => pair?.line.map((vals) => vals.position))
.filter((wall): wall is THREE.Vector3[] => !!wall);
const result = aisleLines.map((pair) =>
pair?.line.map((point) => ({
position: [point.position.x, point.position.z],
uuid: point.uuid,
}))
);
if (!result || result.some((line) => !line)) {
return;
}
const lineFeatures = result?.map((line: any) =>
turf.lineString(line.map((p: any) => p?.position))
);
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
renderWallGeometry(wallPoints);
let union: any = [];
polygons.features.forEach((feature) => {
union.push(feature);
});
if (union.length > 1) {
const unionResult = turf.union(turf.featureCollection(union));
if (unionResult?.geometry.type === "MultiPolygon") {
unionResult.geometry.coordinates.forEach((poly) => {
const coordinates = poly[0].map(([x, z]) => {
return new THREE.Vector3(x, 0, z);
});
renderBoxGeometry(coordinates);
});
} else if (unionResult?.geometry.type === "Polygon") {
const coordinates = unionResult.geometry.coordinates[0].map(
([x, z]) => {
return new THREE.Vector3(x, 0, z);
}
);
renderBoxGeometry(coordinates);
}
} else if (union.length === 1) {
const coordinates = union[0].geometry.coordinates[0].map(
([x, z]: [number, number]) => {
return new THREE.Vector3(x, 0, z);
}
);
// setRooms((prevRooms) => [...prevRooms, coordinates]);
}
}, [lines.current]);
const renderBoxGeometry = (coordinates: THREE.Vector3[]) => {
const minX = Math.min(...coordinates.map((p) => p.x));
const maxX = Math.max(...coordinates.map((p) => p.x));
const minZ = Math.min(...coordinates.map((p) => p.z));
const maxZ = Math.max(...coordinates.map((p) => p.z));
const width = maxX - minX;
const depth = maxZ - minZ;
const height = 3;
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({
color: "#ff66cc",
visible: false,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((minX + maxX) / 2, height / 2, (minZ + maxZ) / 2);
groupRef.current?.add(mesh);
};
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls.forEach((wall) => {
if (wall.length < 2) return;
for (let i = 0; i < wall.length - 1; i++) {
const start = new THREE.Vector3(wall[i].x, wall[i].y, wall[i].z);
const end = new THREE.Vector3(
wall[i + 1].x,
wall[i + 1].y,
wall[i + 1].z
);
const wallHeight = 10;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const wallGeometry = new THREE.BoxGeometry(length, wallHeight);
const wallMaterial = new THREE.MeshBasicMaterial({
color: "#aaa",
transparent: true,
opacity: 0.5,
});
const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial);
const midPoint = new THREE.Vector3()
.addVectors(start, end)
.multiplyScalar(0.5);
wallMesh.position.set(midPoint.x, wallHeight / 2, midPoint.z);
const quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
wallMesh.quaternion.copy(quaternion);
groupRef.current?.add(wallMesh);
}
});
};
return null;
}

View File

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

View File

@@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { import {
CommentsIcon, CommentsIcon,
DownloadIcon, DownloadIcon,
@@ -15,6 +15,7 @@ interface CardProps {
price: number; price: number;
rating: number; rating: number;
views: number; views: number;
image: string;
onSelectCard: (cardData: { onSelectCard: (cardData: {
assetName: string; assetName: string;
uploadedOn: string; uploadedOn: string;
@@ -30,6 +31,7 @@ const Card: React.FC<CardProps> = ({
price, price,
rating, rating,
views, views,
image,
onSelectCard, onSelectCard,
}) => { }) => {
const handleCardSelect = () => { const handleCardSelect = () => {
@@ -42,12 +44,19 @@ const Card: React.FC<CardProps> = ({
<DownloadIcon /> <DownloadIcon />
</div> </div>
<div className="image-container"> <div className="image-container">
<img src={assetImage} alt={assetName} /> <img src={image} alt={assetName} />
</div> </div>
<div className="assets-container"> <div className="assets-container">
<div className="name-container"> <div className="name-container">
<div className="assets-name">{assetName}</div> <div className="assets-name">{assetName.split("_").join(" ")}</div>
<div className="assets-date">{uploadedOn}</div> <div className="assets-date">
Uploaded on -{" "}
{new Date(uploadedOn).toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "2-digit",
})}
</div>
</div> </div>
<div className="details"> <div className="details">
<div className="content"> <div className="content">

View File

@@ -1,9 +1,25 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import Card from "./Card"; import Card from "./Card";
import AssetPreview from "./AssetPreview"; import AssetPreview from "./AssetPreview";
import RenderOverlay from "../../components/templates/Overlay"; import RenderOverlay from "../../components/templates/Overlay";
import { fetchAssets } from "../../services/marketplace/fetchAssets";
interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
}
const CardsContainer: React.FC = () => { const CardsContainer: React.FC = () => {
const [models, setModels] = useState<ModelData[]>([]);
const array = [ const array = [
{ {
id: 1, id: 1,
@@ -120,12 +136,23 @@ const CardsContainer: React.FC = () => {
}) => { }) => {
setSelectedCard(cardData); setSelectedCard(cardData);
}; };
const getAllAssets = async () => {
try {
const assetsData = await fetchAssets();
const reversedData = [...assetsData]?.reverse().slice(0, 8);
setModels(reversedData);
} catch (error) {
} finally {
}
};
useEffect(() => {
getAllAssets();
}, []);
return ( return (
<div className="cards-container-container"> <div className="cards-container-container">
<div className="header">Products You May Like</div> <div className="header">Products You May Like</div>
<div className="cards-wrapper-container"> <div className="cards-wrapper-container">
{array.map((asset) => ( {/* {array.map((asset) => (
<Card <Card
key={asset.id} key={asset.id}
assetName={asset.name} assetName={asset.name}
@@ -135,14 +162,27 @@ const CardsContainer: React.FC = () => {
views={asset.views} views={asset.views}
onSelectCard={handleCardSelect} onSelectCard={handleCardSelect}
/> />
))} ))} */}
{/* <RenderOverlay> */} {models.length > 0 &&
{selectedCard && ( models.map((assetDetail) => (
<AssetPreview <Card
selectedCard={selectedCard} key={assetDetail._id}
setSelectedCard={setSelectedCard} assetName={assetDetail?.filename}
uploadedOn={assetDetail.uploadDate.toString()}
price={36500}
rating={4.5}
views={500}
onSelectCard={handleCardSelect}
image={assetDetail.thumbnail}
/> />
)} ))}
{/* <RenderOverlay> */}
{selectedCard && (
<AssetPreview
selectedCard={selectedCard}
setSelectedCard={setSelectedCard}
/>
)}
{/* </RenderOverlay> */} {/* </RenderOverlay> */}
</div> </div>
</div> </div>

View File

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

View File

@@ -13,6 +13,7 @@ import DuplicationControls from "./duplicationControls";
import CopyPasteControls from "./copyPasteControls"; import CopyPasteControls from "./copyPasteControls";
import MoveControls from "./moveControls"; import MoveControls from "./moveControls";
import RotateControls from "./rotateControls"; import RotateControls from "./rotateControls";
import useModuleStore from "../../../../store/useModuleStore";
const SelectionControls: React.FC = () => { const SelectionControls: React.FC = () => {
const { camera, controls, gl, scene, pointer } = useThree(); const { camera, controls, gl, scene, pointer } = useThree();
@@ -27,6 +28,7 @@ const SelectionControls: React.FC = () => {
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]); const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
const boundingBoxRef = useRef<THREE.Mesh>(); const boundingBoxRef = useRef<THREE.Mesh>();
const { floorItems, setFloorItems } = useFloorItems(); const { floorItems, setFloorItems } = useFloorItems();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
@@ -97,13 +99,16 @@ const SelectionControls: React.FC = () => {
clearSelection(); clearSelection();
} }
if (!toggleView) { if (!toggleView && activeModule === "builder") {
helper.enabled = true; helper.enabled = true;
canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("contextmenu", onContextMenu); canvasElement.addEventListener("contextmenu", onContextMenu);
canvasElement.addEventListener("pointerup", onPointerUp); canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown); canvasElement.addEventListener("keydown", onKeyDown);
} else {
helper.enabled = false;
helper.dispose();
} }
return () => { return () => {
@@ -115,7 +120,13 @@ const SelectionControls: React.FC = () => {
helper.enabled = false; helper.enabled = false;
helper.dispose(); 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(() => { useFrame(() => {
if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { 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 SelectionControls from "./controls/selection/selectionControls";
import MeasurementTool from "./tools/measurementTool"; import MeasurementTool from "./tools/measurementTool";
import Simulation from "../simulation/simulation"; import Simulation from "../simulation/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets"; import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets";
// import Simulation from "./simulationtemp/simulation"; // import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
export default function Scene() { export default function Scene() {
@@ -30,7 +27,6 @@ export default function Scene() {
{ name: "backward", keys: ["ArrowDown", "s", "S"] }, { name: "backward", keys: ["ArrowDown", "s", "S"] },
{ name: "left", keys: ["ArrowLeft", "a", "A"] }, { name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "right", keys: ["ArrowRight", "d", "D"] }, { name: "right", keys: ["ArrowRight", "d", "D"] },
// { name: "jump", keys: ["Space"] },
], []) ], [])
@@ -47,7 +43,6 @@ export default function Scene() {
}} }}
> >
<DroppedObjects/>
<Controls /> <Controls />
<TransformControl /> <TransformControl />
<SelectionControls /> <SelectionControls />
@@ -63,6 +58,9 @@ export default function Scene() {
<MqttEvents /> <MqttEvents />
<Environment files={background} environmentIntensity={1.5} /> <Environment files={background} environmentIntensity={1.5} />
</Canvas> </Canvas>
</KeyboardControls> </KeyboardControls>
); );
} }

View File

@@ -28,7 +28,7 @@ import {
useShadows, useShadows,
useUpdateScene, useUpdateScene,
useWalls, useWalls,
useToolMode useToolMode,
} from "../../../store/store"; } from "../../../store/store";
////////// 3D Function Imports ////////// ////////// 3D Function Imports //////////
@@ -50,16 +50,17 @@ import { findEnvironment } from "../../../services/factoryBuilder/environment/fi
import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility";
import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp";
import ZoneGroup from "../../builder/groups/zoneGroup"; import ZoneGroup from "../../builder/groups/zoneGroup";
import Agv from "../../builder/agv/agv";
export default function World() { export default function World() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling. const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene. const scene = useRef() as Types.RefScene; // Reference to the scene.
const camera = useRef() as Types.RefCamera; // Reference to the camera object. const camera = useRef() as Types.RefCamera; // Reference to the camera object.
const controls = useRef<any>(); // Reference to the controls object. const controls = useRef<any>(); // Reference to the controls object.
const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
// Assigning the scene and camera from the Three.js state to the references. // Assigning the scene and camera from the Three.js state to the references.
@@ -68,47 +69,48 @@ export default function World() {
controls.current = state.controls; controls.current = state.controls;
raycaster.current = state.raycaster; raycaster.current = state.raycaster;
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference. const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference. const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items.
const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active.
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn. const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const floorGroupAisle = useRef() as Types.RefGroup; const floorGroupAisle = useRef() as Types.RefGroup;
const zoneGroup = useRef() as Types.RefGroup; const zoneGroup = useRef() as Types.RefGroup;
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item. const [selectedItemsIndex, setSelectedItemsIndex] =
const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D. const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D.
const { toolMode, setToolMode } = useToolMode(); const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not.
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { roofVisibility, setRoofVisibility } = useRoofVisibility(); const { roofVisibility, setRoofVisibility } = useRoofVisibility();
@@ -118,7 +120,6 @@ export default function World() {
const { walls, setWalls } = useWalls(); const { walls, setWalls } = useWalls();
const [RefTextupdate, setRefTextUpdate] = useState(-1000); const [RefTextupdate, setRefTextUpdate] = useState(-1000);
// const loader = new GLTFLoader(); // const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader(); // const dracoLoader = new DRACOLoader();
@@ -162,7 +163,14 @@ export default function World() {
dragPointControls.current.enabled = false; dragPointControls.current.enabled = false;
} }
if (toggleView) { if (toggleView) {
Layer2DVisibility(activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls); Layer2DVisibility(
activeLayer,
floorPlanGroup,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls
);
} else { } else {
setToolMode(null); setToolMode(null);
setDeletePointOrLine(false); setDeletePointOrLine(false);
@@ -179,11 +187,14 @@ export default function World() {
}, []); }, []);
useEffect(() => { useEffect(() => {
const email = localStorage.getItem('email') const email = localStorage.getItem("email");
const organization = (email!.split("@")[1]).split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
async function fetchVisibility() { async function fetchVisibility() {
const visibility = await findEnvironment(organization, localStorage.getItem('userId')!); const visibility = await findEnvironment(
organization,
localStorage.getItem("userId")!
);
if (visibility) { if (visibility) {
setRoofVisibility(visibility.roofVisibility); setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility); setWallVisibility(visibility.wallVisibility);
@@ -191,13 +202,33 @@ export default function World() {
} }
} }
fetchVisibility(); fetchVisibility();
}, []) }, []);
////////// UseFrame is Here ////////// ////////// UseFrame is Here //////////
useFrame(() => { useFrame(() => {
if (toolMode) { if (toolMode) {
Draw(state, plane, cursorPosition, floorPlanGroupPoint, floorPlanGroupLine, snappedPoint, isSnapped, isSnappedUUID, line, lines, ispreSnapped, floorPlanGroup, ReferenceLineMesh, LineCreated, setRefTextUpdate, Tube, anglesnappedPoint, isAngleSnapped, toolMode) Draw(
state,
plane,
cursorPosition,
floorPlanGroupPoint,
floorPlanGroupLine,
snappedPoint,
isSnapped,
isSnappedUUID,
line,
lines,
ispreSnapped,
floorPlanGroup,
ReferenceLineMesh,
LineCreated,
setRefTextUpdate,
Tube,
anglesnappedPoint,
isAngleSnapped,
toolMode
);
} }
}); });
@@ -304,7 +335,6 @@ export default function World() {
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> */} /> */}
<ZoneGroup /> <ZoneGroup />
<FloorGroupAilse <FloorGroupAilse
@@ -327,10 +357,9 @@ export default function World() {
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> />
<DrieHtmlTemp <DrieHtmlTemp itemsGroup={itemsGroup} />
itemsGroup={itemsGroup}
/>
<Agv lines={lines} plane={plane} />
</> </>
); );
} }

View File

@@ -3,7 +3,6 @@ import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes'; import * as Types from '../../../types/world/worldTypes';
import { useEffect } from 'react'; import { useEffect } from 'react';
interface Path { interface Path {
modeluuid: string; modeluuid: string;
modelName: string; modelName: string;
@@ -11,8 +10,9 @@ interface Path {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
rotation: [number, number, number]; rotation: [number, number, number];
actions: { uuid: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | []; actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; type: 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]; pathPosition: [number, number, number];
pathRotation: [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 point2Position = new THREE.Vector3(0, 1.25, -3.3);
const point1UUID = THREE.MathUtils.generateUUID(); const point1UUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID();
const middlePointUUID = THREE.MathUtils.generateUUID(); const middlePointUUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID();
const newPath: Path = { const newPath: Path = {
modeluuid: item.modeluuid, modeluuid: item.modeluuid,
@@ -43,22 +43,25 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
uuid: point1UUID, uuid: point1UUID,
position: [point1Position.x, point1Position.y, point1Position.z], position: [point1Position.x, point1Position.y, point1Position.z],
rotation: [0, 0, 0], 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: [], triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] },
}, },
{ {
uuid: middlePointUUID, uuid: middlePointUUID,
position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z],
rotation: [0, 0, 0], 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: [], triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] },
}, },
{ {
uuid: point2UUID, uuid: point2UUID,
position: [point2Position.x, point2Position.y, point2Position.z], position: [point2Position.x, point2Position.y, point2Position.z],
rotation: [0, 0, 0], 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: [], triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] },
}, },
], ],
pathPosition: [...item.position], pathPosition: [...item.position],
@@ -76,4 +79,4 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
return null; 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 React, { useEffect, useState } from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import { QuadraticBezierLine } from '@react-three/drei'; import { QuadraticBezierLine } from '@react-three/drei';
import { useConnections, useIsConnecting, useSimulationPaths } from '../../../store/store'; import { useIsConnecting, useSimulationPaths } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore'; import useModuleStore from '../../../store/useModuleStore';
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) { function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { gl, raycaster, scene, pointer, camera } = useThree(); const { gl, raycaster, scene, pointer, camera } = useThree();
const { connections, setConnections, addConnection } = useConnections(); const { setIsConnecting } = useIsConnecting();
const { isConnecting, setIsConnecting } = useIsConnecting();
const { simulationPaths, setSimulationPaths } = useSimulationPaths(); 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 [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 [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(() => { useEffect(() => {
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
let drag = false; let drag = false;
@@ -47,7 +126,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
if (intersects.length > 0) { if (intersects.length > 0) {
const intersected = intersects[0].object; const intersected = intersects[0].object;
if (intersected.name.includes("event-sphere")) { if (intersected.name.includes("action-sphere")) {
const pathUUID = intersected.userData.path.modeluuid; const pathUUID = intersected.userData.path.modeluuid;
const sphereUUID = intersected.uuid; const sphereUUID = intersected.uuid;
const worldPosition = new THREE.Vector3(); const worldPosition = new THREE.Vector3();
@@ -59,9 +138,12 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
); );
if (pathUUID) { if (pathUUID) {
const isAlreadyConnected = connections.some((connection) => // Check if sphere is already connected
connection.fromUUID === sphereUUID || const isAlreadyConnected = simulationPaths.some(path =>
connection.toConnections.some(conn => conn.toUUID === sphereUUID) path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
); );
if (isAlreadyConnected) { if (isAlreadyConnected) {
@@ -89,16 +171,12 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return; return;
} }
addConnection({ handleAddConnection(
fromPathUUID: firstSelected.pathUUID, firstSelected.pathUUID,
fromUUID: firstSelected.sphereUUID, firstSelected.sphereUUID,
toConnections: [{ toPathUUID: pathUUID, toUUID: sphereUUID }] pathUUID,
}); sphereUUID
);
setFirstSelected(null);
setCurrentLine(null);
setIsConnecting(false);
setHoveredSphere(null);
} }
} }
} }
@@ -106,7 +184,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
setFirstSelected(null); setFirstSelected(null);
setCurrentLine(null); setCurrentLine(null);
setIsConnecting(false); setIsConnecting(false);
setHoveredSphere(null);
} }
}; };
@@ -119,7 +196,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
setFirstSelected(null); setFirstSelected(null);
setCurrentLine(null); setCurrentLine(null);
setIsConnecting(false); setIsConnecting(false);
setHoveredSphere(null);
} }
return () => { return () => {
@@ -128,7 +204,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
canvasElement.removeEventListener("mousemove", onMouseMove); canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContextMenu); canvasElement.removeEventListener("contextmenu", onContextMenu);
}; };
}, [camera, scene, raycaster, firstSelected, connections]); }, [camera, scene, raycaster, firstSelected, simulationPaths]);
useFrame(() => { useFrame(() => {
if (firstSelected) { if (firstSelected) {
@@ -153,7 +229,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} }
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => 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) { 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 sphereUUID === sphere.userData.path.points[sphere.userData.path.points.length - 1].uuid
); );
const isAlreadyConnected = connections.some((connection) => const isAlreadyConnected = simulationPaths.some(path =>
connection.fromUUID === sphereUUID || path.points.some(point =>
connection.toConnections.some(conn => conn.toUUID === sphereUUID) point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
); );
if ( if (
@@ -186,10 +264,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} }
if (snappedSphere) { if (snappedSphere) {
setHoveredSphere(snappedSphere);
point = snappedSphere.position; point = snappedSphere.position;
} else {
setHoveredSphere(null);
} }
if (point) { if (point) {
@@ -224,49 +299,48 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} }
}); });
// Render connections from simulationPaths
useEffect(() => {
console.log('connections: ', connections);
}, [connections]);
return ( return (
<> <>
{connections.map((connection, index) => { {simulationPaths.flatMap(path =>
const fromSphere = scene.getObjectByProperty('uuid', connection.fromUUID); path.points.flatMap(point =>
const toSphere = scene.getObjectByProperty('uuid', connection.toConnections[0].toUUID); 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) { if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3(); const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3(); const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition); fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition); toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition); const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2); const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3( const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2, (fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2 (fromWorldPosition.z + toWorldPosition.z) / 2
); );
return ( return (
<QuadraticBezierLine <QuadraticBezierLine
key={index} key={`${point.uuid}-${target.pointUUID}-${index}`}
start={fromWorldPosition.toArray()} start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()} end={toWorldPosition.toArray()}
mid={midPoint.toArray()} mid={midPoint.toArray()}
color="white" color="white"
lineWidth={4} lineWidth={4}
dashed dashed
dashSize={1} dashSize={1}
dashScale={20} dashScale={20}
userData={connection} />
/> );
); }
} return null;
return null; })
})} )
)}
{currentLine && ( {currentLine && (
<QuadraticBezierLine <QuadraticBezierLine

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import SideBarRight from "../components/layout/sidebarRight/SideBarRight";
import useModuleStore from "../store/useModuleStore"; import useModuleStore from "../store/useModuleStore";
import RealTimeVisulization from "../components/ui/componets/RealTimeVisulization"; import RealTimeVisulization from "../components/ui/componets/RealTimeVisulization";
import Tools from "../components/ui/Tools"; import Tools from "../components/ui/Tools";
import Scene from "../modules/scene/scene"; // import Scene from "../modules/scene/scene";
import { import {
useSocketStore, useSocketStore,
useFloorItems, useFloorItems,
@@ -13,17 +13,18 @@ import {
useUserName, useUserName,
useWallItems, useWallItems,
useZones, useZones,
useLoadingProgress,
} from "../store/store"; } from "../store/store";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { usePlayButtonStore } from "../store/usePlayButtonStore"; import { usePlayButtonStore } from "../store/usePlayButtonStore";
import SimulationUI from "../modules/simulation/simulationUI";
import MarketPlace from "../modules/market/MarketPlace"; import MarketPlace from "../modules/market/MarketPlace";
import LoadingPage from "../components/templates/LoadingPage";
import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
const Project: React.FC = () => { const Project: React.FC = () => {
let navigate = useNavigate(); let navigate = useNavigate();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { loadingProgress, setLoadingProgress } = useLoadingProgress();
const { setUserName } = useUserName(); const { setUserName } = useUserName();
const { setOrganization } = useOrganization(); const { setOrganization } = useOrganization();
const { setFloorItems } = useFloorItems(); const { setFloorItems } = useFloorItems();
@@ -51,8 +52,10 @@ const Project: React.FC = () => {
return ( return (
<div className="project-main"> <div className="project-main">
{loadingProgress && <LoadingPage progress={loadingProgress} />}
{!isPlaying && ( {!isPlaying && (
<> <>
<ModuleToggle />
<ModuleToggle /> <ModuleToggle />
<SideBarLeft /> <SideBarLeft />
<SideBarRight /> <SideBarRight />

View File

@@ -2,10 +2,9 @@ import React, { useState, FormEvent } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { LogoIconLarge } from "../components/icons/Logo"; import { LogoIconLarge } from "../components/icons/Logo";
import { EyeIcon } from "../components/icons/ExportCommonIcons"; import { EyeIcon } from "../components/icons/ExportCommonIcons";
import { useOrganization, useUserName } from "../store/store"; import { useLoadingProgress, useOrganization, useUserName } from "../store/store";
import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi"; import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi";
import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi"; import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi";
// import LoadingPage from "../components/templates/LoadingPage";
const UserAuth: React.FC = () => { const UserAuth: React.FC = () => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@@ -14,7 +13,8 @@ const UserAuth: React.FC = () => {
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isSignIn, setIsSignIn] = useState(true); const [isSignIn, setIsSignIn] = useState(true);
const { userName, setUserName } = useUserName(); const { userName, setUserName } = useUserName();
const { organization, setOrganization } = useOrganization(); const { setOrganization } = useOrganization();
const { setLoadingProgress } = useLoadingProgress();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -33,6 +33,7 @@ const UserAuth: React.FC = () => {
localStorage.setItem("email", res.email); localStorage.setItem("email", res.email);
localStorage.setItem("userName", res.name); localStorage.setItem("userName", res.name);
if (res.isShare) { if (res.isShare) {
setLoadingProgress(1);
navigate("/Project"); navigate("/Project");
} }
} else if (res.message === "User Not Found!!! Kindly signup...") { } else if (res.message === "User Not Found!!! Kindly signup...") {
@@ -63,7 +64,6 @@ const UserAuth: React.FC = () => {
return ( return (
<> <>
{/* <LoadingPage progress={20} /> */}
<div className="auth-container"> <div className="auth-container">
<div className="logo-icon"> <div className="logo-icon">
<LogoIconLarge /> <LogoIconLarge />
@@ -141,7 +141,7 @@ const UserAuth: React.FC = () => {
</div> </div>
{!isSignIn && ( {!isSignIn && (
<div className="policy-checkbox"> <div className="policy-checkbox">
<input type="checkbox" name="" id="" required/> <input type="checkbox" name="" id="" required />
<div className="label"> <div className="label">
I have read and agree to the terms of service I have read and agree to the terms of service
</div> </div>

View File

@@ -1,7 +1,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { retrieveGLTF, storeGLTF } from '../../../components/scene/indexDB/idbUtils'; import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();

View File

@@ -0,0 +1,14 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const fetchAssets = async () => {
try {
const response = await fetch(`${BackEnd_url}/api/v1/getAllAssets`);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const result = await response.json();
return result;
} catch (error) {
console.log("error: ", error);
// throw new Error(error.message);
}
};

View File

@@ -2,30 +2,47 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_BACKEND_URL}`;
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
export const panelData = async (organization: string, zoneID: string, panelOrder: Side[]) => { export const panelData = async (
console.log('panelOrder: ', panelOrder); organization: string,
console.log('zoneID: ', zoneID); zoneID: string,
console.log('organization: ', organization); panelOrder: Side[]
try { ) => {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, { console.log("panelOrder: ", panelOrder);
method: "POST", console.log("zoneID: ", zoneID);
headers: { console.log("organization: ", organization);
"Content-Type": "application/json", try {
}, const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, {
body: JSON.stringify({ organization, zoneID, panelOrder }), method: "POST",
}); headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneID, panelOrder }),
});
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to add panelOrder for Zone"); throw new Error("Failed to add panelOrder for Zone");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
} }
};
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};
// {objects.map((obj, index) => (
// <group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
// <Html wrapperClass={obj.className}>
// <div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
// <div className="header">{obj.header}</div>
// <div className="data-values">
// <div className="value">{obj.value}</div>
// <div className="per">{obj.per}</div>
// </div>
// </div>
// </Html>
// </group>
// ))}

View File

@@ -2,6 +2,7 @@ import * as THREE from "three";
import * as Types from "../types/world/worldTypes"; import * as Types from "../types/world/worldTypes";
import { create } from "zustand"; import { create } from "zustand";
import { io } from "socket.io-client"; import { io } from "socket.io-client";
import { ComponentType, SVGProps } from "react";
export const useSocketStore = create<any>((set: any, get: any) => ({ export const useSocketStore = create<any>((set: any, get: any) => ({
socket: null, socket: null,
@@ -29,6 +30,11 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
}, },
})); }));
export const useLoadingProgress = create<{ loadingProgress: number; setLoadingProgress: (x: number) => void }>((set) => ({
loadingProgress: 1,
setLoadingProgress: (x: number) => set({ loadingProgress: x }),
}));
export const useOrganization = create<any>((set: any) => ({ export const useOrganization = create<any>((set: any) => ({
organization: "", organization: "",
setOrganization: (x: any) => set(() => ({ organization: x })), setOrganization: (x: any) => set(() => ({ organization: x })),
@@ -306,27 +312,19 @@ export const useSelectedPath = create<any>((set: any) => ({
})); }));
interface Path { interface Path {
modeluuid: string; modeluuid: string;
modelName: string; modelName: string;
points: { points: {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
rotation: [number, number, number]; rotation: [number, number, number];
actions: 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 }[] | [];
uuid: string; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
type: string; }[];
material: string; pathPosition: [number, number, number];
delay: number | string; pathRotation: [number, number, number];
spawnInterval: number | string; speed: number;
isUsed: boolean;
}[]
| [];
triggers: { uuid: string; type: string; isUsed: boolean }[] | [];
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
} }
interface SimulationPathsStore { interface SimulationPathsStore {
@@ -335,79 +333,8 @@ interface SimulationPathsStore {
} }
export const useSimulationPaths = create<SimulationPathsStore>((set) => ({ export const useSimulationPaths = create<SimulationPathsStore>((set) => ({
simulationPaths: [], simulationPaths: [],
setSimulationPaths: (paths) => set({ simulationPaths: paths }), setSimulationPaths: (paths: Path[]) => set({ simulationPaths: paths }),
}));
// interface Point {
// uuid: string;
// position: [number, number, number];
// rotation: [number, number, number];
// event: {
// uuid: string;
// type: string;
// material: string;
// delay: number | string;
// spawnInterval: number | string;
// isUsed: boolean;
// };
// trigger: {
// uuid: string;
// type: string;
// isUsed: boolean;
// };
// }
// interface Process {
// processId: string;
// processName: string;
// points: Point[];
// pathPosition: [number, number, number];
// pathRotation: [number, number, number];
// speed: number;
// isUsed: boolean;
// }
// interface Path {
// modeluuid: string;
// processes: Process[];
// }
// interface SimulationPathsStore {
// simulationPaths: Path[];
// setSimulationPaths: (paths: Path[]) => void;
// }
// export const useSimulationPaths = create<SimulationPathsStore>((set) => ({
// simulationPaths: [],
// setSimulationPaths: (paths) => set({ simulationPaths: paths }),
// }));
export const useConnections = create<Types.ConnectionStore>((set) => ({
connections: [],
setConnections: (connections) => set({ connections }),
addConnection: (newConnection) =>
set((state) => ({
connections: [...state.connections, newConnection],
})),
removeConnection: (fromUUID, toUUID) =>
set((state) => ({
connections: state.connections
.map((connection) =>
connection.fromUUID === fromUUID
? {
...connection,
toConnections: connection.toConnections.filter(
(to) => to.toUUID !== toUUID
),
}
: connection
)
.filter((connection) => connection.toConnections.length > 0),
})),
})); }));
export const useIsConnecting = create<any>((set: any) => ({ export const useIsConnecting = create<any>((set: any) => ({
@@ -428,30 +355,14 @@ export const usezonePosition = create<any>((set: any) => ({
setZonePosition: (x: any) => set({ zonePosition: x }), setZonePosition: (x: any) => set({ zonePosition: x }),
})); }));
interface EditPositionState { interface EditPositionState {
Edit: boolean; Edit: boolean;
setEdit: (value: boolean) => void; setEdit: (value: boolean) => void;
} }
export const useEditPosition = create<EditPositionState>((set) => ({ export const useEditPosition = create<EditPositionState>((set) => ({
Edit: false, Edit: false,
setEdit: (value) => set({ Edit: value }), // Properly updating the state setEdit: (value) => set({ Edit: value }), // Properly updating the state
})); }));
interface DroppedObject {
header: string;
value: string;
per: string;
className: string;
}
interface DroppedObjectsState {
objects: DroppedObject[];
addObject: (obj: DroppedObject) => void;
}
export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
objects: [],
addObject: (obj) => set((state) => ({ objects: [...state.objects, obj] })),
}));

View File

@@ -1,28 +1,28 @@
import { create } from "zustand"; import { create } from "zustand";
interface Measurement { interface Measurement {
name: string; name: string;
fields: string; fields: string;
} }
interface MeasurementStore { interface MeasurementStore {
measurements: Measurement[]; measurements: Measurement[];
interval: number; interval: number;
duration: string; duration: string;
setMeasurements: (newMeasurements: Measurement[]) => void; setMeasurements: (newMeasurements: Measurement[]) => void;
updateDuration: (newDuration: string) => void; updateDuration: (newDuration: string) => void;
} }
const useChartStore = create<MeasurementStore>((set) => ({ const useChartStore = create<MeasurementStore>((set) => ({
measurements: [], measurements: [],
interval: 1000, interval: 1000,
duration: "1h", duration: "1h",
setMeasurements: (newMeasurements) => setMeasurements: (newMeasurements) =>
set(() => ({ measurements: newMeasurements })), set(() => ({ measurements: newMeasurements })),
updateDuration: (newDuration) => updateDuration: (newDuration) =>
set(() => ({ duration: newDuration })), set(() => ({ duration: newDuration })),
})); }));
export default useChartStore; export default useChartStore;

View File

@@ -0,0 +1,78 @@
import { create } from "zustand";
type DroppedObject = {
className: string;
position: [number, number];
value?: number;
per?: string;
header?: string;
};
type Zone = {
zoneName: string;
zoneId: string;
objects: DroppedObject[];
};
type DroppedObjectsState = {
zones: Record<string, Zone>;
setZone: (zoneName: string, zoneId: string) => void;
addObject: (zoneName: string, newObject: DroppedObject) => void;
updateObjectPosition: (
zoneName: string,
index: number,
newPosition: [number, number]
) => void;
};
export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
zones: {},
setZone: (zoneName: string, zoneId: string) =>
set((state) => ({
zones: {
[zoneName]: state.zones[zoneName] || { zoneName, zoneId, objects: [] }, // Keep existing zone if it exists
},
})),
addObject: (zoneName: string, newObject: DroppedObject) =>
set((state) => ({
zones: {
...state.zones,
[zoneName]: {
...state.zones[zoneName],
objects: [...state.zones[zoneName].objects, newObject], // Append new object
},
},
})),
updateObjectPosition: (zoneName, index, newPosition) =>
set((state) => {
const zone = state.zones[zoneName];
if (!zone) return state;
return {
zones: {
[zoneName]: {
...zone,
objects: zone.objects.map((obj, i) =>
i === index ? { ...obj, position: newPosition } : obj
),
},
},
};
}),
}));
export interface DroppedObjects {
header: string;
value: string | number; // ✅ Allows both numbers and formatted strings
per: string;
className: string;
position: [number, number]; // ✅ Ensures position is a tuple
}
export interface Zones {
zoneName: string;
zoneId: string;
objects: DroppedObject[];
}

View File

@@ -8,6 +8,7 @@
background-color: var(--background-color-secondary); background-color: var(--background-color-secondary);
position: absolute; position: absolute;
left: 0; left: 0;
top: 0;
padding: 100px 50px; padding: 100px 50px;
padding-bottom: 32px; padding-bottom: 32px;
backdrop-filter: blur(6px); backdrop-filter: blur(6px);
@@ -89,6 +90,7 @@
color: var(--text-color); color: var(--text-color);
font-weight: $medium-weight; font-weight: $medium-weight;
font-size: $xlarge; font-size: $xlarge;
margin-bottom: 12px;
} }
.cards-wrapper-container { .cards-wrapper-container {
@@ -122,7 +124,13 @@
.image-container { .image-container {
width: 100%; width: 100%;
display: flex; display: flex;
max-height: 180px;
justify-content: center; justify-content: center;
img{
height: inherit;
width: 100%;
object-fit: cover;
}
} }
.assets-container { .assets-container {
@@ -133,6 +141,9 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 3px; gap: 3px;
.assets-name{
text-transform: capitalize;
}
.asstes-container { .asstes-container {
font-weight: #{$bold-weight}; font-weight: #{$bold-weight};

View File

@@ -9,7 +9,6 @@
padding: 6px; padding: 6px;
.floating { .floating {
min-height: 170px; min-height: 170px;
background: var(--background-color); background: var(--background-color);
border: 1.23px solid var(--border-color); border: 1.23px solid var(--border-color);

View File

@@ -530,7 +530,7 @@
border-radius: #{$border-radius-medium}; border-radius: #{$border-radius-medium};
path { path {
stroke: var(--accent-color); stroke: var(--accent-color);
stroke-width: 1.5px; strokeWidth: 1.5px;
} }
&:hover { &:hover {
background: var(--accent-color); background: var(--accent-color);

View File

@@ -15,7 +15,18 @@
border-radius: #{$border-radius-medium}; border-radius: #{$border-radius-medium};
transition: all 0.2s; transition: all 0.2s;
z-index: #{$z-index-default}; z-index: #{$z-index-default};
.floating {
width: 100%;
max-width: 250px;
min-height: 83px;
background: var(--background-color);
border: 1.23px solid var(--border-color);
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
border-radius: $border-radius-medium;
padding: 18px;
position: absolute;
z-index: 1000;
}
.scene-container { .scene-container {
overflow: hidden; overflow: hidden;
} }
@@ -69,7 +80,12 @@
display: none; display: none;
} }
} }
.no-zone {
@include flex-center;
gap: 4px;
padding: 4px;
color: var(--text-disabled);
}
.zone { .zone {
width: auto; width: auto;
background-color: var(--background-color); background-color: var(--background-color);