simulation #13

Merged
Vishnu merged 8 commits from simulation into main 2025-03-27 12:28:04 +00:00
28 changed files with 23183 additions and 22726 deletions

44220
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { useRef, useState, useMemo, useEffect } from "react";
import {
AddIcon,
InfoIcon,
@ -11,80 +11,332 @@ import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { handleResize } from "../../../../functions/handleResizePannel";
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 { selectedActionSphere } = useSelectedActionSphere();
console.log("selectedActionSphere: ", selectedActionSphere);
const [actionList, setActionList] = useState<string[]>([]);
const [triggerList, setTriggerList] = useState<string[]>([]);
const [selectedItem, setSelectedItem] = useState<{
type: "action" | "trigger";
name: string;
} | null>(null);
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const actionsContainerRef = 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 = () => {
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 = () => {
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) => {
setActionList(actionList.filter((_, i) => i !== index));
if (
selectedItem?.type === "action" &&
selectedItem.name === actionList[index]
) {
setSelectedItem(null);
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
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) => {
setTriggerList(triggerList.filter((_, i) => i !== index));
if (
selectedItem?.type === "trigger" &&
selectedItem.name === triggerList[index]
) {
setSelectedItem(null);
// Do the same for trigger toggle
const handleTriggerToggle = (uuid: 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,
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) => {
setSelectedItem({ type, name });
};
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
const [processes, setProcesses] = useState<string[]>([]);
const [activeProcess, setActiveProcesses] = useState<string>();
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
};
useEffect(() => {
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
}, [selectedActionSphere]);
return (
<div className="machine-mechanics-container">
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "path name not found"}
{selectedActionSphere?.path?.modelName || "point name not found"}
</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="actions">
<div className="header">
@ -99,25 +351,24 @@ const MachineMechanics: React.FC = () => {
style={{ height: "120px" }}
>
<div className="list-container">
{actionList.map((action, index) => (
{selectedPoint?.actions.map((action) => (
<div
key={index}
className={`list-item ${
selectedItem?.type === "action" &&
selectedItem.name === action
? "active"
: ""
}`}
key={action.uuid}
className={`list-item ${selectedItem?.type === "action" &&
selectedItem.item?.uuid === action.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => handleSelectItem("action", action)}
onClick={() => setSelectedItem({ type: "action", item: action })}
>
<RenameInput value={action} />
<RenameInput value={action.name} />
</div>
<div
className="remove-button"
onClick={() => handleRemoveAction(index)}
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div>
@ -146,25 +397,24 @@ const MachineMechanics: React.FC = () => {
style={{ height: "120px" }}
>
<div className="list-container">
{triggerList.map((trigger, index) => (
{selectedPoint?.triggers.map((trigger) => (
<div
key={index}
className={`list-item ${
selectedItem?.type === "trigger" &&
selectedItem.name === trigger
? "active"
: ""
}`}
key={trigger.uuid}
className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem.item?.uuid === trigger.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => handleSelectItem("trigger", trigger)}
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
>
<RenameInput value={trigger} />
<RenameInput value={trigger.name} />
</div>
<div
className="remove-button"
onClick={() => handleRemoveTrigger(index)}
onClick={() => handleDeleteTrigger(trigger.uuid)}
>
<RemoveIcon />
</div>
@ -183,28 +433,101 @@ const MachineMechanics: React.FC = () => {
<div className="selected-properties-container">
{selectedItem && (
<>
<div className="properties-header">{selectedItem.name}</div>
<LabledDropdown
defaultOption="On-hit"
options={["On-hit", "Buffer"]}
/>
<InputWithDropDown
label="Speed"
value=""
activeOption=".mm"
onChange={() => {}}
/>
<EyeDropInput />
<div className="properties-header">{selectedItem.item.name}</div>
{selectedItem.type === "action" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleActionToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
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 className="footer">
<InfoIcon />
By Selecting Path, you can create Object Triggers.
By selecting points, you can create events and triggers.
</div>
</div>
</div>
);
};
export default MachineMechanics;
export default MachineMechanics;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,21 +4,27 @@ import RenameInput from "./RenameInput";
type InputWithDropDownProps = {
label: string;
value: string;
min?: number
defaultValue?: string;
options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option
onClick?: () => void;
onChange: (newValue: string) => void;
editableLabel?: boolean;
placeholder?: string; // New placeholder prop
};
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label,
value,
min,
defaultValue,
options,
activeOption,
onClick,
onChange,
editableLabel = false,
placeholder = "Inherit", // Default empty placeholder
}) => {
const separatedWords = label
.split(/(?=[A-Z])/)
@ -38,11 +44,13 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
)}
<div className="input default" id={separatedWords}>
<input
type="text"
min={min}
type="number"
defaultValue={value}
onChange={(e) => {
onChange(e.target.value);
}}
placeholder={placeholder} // Added placeholder prop
/>
{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";
type LabledDropdownProps = {
defaultOption: string; // Initial active option
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 [activeOption, setActiveOption] = useState(defaultOption); // State for active option
const LabledDropdown: React.FC<LabledDropdownProps> = ({
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) => {
setActiveOption(option); // Update the active option state
setActiveOption(option);
if (onSelect) {
onSelect(option);
}
};
return (
<div className="value-field-container">
<div className="label">Type</div>
<div className={`value-field-container ${className}`}>
<div className="label">{label}</div>
<RegularDropDown
header={activeOption} // Display the current active option
options={options} // Use the options from props
onSelect={handleSelect} // Handle option selection
search = {false}
header={activeOption}
options={options}
onSelect={handleSelect}
search={search}
/>
</div>
);
};
export default LabledDropdown;
export default LabledDropdown;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
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 dracoLoader = new DRACOLoader();

View File

@ -29,6 +29,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) => ({
organization: "",
setOrganization: (x: any) => set(() => ({ organization: x })),
@ -306,27 +311,19 @@ export const useSelectedPath = create<any>((set: any) => ({
}));
interface Path {
modeluuid: string;
modelName: string;
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions:
| {
uuid: string;
type: string;
material: string;
delay: number | string;
spawnInterval: number | string;
isUsed: boolean;
}[]
| [];
triggers: { uuid: string; type: string; isUsed: boolean }[] | [];
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
modeluuid: string;
modelName: string;
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
}
interface SimulationPathsStore {
@ -335,79 +332,8 @@ interface SimulationPathsStore {
}
export const useSimulationPaths = create<SimulationPathsStore>((set) => ({
simulationPaths: [],
setSimulationPaths: (paths) => 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),
})),
simulationPaths: [],
setSimulationPaths: (paths: Path[]) => set({ simulationPaths: paths }),
}));
export const useIsConnecting = create<any>((set: any) => ({

View File

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

View File

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

View File

@ -69,7 +69,12 @@
display: none;
}
}
.no-zone {
@include flex-center;
gap: 4px;
padding: 4px;
color: var(--text-disabled);
}
.zone {
width: auto;
background-color: var(--background-color);