"updated single flow"
This commit is contained in:
commit
863d2c6393
|
@ -35,19 +35,32 @@ interface ProductionCapacityProps {
|
|||
// onPointerDown:any
|
||||
}
|
||||
|
||||
const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
||||
const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
rotation,
|
||||
onContextMenu,
|
||||
}) => {
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
||||
const {
|
||||
measurements: chartMeasurements,
|
||||
duration: chartDuration,
|
||||
name: widgetName,
|
||||
} = useChartStore();
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
const [name, setName] = useState("Widget")
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
const [duration, setDuration] = useState("1h");
|
||||
const [name, setName] = useState("Widget");
|
||||
const [chartData, setChartData] = useState<{
|
||||
labels: string[];
|
||||
datasets: any[];
|
||||
}>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
// Chart data for a week
|
||||
const defaultChartData = {
|
||||
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
|
||||
|
@ -101,7 +114,8 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||
return;
|
||||
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
|
@ -111,7 +125,6 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
interval: 1000,
|
||||
};
|
||||
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
@ -148,22 +161,21 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
const fetchSavedInputes = async () => {
|
||||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||
);
|
||||
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setName(response.data.widgetName)
|
||||
setmeasurements(response.data.Data.measurements);
|
||||
setDuration(response.data.Data.duration);
|
||||
setName(response.data.widgetName);
|
||||
} else {
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSavedInputes();
|
||||
|
@ -173,13 +185,9 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
if (selectedChartId?.id === id) {
|
||||
fetchSavedInputes();
|
||||
}
|
||||
}
|
||||
, [chartMeasurements, chartDuration, widgetName])
|
||||
}, [chartMeasurements, chartDuration, widgetName]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
|
||||
}, [rotation])
|
||||
useEffect(() => { }, [rotation]);
|
||||
const rotationDegrees = {
|
||||
x: (rotation[0] * 180) / Math.PI,
|
||||
y: (rotation[1] * 180) / Math.PI,
|
||||
|
@ -187,30 +195,44 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
};
|
||||
|
||||
const transformStyle = {
|
||||
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
||||
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg) translate(-50%, -50%)`,
|
||||
};
|
||||
|
||||
return (
|
||||
<Html position={position}
|
||||
|
||||
<Html
|
||||
position={position}
|
||||
scale={[0.5, 0.5, 0.5]}
|
||||
transform
|
||||
sprite
|
||||
zIndexRange={[1,0]}
|
||||
// center
|
||||
// distanceFactor={10} // Adjusted for visual balance
|
||||
zIndexRange={[1, 0]}
|
||||
style={{
|
||||
transform: transformStyle.transform,
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.1s ease-out'
|
||||
}}>
|
||||
<div className="productionCapacity-wrapper card"
|
||||
transformStyle: "preserve-3d",
|
||||
transition: "transform 0.1s ease-out",
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
e.preventDefault();
|
||||
// e.stopPropagation();
|
||||
}}
|
||||
wrapperClass="pointer-none"
|
||||
|
||||
>
|
||||
<div
|
||||
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
|
||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||
onContextMenu={onContextMenu}
|
||||
|
||||
style={{
|
||||
width: '300px', // Original width
|
||||
height: '300px', // Original height
|
||||
width: "300px", // Original width
|
||||
height: "300px", // Original height
|
||||
transform: transformStyle.transform,
|
||||
transformStyle: 'preserve-3d'
|
||||
transformStyle: "preserve-3d",
|
||||
position: "absolute",
|
||||
}}
|
||||
>
|
||||
<div className="headeproductionCapacityr-wrapper">
|
||||
|
@ -233,10 +255,18 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
|
|||
</div>{" "}
|
||||
<div className="bar-chart charts">
|
||||
{/* Bar Chart */}
|
||||
<Bar data={Object.keys(measurements).length > 0 ? chartData : defaultChartData} options={chartOptions} />
|
||||
<Bar
|
||||
data={
|
||||
Object.keys(measurements).length > 0
|
||||
? chartData
|
||||
: defaultChartData
|
||||
}
|
||||
options={chartOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Html>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -46,20 +46,32 @@ interface ReturnOfInvestmentProps {
|
|||
rotation: [number, number, number];
|
||||
onContextMenu?: (event: React.MouseEvent) => void;
|
||||
}
|
||||
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
||||
|
||||
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
rotation,
|
||||
onContextMenu,
|
||||
}) => {
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
||||
const {
|
||||
measurements: chartMeasurements,
|
||||
duration: chartDuration,
|
||||
name: widgetName,
|
||||
} = useChartStore();
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
const [name, setName] = useState("Widget")
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
const [duration, setDuration] = useState("1h");
|
||||
const [name, setName] = useState("Widget");
|
||||
const [chartData, setChartData] = useState<{
|
||||
labels: string[];
|
||||
datasets: any[];
|
||||
}>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
// Improved sample data for the smooth curve graph (single day)
|
||||
const graphData: ChartData<"line"> = {
|
||||
labels: [
|
||||
|
@ -129,7 +141,8 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||
return;
|
||||
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
|
@ -139,7 +152,6 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
interval: 1000,
|
||||
};
|
||||
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
@ -157,8 +169,10 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
return {
|
||||
label: datasetKey,
|
||||
data: responseData[datasetKey]?.values ?? [],
|
||||
borderColor: index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color
|
||||
backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)",
|
||||
borderColor:
|
||||
index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color
|
||||
backgroundColor:
|
||||
index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)",
|
||||
fill: true,
|
||||
tension: 0.4, // Smooth curve effect
|
||||
pointRadius: 0, // Hide dots
|
||||
|
@ -177,14 +191,15 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
const fetchSavedInputes = async () => {
|
||||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setName(response.data.widgetName)
|
||||
setmeasurements(response.data.Data.measurements);
|
||||
setDuration(response.data.Data.duration);
|
||||
setName(response.data.widgetName);
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
}
|
||||
|
@ -192,7 +207,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
console.error("There was an error!", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSavedInputes();
|
||||
|
@ -202,8 +217,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
if (selectedChartId?.id === id) {
|
||||
fetchSavedInputes();
|
||||
}
|
||||
}
|
||||
, [chartMeasurements, chartDuration, widgetName])
|
||||
}, [chartMeasurements, chartDuration, widgetName]);
|
||||
const rotationDegrees = {
|
||||
x: (rotation[0] * 180) / Math.PI,
|
||||
y: (rotation[1] * 180) / Math.PI,
|
||||
|
@ -215,26 +229,32 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||
};
|
||||
|
||||
return (
|
||||
<Html position={[position[0], position[1], position[2]]}
|
||||
<Html
|
||||
position={[position[0], position[1], position[2]]}
|
||||
scale={[0.5, 0.5, 0.5]}
|
||||
transform
|
||||
zIndexRange={[1, 0]}
|
||||
sprite
|
||||
style={{
|
||||
transform: transformStyle.transform,
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.1s ease-out'
|
||||
|
||||
transformStyle: "preserve-3d",
|
||||
transition: "transform 0.1s ease-out",
|
||||
}}
|
||||
>
|
||||
<div className="returnOfInvestment card"
|
||||
<div
|
||||
className={`returnOfInvestment card ${
|
||||
selectedChartId?.id === id ? "activeChart" : ""
|
||||
}`}
|
||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<div className="header">Return of Investment</div>
|
||||
<div className="lineGraph charts">
|
||||
{/* Smooth curve graph with two datasets */}
|
||||
<SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
||||
<SmoothLineGraphComponent
|
||||
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||
options={graphOptions}
|
||||
/>
|
||||
</div>
|
||||
<div className="returns-wrapper">
|
||||
<div className="icon">
|
||||
|
|
|
@ -13,16 +13,26 @@ interface StateWorkingProps {
|
|||
rotation: [number, number, number];
|
||||
onContextMenu?: (event: React.MouseEvent) => void;
|
||||
}
|
||||
const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
||||
const StateWorking: React.FC<StateWorkingProps> = ({
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
rotation,
|
||||
onContextMenu,
|
||||
}) => {
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
||||
const {
|
||||
measurements: chartMeasurements,
|
||||
duration: chartDuration,
|
||||
name: widgetName,
|
||||
} = useChartStore();
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
const [name, setName] = useState("Widget")
|
||||
const [duration, setDuration] = useState("1h");
|
||||
const [name, setName] = useState("Widget");
|
||||
const [datas, setDatas] = useState<any>({});
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
// const datas = [
|
||||
// { key: "Oil Tank:", value: "24/341" },
|
||||
// { key: "Oil Refin:", value: 36.023 },
|
||||
|
@ -33,7 +43,8 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
// ];
|
||||
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||
return;
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
const inputData = {
|
||||
measurements,
|
||||
|
@ -46,7 +57,6 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
socket.on("connect", startStream);
|
||||
socket.on("lastOutput", (response) => {
|
||||
const responseData = response;
|
||||
|
||||
|
||||
setDatas(responseData);
|
||||
});
|
||||
|
@ -59,14 +69,15 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
const fetchSavedInputes = async () => {
|
||||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setName(response.data.widgetName)
|
||||
setmeasurements(response.data.Data.measurements);
|
||||
setDuration(response.data.Data.duration);
|
||||
setName(response.data.widgetName);
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
}
|
||||
|
@ -74,10 +85,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
console.error("There was an error!", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSavedInputes();
|
||||
|
@ -87,8 +95,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
if (selectedChartId?.id === id) {
|
||||
fetchSavedInputes();
|
||||
}
|
||||
}
|
||||
, [chartMeasurements, chartDuration, widgetName])
|
||||
}, [chartMeasurements, chartDuration, widgetName]);
|
||||
|
||||
const rotationDegrees = {
|
||||
x: (rotation[0] * 180) / Math.PI,
|
||||
|
@ -100,19 +107,22 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
||||
};
|
||||
return (
|
||||
<Html position={[position[0], position[1], position[2]]}
|
||||
<Html
|
||||
position={[position[0], position[1], position[2]]}
|
||||
scale={[0.5, 0.5, 0.5]}
|
||||
transform
|
||||
zIndexRange={[1, 0]}
|
||||
sprite
|
||||
style={{
|
||||
transform: transformStyle.transform,
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.1s ease-out'
|
||||
|
||||
transformStyle: "preserve-3d",
|
||||
transition: "transform 0.1s ease-out",
|
||||
}}
|
||||
>
|
||||
<div className="stateWorking-wrapper card"
|
||||
<div
|
||||
className={`stateWorking-wrapper card ${
|
||||
selectedChartId?.id === id ? "activeChart" : ""
|
||||
}`}
|
||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
|
@ -120,12 +130,10 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
<div className="header">
|
||||
<span>State</span>
|
||||
<span>
|
||||
{datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
|
||||
{datas?.input1 ? datas.input1 : "input1"} <span>.</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="img">
|
||||
{/* <img src={image} alt="" /> */}
|
||||
</div>
|
||||
<div className="img">{/* <img src={image} alt="" /> */}</div>
|
||||
</div>
|
||||
{/* Data */}
|
||||
<div className="data-wrapper">
|
||||
|
@ -136,28 +144,52 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
</div>
|
||||
))} */}
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}</div>
|
||||
<div className="key">{datas?.input2 ? datas.input2 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input2?.fields
|
||||
? measurements.input2.fields
|
||||
: "input2"}
|
||||
</div>
|
||||
<div className="key">{datas?.input2 ? datas.input2 : "data"}</div>
|
||||
</div>
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}</div>
|
||||
<div className="key">{datas?.input3 ? datas.input3 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input3?.fields
|
||||
? measurements.input3.fields
|
||||
: "input3"}
|
||||
</div>
|
||||
<div className="key">{datas?.input3 ? datas.input3 : "data"}</div>
|
||||
</div>
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}</div>
|
||||
<div className="key">{datas?.input4 ? datas.input4 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input4?.fields
|
||||
? measurements.input4.fields
|
||||
: "input4"}
|
||||
</div>
|
||||
<div className="key">{datas?.input4 ? datas.input4 : "data"}</div>
|
||||
</div>
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}</div>
|
||||
<div className="key">{datas?.input5 ? datas.input5 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input5?.fields
|
||||
? measurements.input5.fields
|
||||
: "input5"}
|
||||
</div>
|
||||
<div className="key">{datas?.input5 ? datas.input5 : "data"}</div>
|
||||
</div>
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}</div>
|
||||
<div className="key">{datas?.input6 ? datas.input6 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input6?.fields
|
||||
? measurements.input6.fields
|
||||
: "input6"}
|
||||
</div>
|
||||
<div className="key">{datas?.input6 ? datas.input6 : "data"}</div>
|
||||
</div>
|
||||
<div className="data-table">
|
||||
<div className="data">{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}</div>
|
||||
<div className="key">{datas?.input7 ? datas.input7 : 'data'}</div>
|
||||
<div className="data">
|
||||
{measurements?.input7?.fields
|
||||
? measurements.input7.fields
|
||||
: "input7"}
|
||||
</div>
|
||||
<div className="key">{datas?.input7 ? datas.input7 : "data"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -166,5 +198,3 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||
};
|
||||
|
||||
export default StateWorking;
|
||||
|
||||
|
||||
|
|
|
@ -49,20 +49,32 @@ interface ThroughputProps {
|
|||
onContextMenu?: (event: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
||||
|
||||
const Throughput: React.FC<ThroughputProps> = ({
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
rotation,
|
||||
onContextMenu,
|
||||
}) => {
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
||||
const {
|
||||
measurements: chartMeasurements,
|
||||
duration: chartDuration,
|
||||
name: widgetName,
|
||||
} = useChartStore();
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
const [name, setName] = useState("Widget")
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
const [duration, setDuration] = useState("1h");
|
||||
const [name, setName] = useState("Widget");
|
||||
const [chartData, setChartData] = useState<{
|
||||
labels: string[];
|
||||
datasets: any[];
|
||||
}>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
// Sample data for the line graph
|
||||
const graphData: ChartData<"line"> = {
|
||||
|
@ -112,7 +124,8 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||
return;
|
||||
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
|
@ -122,7 +135,6 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
interval: 1000,
|
||||
};
|
||||
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
@ -157,14 +169,15 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
const fetchSavedInputes = async () => {
|
||||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
||||
const response = await axios.get(
|
||||
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||
);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setName(response.data.widgetName)
|
||||
setmeasurements(response.data.Data.measurements);
|
||||
setDuration(response.data.Data.duration);
|
||||
setName(response.data.widgetName);
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
}
|
||||
|
@ -172,7 +185,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
console.error("There was an error!", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSavedInputes();
|
||||
|
@ -182,8 +195,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
if (selectedChartId?.id === id) {
|
||||
fetchSavedInputes();
|
||||
}
|
||||
}
|
||||
, [chartMeasurements, chartDuration, widgetName])
|
||||
}, [chartMeasurements, chartDuration, widgetName]);
|
||||
const rotationDegrees = {
|
||||
x: (rotation[0] * 180) / Math.PI,
|
||||
y: (rotation[1] * 180) / Math.PI,
|
||||
|
@ -195,19 +207,22 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
};
|
||||
|
||||
return (
|
||||
<Html position={[position[0], position[1], position[2]]}
|
||||
<Html
|
||||
position={[position[0], position[1], position[2]]}
|
||||
scale={[0.5, 0.5, 0.5]}
|
||||
transform
|
||||
zIndexRange={[1, 0]}
|
||||
sprite
|
||||
style={{
|
||||
transform: transformStyle.transform,
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.1s ease-out'
|
||||
|
||||
transformStyle: "preserve-3d",
|
||||
transition: "transform 0.1s ease-out",
|
||||
}}
|
||||
>
|
||||
<div className="throughput-wrapper"
|
||||
<div
|
||||
className={`throughput-wrapper card ${
|
||||
selectedChartId?.id === id ? "activeChart" : ""
|
||||
}`}
|
||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
|
@ -234,7 +249,10 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||
</div>
|
||||
<div className="line-graph">
|
||||
{/* Line graph using react-chartjs-2 */}
|
||||
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
||||
<LineGraphComponent
|
||||
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||
options={graphOptions}
|
||||
/>
|
||||
</div>
|
||||
<div className="footer">
|
||||
You made an extra <span className="value">$1256.13</span> this month
|
||||
|
|
|
@ -129,6 +129,7 @@ const Assets: React.FC = () => {
|
|||
} else {
|
||||
try {
|
||||
const res = await getCategoryAsset(asset);
|
||||
console.log('res: ', res);
|
||||
setCategoryAssets(res);
|
||||
setFiltereredAssets(res);
|
||||
} catch (error) {}
|
||||
|
@ -147,7 +148,7 @@ const Assets: React.FC = () => {
|
|||
<div className="assets-container">
|
||||
{categoryAssets &&
|
||||
categoryAssets?.map((asset: any, index: number) => (
|
||||
<div key={index} className="assets">
|
||||
<div key={index} className="assets" id={asset.filename}>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
|
@ -171,6 +172,7 @@ const Assets: React.FC = () => {
|
|||
<div className="assets-wrapper">
|
||||
<div
|
||||
className="back-button"
|
||||
id="asset-backButtom"
|
||||
onClick={() => {
|
||||
setSelectedCategory(null);
|
||||
setCategoryAssets([]);
|
||||
|
@ -182,7 +184,7 @@ const Assets: React.FC = () => {
|
|||
<div className="assets-container">
|
||||
{categoryAssets &&
|
||||
categoryAssets?.map((asset: any, index: number) => (
|
||||
<div key={index} className="assets">
|
||||
<div key={index} className="assets" id={asset.filename}>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
|
@ -190,7 +192,7 @@ const Assets: React.FC = () => {
|
|||
onPointerDown={() =>
|
||||
setSelectedItem({
|
||||
name: asset.filename,
|
||||
id: asset.modelfileID,
|
||||
id: asset.AssetID,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
@ -222,6 +224,7 @@ const Assets: React.FC = () => {
|
|||
<div
|
||||
key={index}
|
||||
className="category"
|
||||
id={category}
|
||||
onClick={() => fetchCategoryAssets(category)}
|
||||
>
|
||||
<img
|
||||
|
|
|
@ -3,13 +3,11 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore
|
|||
import useTemplateStore from "../../../../store/useTemplateStore";
|
||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
||||
import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate";
|
||||
import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate";
|
||||
import { useSocketStore } from "../../../../store/store";
|
||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
|
||||
const Templates = () => {
|
||||
const { templates, removeTemplate } = useTemplateStore();
|
||||
const { setTemplates } = useTemplateStore();
|
||||
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
||||
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
|
||||
|
@ -35,15 +33,14 @@ const Templates = () => {
|
|||
let deleteTemplate = {
|
||||
organization: organization,
|
||||
templateID: id,
|
||||
}
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate)
|
||||
visualizationSocket.emit(
|
||||
"v2:viz-template:deleteTemplate",
|
||||
deleteTemplate
|
||||
);
|
||||
}
|
||||
removeTemplate(id);
|
||||
// let response = await deleteTemplateApi(id, organization);
|
||||
|
||||
// if (response.message === "Template deleted successfully") {
|
||||
// }
|
||||
} catch (error) {
|
||||
console.error("Error deleting template:", error);
|
||||
}
|
||||
|
@ -60,114 +57,59 @@ const Templates = () => {
|
|||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
templateID: template.id,
|
||||
}
|
||||
console.log('template: ', template);
|
||||
console.log('loadingTemplate: ', loadingTemplate);
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate)
|
||||
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate);
|
||||
}
|
||||
// let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization);
|
||||
|
||||
// if (response.message === "Template placed in Zone") {
|
||||
setSelectedZone({
|
||||
panelOrder: template.panelOrder,
|
||||
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
|
||||
widgets: template.widgets,
|
||||
setSelectedZone({
|
||||
panelOrder: template.panelOrder,
|
||||
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
|
||||
widgets: template.widgets,
|
||||
});
|
||||
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
|
||||
if (Array.isArray(template.floatingWidget)) {
|
||||
template.floatingWidget.forEach((val: any) => {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, val);
|
||||
});
|
||||
|
||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
|
||||
if (Array.isArray(template.floatingWidget)) {
|
||||
template.floatingWidget.forEach((val: any) => {
|
||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
|
||||
});
|
||||
}
|
||||
// }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading template:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
className="template-list"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
|
||||
gap: "1rem",
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
{templates.map((template) => (
|
||||
<div
|
||||
key={template.id}
|
||||
className="template-item"
|
||||
style={{
|
||||
border: "1px solid #e0e0e0",
|
||||
borderRadius: "8px",
|
||||
padding: "1rem",
|
||||
transition: "box-shadow 0.3s ease",
|
||||
}}
|
||||
>
|
||||
<div className="template-list">
|
||||
{templates.map((template, index) => (
|
||||
<div key={template.id} className="template-item">
|
||||
{template?.snapshot && (
|
||||
<div style={{ position: "relative", paddingBottom: "56.25%" }}>
|
||||
{" "}
|
||||
{/* 16:9 aspect ratio */}
|
||||
<div className="template-image-container">
|
||||
<img
|
||||
src={template.snapshot} // Corrected from template.image to template.snapshot
|
||||
src={template.snapshot}
|
||||
alt={`${template.name} preview`}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "transform 0.3s ease",
|
||||
// ':hover': {
|
||||
// transform: 'scale(1.05)'
|
||||
// }
|
||||
}}
|
||||
className="template-image"
|
||||
onClick={() => handleLoadTemplate(template)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginTop: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<div className="template-details">
|
||||
<div
|
||||
onClick={() => handleLoadTemplate(template)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontWeight: "500",
|
||||
// ':hover': {
|
||||
// textDecoration: 'underline'
|
||||
// }
|
||||
}}
|
||||
className="template-name"
|
||||
>
|
||||
{template.name}
|
||||
{/* {`Template ${index + 1}`} */}
|
||||
<RenameInput value={`Template ${index + 1}`} />
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleDeleteTemplate(template.id)}
|
||||
style={{
|
||||
padding: "0.25rem 0.5rem",
|
||||
background: "#ff4444",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "opacity 0.3s ease",
|
||||
// ':hover': {
|
||||
// opacity: 0.8
|
||||
// }
|
||||
}}
|
||||
className="delete-button"
|
||||
aria-label="Delete template"
|
||||
>
|
||||
Delete
|
||||
|
@ -176,14 +118,7 @@ const Templates = () => {
|
|||
</div>
|
||||
))}
|
||||
{templates.length === 0 && (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
color: "#666",
|
||||
padding: "2rem",
|
||||
gridColumn: "1 / -1",
|
||||
}}
|
||||
>
|
||||
<div className="no-templates">
|
||||
No saved templates yet. Create one in the visualization view!
|
||||
</div>
|
||||
)}
|
||||
|
@ -192,4 +127,3 @@ const Templates = () => {
|
|||
};
|
||||
|
||||
export default Templates;
|
||||
|
||||
|
|
|
@ -21,11 +21,13 @@ const Widgets3D = () => {
|
|||
className="widget-item"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
let name = widget.name
|
||||
let crt = e.target
|
||||
if (crt instanceof HTMLElement) {
|
||||
const widget = crt.cloneNode(true) as HTMLElement;
|
||||
e.dataTransfer.setDragImage(widget, 0, 0)
|
||||
e.dataTransfer.effectAllowed = "move"
|
||||
e.dataTransfer.setData("text/plain", "ui-" + name)
|
||||
}
|
||||
}}
|
||||
onPointerDown={() => {
|
||||
|
@ -40,7 +42,7 @@ const Widgets3D = () => {
|
|||
className="widget-image"
|
||||
src={widget.img}
|
||||
alt={widget.name}
|
||||
// draggable={false}
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -46,7 +46,7 @@ const WidgetsFloating = () => {
|
|||
))} */}
|
||||
{/* Floating 1 */}
|
||||
<SimpleCard
|
||||
header={"Today’s Money"}
|
||||
header={"Today’s Earnings"}
|
||||
icon={WalletIcon}
|
||||
value={"$53,000"}
|
||||
per={"+55%"}
|
||||
|
|
|
@ -45,7 +45,7 @@ const Header: React.FC = () => {
|
|||
<div
|
||||
key={index}
|
||||
className="user-profile"
|
||||
style={{ background: getAvatarColor(index) }}
|
||||
style={{ background: getAvatarColor(index, user.userName) }}
|
||||
>
|
||||
{user.userName[0]}
|
||||
</div>
|
||||
|
|
|
@ -16,19 +16,21 @@ import Analysis from "./analysis/Analysis";
|
|||
import Simulations from "./simulation/Simulations";
|
||||
import {
|
||||
useSelectedActionSphere,
|
||||
useselectedFloorItem,
|
||||
useSelectedFloorItem,
|
||||
} from "../../../store/store";
|
||||
import GlobalProperties from "./properties/GlobalProperties";
|
||||
import AsstePropertiies from "./properties/AssetProperties";
|
||||
import ZoneProperties from "./properties/ZoneProperties";
|
||||
import VehicleMechanics from "./mechanics/VehicleMechanics";
|
||||
import StaticMachineMechanics from "./mechanics/StaticMachineMechanics";
|
||||
import ArmBotMechanics from "./mechanics/ArmBotMechanics";
|
||||
|
||||
const SideBarRight: React.FC = () => {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleUI } = useToggleStore();
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useselectedFloorItem();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
// Reset activeList whenever activeModule changes
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
|
@ -42,9 +44,8 @@ const SideBarRight: React.FC = () => {
|
|||
<div className="sidebar-actions-container">
|
||||
{/* {activeModule === "builder" && ( */}
|
||||
<div
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("properties")}
|
||||
>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
|
@ -53,25 +54,22 @@ const SideBarRight: React.FC = () => {
|
|||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<div
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("mechanics")}
|
||||
>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</div>
|
||||
<div
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("simulations")}
|
||||
>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</div>
|
||||
<div
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("analysis")}
|
||||
>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
|
@ -103,7 +101,7 @@ const SideBarRight: React.FC = () => {
|
|||
)}
|
||||
{toggleUI &&
|
||||
subModule === "zoneProperties" &&
|
||||
activeModule === "builder" && (
|
||||
(activeModule === "builder" || activeModule === "simulation") && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<ZoneProperties />
|
||||
|
@ -132,10 +130,28 @@ const SideBarRight: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{subModule === "mechanics" &&
|
||||
selectedActionSphere &&
|
||||
selectedActionSphere.path.type === "StaticMachine" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<StaticMachineMechanics />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{subModule === "mechanics" &&
|
||||
selectedActionSphere &&
|
||||
selectedActionSphere.path.type === "ArmBot" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<ArmBotMechanics />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{subModule === "mechanics" && !selectedActionSphere && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<ConveyorMechanics />
|
||||
<ConveyorMechanics /> {/* default */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
import React, { useRef, useMemo, useCallback, useState } from "react";
|
||||
import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
|
||||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
import { handleResize } from "../../../../functions/handleResizePannel";
|
||||
|
||||
interface ConnectedModel {
|
||||
modelUUID: string;
|
||||
modelName: string;
|
||||
points: {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
index?: number;
|
||||
}[];
|
||||
triggers?: {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
isUsed: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
const ArmBotMechanics: React.FC = () => {
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const { socket } = useSocketStore();
|
||||
const [selectedProcessIndex, setSelectedProcessIndex] = useState<number | null>(null);
|
||||
const actionsContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Get connected models and their triggers
|
||||
const connectedModels = useMemo<ConnectedModel[]>(() => {
|
||||
if (!selectedActionSphere?.points?.uuid) return [];
|
||||
|
||||
const armBotPaths = simulationStates.filter(
|
||||
(path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
|
||||
);
|
||||
|
||||
const currentPoint = armBotPaths.find(
|
||||
(path) => path.points.uuid === selectedActionSphere.points.uuid
|
||||
)?.points;
|
||||
|
||||
if (!currentPoint?.connections?.targets) return [];
|
||||
|
||||
return currentPoint.connections.targets.reduce<ConnectedModel[]>((acc, target) => {
|
||||
const connectedModel = simulationStates.find(
|
||||
(model) => model.modeluuid === target.modelUUID
|
||||
);
|
||||
|
||||
if (!connectedModel) return acc;
|
||||
|
||||
let triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] = [];
|
||||
let points: { uuid: string; position: [number, number, number] }[] = [];
|
||||
|
||||
if (connectedModel.type === "Conveyor") {
|
||||
const conveyor = connectedModel as Types.ConveyorEventsSchema;
|
||||
|
||||
const connectedPointUUIDs = currentPoint?.connections?.targets
|
||||
.filter(t => t.modelUUID === connectedModel.modeluuid)
|
||||
.map(t => t.pointUUID) || [];
|
||||
|
||||
points = conveyor.points
|
||||
.map((point, idx) => ({
|
||||
uuid: point.uuid,
|
||||
position: point.position,
|
||||
index: idx
|
||||
}))
|
||||
.filter(point => connectedPointUUIDs.includes(point.uuid));
|
||||
|
||||
|
||||
triggers = conveyor.points.flatMap(p => p.triggers?.filter(t => t.isUsed) || []);
|
||||
}
|
||||
else if (connectedModel.type === "StaticMachine") {
|
||||
const staticMachine = connectedModel as Types.StaticMachineEventsSchema;
|
||||
|
||||
points = [{
|
||||
uuid: staticMachine.points.uuid,
|
||||
position: staticMachine.points.position
|
||||
}];
|
||||
|
||||
triggers = staticMachine.points.triggers ?
|
||||
[{
|
||||
uuid: staticMachine.points.triggers.uuid,
|
||||
name: staticMachine.points.triggers.name,
|
||||
type: staticMachine.points.triggers.type,
|
||||
isUsed: true // StaticMachine triggers are always considered used
|
||||
}] : [];
|
||||
}
|
||||
|
||||
if (!acc.some(m => m.modelUUID === connectedModel.modeluuid)) {
|
||||
acc.push({
|
||||
modelUUID: connectedModel.modeluuid,
|
||||
modelName: connectedModel.modelName,
|
||||
points,
|
||||
triggers
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}, [selectedActionSphere, simulationStates]);
|
||||
|
||||
// Get triggers from connected models
|
||||
const connectedTriggers = useMemo(() => {
|
||||
return connectedModels.flatMap(model =>
|
||||
(model.triggers || []).map(trigger => ({
|
||||
...trigger,
|
||||
displayName: `${model.modelName} - ${trigger.name}`,
|
||||
modelUUID: model.modelUUID
|
||||
}))
|
||||
);
|
||||
}, [connectedModels]);
|
||||
|
||||
// Get all points from connected models
|
||||
const connectedPoints = useMemo(() => {
|
||||
return connectedModels.flatMap(model =>
|
||||
model.points.map(point => ({
|
||||
...point,
|
||||
displayName: `${model.modelName} - Point${typeof point.index === 'number' ? ` ${point.index}` : ''}`,
|
||||
modelUUID: model.modelUUID
|
||||
}))
|
||||
);
|
||||
}, [connectedModels]);
|
||||
|
||||
|
||||
const { selectedPoint } = useMemo(() => {
|
||||
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null };
|
||||
|
||||
const armBotPaths = simulationStates.filter(
|
||||
(path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
|
||||
);
|
||||
|
||||
const points = armBotPaths.find(
|
||||
(path) => path.points.uuid === selectedActionSphere.points.uuid
|
||||
)?.points;
|
||||
|
||||
return {
|
||||
selectedPoint: points || null
|
||||
};
|
||||
}, [selectedActionSphere, simulationStates]);
|
||||
|
||||
const updateBackend = async (updatedPath: Types.ArmBotEventsSchema | undefined) => {
|
||||
if (!updatedPath) return;
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modeluuid: updatedPath.modeluuid,
|
||||
eventData: { type: "ArmBot", points: updatedPath.points }
|
||||
}
|
||||
console.log('data: ', data);
|
||||
|
||||
socket.emit('v2:model-asset:updateEventData', data);
|
||||
}
|
||||
|
||||
const handleActionUpdate = useCallback((updatedAction: Partial<Types.ArmBotEventsSchema['points']['actions']>) => {
|
||||
if (!selectedActionSphere?.points?.uuid || !selectedPoint) return;
|
||||
|
||||
const updatedPaths = simulationStates.map((path) => {
|
||||
if (path.type === "ArmBot" && path.points.uuid === selectedActionSphere.points.uuid) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
actions: {
|
||||
...path.points.actions,
|
||||
...updatedAction
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
const updatedPath = updatedPaths.find(
|
||||
(path): path is Types.ArmBotEventsSchema =>
|
||||
path.type === "ArmBot" &&
|
||||
path.points.uuid === selectedActionSphere.points.uuid
|
||||
);
|
||||
updateBackend(updatedPath);
|
||||
|
||||
setSimulationStates(updatedPaths);
|
||||
}, [selectedActionSphere?.points?.uuid, selectedPoint, simulationStates, setSimulationStates]);
|
||||
|
||||
const handleSpeedChange = useCallback((speed: number) => {
|
||||
handleActionUpdate({ speed });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleProcessChange = useCallback((processes: Types.ArmBotEventsSchema['points']['actions']['processes']) => {
|
||||
handleActionUpdate({ processes });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleAddProcess = useCallback(() => {
|
||||
if (!selectedPoint) return;
|
||||
|
||||
const newProcess: any = {
|
||||
triggerId: "",
|
||||
startPoint: "",
|
||||
endPoint: ""
|
||||
};
|
||||
|
||||
const updatedProcesses = selectedPoint.actions.processes ? [...selectedPoint.actions.processes, newProcess] : [newProcess];
|
||||
|
||||
handleProcessChange(updatedProcesses);
|
||||
setSelectedProcessIndex(updatedProcesses.length - 1);
|
||||
}, [selectedPoint, handleProcessChange]);
|
||||
|
||||
const handleDeleteProcess = useCallback((index: number) => {
|
||||
if (!selectedPoint?.actions.processes) return;
|
||||
|
||||
const updatedProcesses = [...selectedPoint.actions.processes];
|
||||
updatedProcesses.splice(index, 1);
|
||||
|
||||
handleProcessChange(updatedProcesses);
|
||||
|
||||
// Reset selection if deleting the currently selected process
|
||||
if (selectedProcessIndex === index) {
|
||||
setSelectedProcessIndex(null);
|
||||
} else if (selectedProcessIndex !== null && selectedProcessIndex > index) {
|
||||
// Adjust selection index if needed
|
||||
setSelectedProcessIndex(selectedProcessIndex - 1);
|
||||
}
|
||||
}, [selectedPoint, selectedProcessIndex, handleProcessChange]);
|
||||
|
||||
const handleTriggerSelect = useCallback((displayName: string, index: number) => {
|
||||
const selected = connectedTriggers.find(t => t.displayName === displayName);
|
||||
if (!selected || !selectedPoint?.actions.processes) return;
|
||||
|
||||
const oldProcess = selectedPoint.actions.processes[index];
|
||||
|
||||
const updatedProcesses = [...selectedPoint.actions.processes];
|
||||
|
||||
// Only reset start/end if new trigger invalidates them (your logic can expand this)
|
||||
updatedProcesses[index] = {
|
||||
...oldProcess,
|
||||
triggerId: selected.uuid,
|
||||
startPoint: oldProcess.startPoint || "", // preserve if exists
|
||||
endPoint: oldProcess.endPoint || "" // preserve if exists
|
||||
};
|
||||
|
||||
handleProcessChange(updatedProcesses);
|
||||
}, [connectedTriggers, selectedPoint, handleProcessChange]);
|
||||
|
||||
const handleStartPointSelect = useCallback((displayName: string, index: number) => {
|
||||
if (!selectedPoint?.actions.processes) return;
|
||||
|
||||
const point = connectedPoints.find(p => p.displayName === displayName);
|
||||
if (!point) return;
|
||||
|
||||
const updatedProcesses = [...selectedPoint.actions.processes];
|
||||
updatedProcesses[index] = {
|
||||
...updatedProcesses[index],
|
||||
startPoint: point.uuid
|
||||
};
|
||||
|
||||
handleProcessChange(updatedProcesses);
|
||||
}, [selectedPoint, connectedPoints, handleProcessChange]);
|
||||
|
||||
const handleEndPointSelect = useCallback((displayName: string, index: number) => {
|
||||
if (!selectedPoint?.actions.processes) return;
|
||||
|
||||
const point = connectedPoints.find(p => p.displayName === displayName);
|
||||
if (!point) return;
|
||||
|
||||
const updatedProcesses = [...selectedPoint.actions.processes];
|
||||
updatedProcesses[index] = {
|
||||
...updatedProcesses[index],
|
||||
endPoint: point.uuid
|
||||
};
|
||||
|
||||
handleProcessChange(updatedProcesses);
|
||||
}, [selectedPoint, connectedPoints, handleProcessChange]);
|
||||
|
||||
const getProcessByIndex = useCallback((index: number) => {
|
||||
if (!selectedPoint?.actions.processes || index >= selectedPoint.actions.processes.length) return null;
|
||||
return selectedPoint.actions.processes[index];
|
||||
}, [selectedPoint]);
|
||||
|
||||
const getFilteredTriggerOptions = (currentIndex: number) => {
|
||||
const usedTriggerUUIDs = selectedPoint?.actions.processes?.filter((_, i) => i !== currentIndex).map(p => p.triggerId).filter(Boolean) ?? [];
|
||||
|
||||
return connectedTriggers.filter(trigger => !usedTriggerUUIDs.includes(trigger.uuid)).map(trigger => trigger.displayName);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "ArmBot point not found"}
|
||||
</div>
|
||||
|
||||
<div className="machine-mechanics-content-container">
|
||||
<div className="selected-properties-container">
|
||||
<div className="properties-header">ArmBot Properties</div>
|
||||
|
||||
{selectedPoint && (
|
||||
<>
|
||||
<InputWithDropDown
|
||||
key={`speed-${selectedPoint.uuid}`}
|
||||
label="ArmBot Speed"
|
||||
value={selectedPoint.actions.speed.toString()}
|
||||
onChange={(value) => handleSpeedChange(parseInt(value))}
|
||||
/>
|
||||
|
||||
<div className="actions">
|
||||
<div className="header">
|
||||
<div className="header-value">Processes</div>
|
||||
<div className="add-button" onClick={handleAddProcess}>
|
||||
<AddIcon /> Add
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="lists-main-container"
|
||||
ref={actionsContainerRef}
|
||||
style={{ height: "120px" }}
|
||||
>
|
||||
<div className="list-container">
|
||||
{selectedPoint.actions.processes?.map((process, index) => (
|
||||
<div
|
||||
key={`process-${index}`}
|
||||
className={`list-item ${selectedProcessIndex === index ? "active" : ""}`}
|
||||
>
|
||||
<div
|
||||
className="value"
|
||||
onClick={() => setSelectedProcessIndex(index)}
|
||||
>
|
||||
Process {index + 1}
|
||||
</div>
|
||||
<div
|
||||
className="remove-button"
|
||||
onClick={() => handleDeleteProcess(index)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="resize-icon"
|
||||
id="action-resize"
|
||||
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
|
||||
>
|
||||
<ResizeHeightIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedProcessIndex !== null && (
|
||||
<div className="process-configuration">
|
||||
<LabledDropdown
|
||||
key={`trigger-select-${selectedProcessIndex}`}
|
||||
label="Select Trigger"
|
||||
defaultOption={
|
||||
connectedTriggers.find(t =>
|
||||
t.uuid === getProcessByIndex(selectedProcessIndex)?.triggerId
|
||||
)?.displayName || 'Select a trigger'
|
||||
}
|
||||
onSelect={(value) => handleTriggerSelect(value, selectedProcessIndex)}
|
||||
options={getFilteredTriggerOptions(selectedProcessIndex)}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
key={`start-point-${selectedProcessIndex}`}
|
||||
label="Start Point"
|
||||
defaultOption={
|
||||
connectedPoints.find(p =>
|
||||
p.uuid === getProcessByIndex(selectedProcessIndex)?.startPoint
|
||||
)?.displayName || 'Select start point'
|
||||
}
|
||||
onSelect={(value) => handleStartPointSelect(value, selectedProcessIndex)}
|
||||
options={connectedPoints.map(point => point.displayName)}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
key={`end-point-${selectedProcessIndex}`}
|
||||
label="End Point"
|
||||
defaultOption={
|
||||
connectedPoints.find(p =>
|
||||
p.uuid === getProcessByIndex(selectedProcessIndex)?.endPoint
|
||||
)?.displayName || 'Select end point'
|
||||
}
|
||||
onSelect={(value) => handleEndPointSelect(value, selectedProcessIndex)}
|
||||
options={connectedPoints.map(point => point.displayName)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
Configure ArmBot properties and trigger-based processes.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ArmBotMechanics);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,188 @@
|
|||
import React, { useRef, useMemo, useCallback } from "react";
|
||||
import { InfoIcon } from "../../../icons/ExportCommonIcons";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
|
||||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
|
||||
|
||||
const StaticMachineMechanics: React.FC = () => {
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const propertiesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { selectedPoint, connectedPointUuids } = useMemo(() => {
|
||||
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
|
||||
|
||||
const staticMachinePaths = simulationStates.filter(
|
||||
(path): path is Types.StaticMachineEventsSchema => path.type === "StaticMachine"
|
||||
);
|
||||
|
||||
const points = staticMachinePaths.find(
|
||||
(path) => path.points.uuid === selectedActionSphere.points.uuid
|
||||
)?.points;
|
||||
|
||||
if (!points) return { selectedPoint: null, connectedPointUuids: [] };
|
||||
|
||||
const connectedUuids: string[] = [];
|
||||
if (points.connections?.targets) {
|
||||
points.connections.targets.forEach(target => {
|
||||
connectedUuids.push(target.pointUUID);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
selectedPoint: points,
|
||||
connectedPointUuids: connectedUuids
|
||||
};
|
||||
}, [selectedActionSphere, simulationStates]);
|
||||
|
||||
const updateBackend = async (updatedPath: Types.StaticMachineEventsSchema | undefined) => {
|
||||
if (!updatedPath) return;
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||
|
||||
// await setEventApi(
|
||||
// organization,
|
||||
// updatedPath.modeluuid,
|
||||
// { type: "Vehicle", points: updatedPath.points }
|
||||
// );
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modeluuid: updatedPath.modeluuid,
|
||||
eventData: { type: "StaticMachine", points: updatedPath.points }
|
||||
}
|
||||
|
||||
socket.emit('v2:model-asset:updateEventData', data);
|
||||
}
|
||||
|
||||
const handleActionUpdate = useCallback((updatedAction: Partial<Types.StaticMachineEventsSchema['points']['actions']>) => {
|
||||
if (!selectedActionSphere?.points?.uuid) return;
|
||||
|
||||
const updatedPaths = simulationStates.map((path) => {
|
||||
if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
actions: {
|
||||
...path.points.actions,
|
||||
...updatedAction
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
const updatedPath = updatedPaths.find(
|
||||
(path): path is Types.StaticMachineEventsSchema =>
|
||||
path.type === "StaticMachine" &&
|
||||
path.points.uuid === selectedActionSphere.points.uuid
|
||||
);
|
||||
updateBackend(updatedPath);
|
||||
|
||||
setSimulationStates(updatedPaths);
|
||||
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
|
||||
|
||||
const handleBufferChange = useCallback((buffer: number) => {
|
||||
handleActionUpdate({ buffer });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleMaterialChange = useCallback((material: string) => {
|
||||
handleActionUpdate({ material });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleTriggerChange = useCallback((updatedTrigger: Partial<Types.StaticMachineEventsSchema['points']['triggers']>) => {
|
||||
if (!selectedActionSphere?.points?.uuid) return;
|
||||
|
||||
const updatedPaths = simulationStates.map((path) => {
|
||||
if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
triggers: {
|
||||
...path.points.triggers,
|
||||
...updatedTrigger
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
const updatedPath = updatedPaths.find(
|
||||
(path): path is Types.StaticMachineEventsSchema =>
|
||||
path.type === "StaticMachine" &&
|
||||
path.points.uuid === selectedActionSphere.points.uuid
|
||||
);
|
||||
updateBackend(updatedPath);
|
||||
|
||||
setSimulationStates(updatedPaths);
|
||||
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
|
||||
|
||||
const handleTriggerTypeChange = useCallback((type: string) => {
|
||||
handleTriggerChange({ type });
|
||||
}, [handleTriggerChange]);
|
||||
|
||||
return (
|
||||
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "Machine point not found"}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="machine-mechanics-content-container">
|
||||
<div className="selected-properties-container" ref={propertiesContainerRef}>
|
||||
<div className="properties-header">Machine Properties</div>
|
||||
|
||||
{selectedPoint && (
|
||||
<>
|
||||
<InputWithDropDown
|
||||
key={`buffer-${selectedPoint.uuid}`}
|
||||
label="Buffer Time"
|
||||
value={selectedPoint.actions.buffer.toString()}
|
||||
onChange={(value) => handleBufferChange(parseInt(value))}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
key={`material-${selectedPoint.uuid}`}
|
||||
label="Material"
|
||||
defaultOption={selectedPoint.actions.material}
|
||||
onSelect={(value) => handleMaterialChange(value)}
|
||||
options={["Inherit", "Crate", "Box"]}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
key={`trigger-type-${selectedPoint.uuid}`}
|
||||
label="Trigger Type"
|
||||
defaultOption={selectedPoint.triggers.type}
|
||||
onSelect={(value) => handleTriggerTypeChange(value)}
|
||||
options={["OnComplete", "OnStart"]}
|
||||
/>
|
||||
|
||||
{/* <LabeledButton
|
||||
label="Reset"
|
||||
value="Reset Settings"
|
||||
onClick={() => {
|
||||
// Implement reset functionality if needed
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
Configure machine interaction properties and triggers.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(StaticMachineMechanics);
|
|
@ -5,6 +5,7 @@ import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionS
|
|||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import PositionInput from "../customInput/PositionInputs";
|
||||
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
|
||||
import LabeledButton from "../../../ui/inputs/LabledButton";
|
||||
|
||||
const VehicleMechanics: React.FC = () => {
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
|
@ -126,6 +127,33 @@ const VehicleMechanics: React.FC = () => {
|
|||
setSimulationStates(updatedPaths);
|
||||
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
|
||||
|
||||
|
||||
const ResetVehicleState = React.useCallback(() => {
|
||||
if (!selectedActionSphere?.points?.uuid) return;
|
||||
|
||||
const updatedPaths = simulationStates.map((state) => {
|
||||
if (state.type === "Vehicle" && state.points.uuid === selectedActionSphere.points.uuid) {
|
||||
return {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
actions: { ...state.points.actions, start: {}, end: {} }
|
||||
}
|
||||
};
|
||||
}
|
||||
return state;
|
||||
});
|
||||
|
||||
const updatedPath = updatedPaths.find(
|
||||
(path): path is Types.VehicleEventsSchema =>
|
||||
path.type === "Vehicle" &&
|
||||
path.points.uuid === selectedActionSphere.points.uuid
|
||||
);
|
||||
updateBackend(updatedPath);
|
||||
|
||||
setSimulationStates(updatedPaths);
|
||||
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
|
||||
|
||||
const handleStartEyeDropClick = () => {
|
||||
setEditingPoint('start');
|
||||
setEyeDropMode(true);
|
||||
|
@ -193,6 +221,14 @@ const VehicleMechanics: React.FC = () => {
|
|||
handleEyeDropClick={handleEndEyeDropClick}
|
||||
/>
|
||||
|
||||
<LabeledButton
|
||||
label="Reset"
|
||||
value="Clear Points"
|
||||
onClick={() => {
|
||||
ResetVehicleState();
|
||||
}}
|
||||
/>
|
||||
|
||||
<InputWithDropDown
|
||||
key={`hitcount-${selectedPoint.uuid}`}
|
||||
label="Hit Count"
|
||||
|
|
|
@ -4,7 +4,7 @@ import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
|||
import { RemoveIcon } from "../../../icons/ExportCommonIcons";
|
||||
import PositionInput from "../customInput/PositionInputs";
|
||||
import RotationInput from "../customInput/RotationInput";
|
||||
import { useselectedFloorItem } from "../../../../store/store";
|
||||
import { useSelectedFloorItem } from "../../../../store/store";
|
||||
import * as THREE from "three";
|
||||
|
||||
interface UserData {
|
||||
|
@ -16,7 +16,7 @@ interface UserData {
|
|||
const AssetProperties: React.FC = () => {
|
||||
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
|
||||
const [nextId, setNextId] = useState(1); // Unique ID for new entries
|
||||
const { selectedFloorItem } = useselectedFloorItem();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
// Function to handle adding new user data
|
||||
const handleAddUserData = () => {
|
||||
const newUserData: UserData = {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
|||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import Vector3Input from "../customInput/Vector3Input";
|
||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||
import { useEditPosition, usezonePosition, usezoneTarget } from "../../../../store/store";
|
||||
import { useEditPosition, usezonePosition, useZones, usezoneTarget } from "../../../../store/store";
|
||||
import { zoneCameraUpdate } from "../../../../services/realTimeVisulization/zoneData/zoneCameraUpdation";
|
||||
|
||||
const ZoneProperties: React.FC = () => {
|
||||
|
@ -10,6 +10,7 @@ const ZoneProperties: React.FC = () => {
|
|||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { zonePosition, setZonePosition } = usezonePosition();
|
||||
const { zoneTarget, setZoneTarget } = usezoneTarget();
|
||||
const { zones, setZones } = useZones();
|
||||
|
||||
useEffect(() => {
|
||||
setZonePosition(selectedZone.zoneViewPortPosition)
|
||||
|
@ -28,11 +29,14 @@ const ZoneProperties: React.FC = () => {
|
|||
};
|
||||
|
||||
let response = await zoneCameraUpdate(zonesdata, organization);
|
||||
console.log('response: ', response);
|
||||
if (response.message === "updated successfully") {
|
||||
setEdit(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
|
||||
setEdit(false);
|
||||
} catch (error) {
|
||||
console.error("Error in handleSetView:", error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,17 +44,32 @@ const ZoneProperties: React.FC = () => {
|
|||
setEdit(!Edit); // This will toggle the `Edit` state correctly
|
||||
}
|
||||
|
||||
function handleZoneNameChange(newName: string) {
|
||||
setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
|
||||
async function handleZoneNameChange(newName: string) {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneName: newName
|
||||
};
|
||||
// Call your API to update the zone
|
||||
let response = await zoneCameraUpdate(zonesdata, organization);
|
||||
console.log('response: ', response);
|
||||
if (response.message === "updated successfully") {
|
||||
setZones((prevZones: any[]) =>
|
||||
prevZones.map((zone) =>
|
||||
zone.zoneId === selectedZone.zoneId
|
||||
? { ...zone, zoneName: newName }
|
||||
: zone
|
||||
)
|
||||
);
|
||||
}else{
|
||||
console.log(response?.message);
|
||||
}
|
||||
}
|
||||
|
||||
function handleVectorChange(key: "zoneViewPortTarget" | "zoneViewPortPosition", newValue: [number, number, number]) {
|
||||
setSelectedZone((prev) => ({ ...prev, [key]: newValue }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [selectedZone]);
|
||||
|
||||
return (
|
||||
<div className="zone-properties-container">
|
||||
|
|
|
@ -42,16 +42,19 @@ const Design = () => {
|
|||
const [elementColor, setElementColor] = useState("#6f42c1");
|
||||
const [showColorPicker, setShowColorPicker] = useState(false);
|
||||
const [chartElements, setChartElements] = useState<ChartElement[]>([]);
|
||||
const [selectedElementToStyle, setSelectedElementToStyle] = useState<string | null>(null);
|
||||
const [selectedElementToStyle, setSelectedElementToStyle] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [nameInput, setNameInput] = useState("");
|
||||
const chartRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { selectedChartId, setSelectedChartId, widgets, setWidgets } = useWidgetStore();
|
||||
const { selectedChartId, setSelectedChartId, widgets, setWidgets } =
|
||||
useWidgetStore();
|
||||
|
||||
// Initialize name input and extract elements when selectedChartId changes
|
||||
useEffect(() => {
|
||||
setNameInput(selectedChartId?.header || selectedChartId?.title || "");
|
||||
|
||||
|
||||
if (!chartRef.current) return;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
|
@ -65,13 +68,16 @@ const Design = () => {
|
|||
})
|
||||
.map((el, index) => {
|
||||
const tagName = el.tagName.toLowerCase();
|
||||
const className = typeof el.className === "string" ? el.className : "";
|
||||
const className =
|
||||
typeof el.className === "string" ? el.className : "";
|
||||
const textContent = el.textContent?.trim() || "";
|
||||
|
||||
let selector = tagName;
|
||||
|
||||
if (className && typeof className === "string") {
|
||||
const classList = className.split(/\s+/).filter((c) => c.length > 0);
|
||||
const classList = className
|
||||
.split(/\s+/)
|
||||
.filter((c) => c.length > 0);
|
||||
if (classList.length > 0) {
|
||||
selector += "." + classList.join(".");
|
||||
}
|
||||
|
@ -126,7 +132,13 @@ const Design = () => {
|
|||
|
||||
useEffect(() => {
|
||||
applyStyles();
|
||||
}, [selectedFont, selectedSize, selectedWeight, elementColor, selectedElementToStyle]);
|
||||
}, [
|
||||
selectedFont,
|
||||
selectedSize,
|
||||
selectedWeight,
|
||||
elementColor,
|
||||
selectedElementToStyle,
|
||||
]);
|
||||
|
||||
const handleUpdateWidget = (updatedProperties: Partial<Widget>) => {
|
||||
if (!selectedChartId) return;
|
||||
|
@ -138,7 +150,9 @@ const Design = () => {
|
|||
setSelectedChartId(updatedChartId);
|
||||
|
||||
const updatedWidgets = widgets.map((widget) =>
|
||||
widget.id === selectedChartId.id ? { ...widget, ...updatedProperties } : widget
|
||||
widget.id === selectedChartId.id
|
||||
? { ...widget, ...updatedProperties }
|
||||
: widget
|
||||
);
|
||||
setWidgets(updatedWidgets);
|
||||
};
|
||||
|
@ -146,7 +160,7 @@ const Design = () => {
|
|||
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newName = e.target.value;
|
||||
setNameInput(newName);
|
||||
|
||||
|
||||
if (selectedChartId?.title) {
|
||||
handleUpdateWidget({ title: newName });
|
||||
} else if (selectedChartId?.header) {
|
||||
|
@ -155,12 +169,12 @@ const Design = () => {
|
|||
};
|
||||
|
||||
const defaultChartData = {
|
||||
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [
|
||||
{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: elementColor,
|
||||
borderColor: "#ffffff",
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
|
@ -311,4 +325,4 @@ const Design = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default Design;
|
||||
export default Design;
|
||||
|
|
|
@ -7,13 +7,11 @@ import {
|
|||
VisualizationIcon,
|
||||
} from "../icons/ExportModuleIcons";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import { useSelectedZoneStore } from "../../store/useZoneStore";
|
||||
|
||||
const ModuleToggle: React.FC = () => {
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
|
||||
|
||||
return (
|
||||
<div className="module-toggle-container">
|
||||
<div
|
||||
|
|
|
@ -22,7 +22,7 @@ import { useSelectedZoneStore } from "../../store/useZoneStore";
|
|||
import {
|
||||
useActiveTool,
|
||||
useAddAction,
|
||||
useDeleteModels,
|
||||
useDeleteTool,
|
||||
useDeletePointOrLine,
|
||||
useMovePoint,
|
||||
useRefTextUpdate,
|
||||
|
@ -31,16 +31,18 @@ import {
|
|||
useToggleView,
|
||||
useToolMode,
|
||||
useTransformMode,
|
||||
useActiveSubTool,
|
||||
} from "../../store/store";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import {
|
||||
use3DWidget,
|
||||
useDroppedObjectsStore,
|
||||
useFloatingWidget,
|
||||
} from "../../store/useDroppedObjectsStore";
|
||||
|
||||
const Tools: React.FC = () => {
|
||||
const { templates } = useTemplateStore();
|
||||
const [activeSubTool, setActiveSubTool] = useState("cursor");
|
||||
const { activeSubTool, setActiveSubTool } = useActiveSubTool();
|
||||
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
|
||||
|
@ -52,11 +54,14 @@ const Tools: React.FC = () => {
|
|||
const { addTemplate } = useTemplateStore();
|
||||
const { selectedZone } = useSelectedZoneStore();
|
||||
const { floatingWidget } = useFloatingWidget();
|
||||
|
||||
const { widgets3D } = use3DWidget();
|
||||
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
// wall options
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { setDeleteModels } = useDeleteModels();
|
||||
const { setDeleteTool } = useDeleteTool();
|
||||
const { setAddAction } = useAddAction();
|
||||
const { setSelectedWallItem } = useSelectedWallItem();
|
||||
|
||||
|
@ -84,7 +89,7 @@ const Tools: React.FC = () => {
|
|||
const toggleSwitch = () => {
|
||||
if (toggleThreeD) {
|
||||
setSelectedWallItem(null);
|
||||
setDeleteModels(false);
|
||||
setDeleteTool(false);
|
||||
setAddAction(null);
|
||||
setToggleView(true);
|
||||
// localStorage.setItem("navBarUi", JSON.stringify(!toggleThreeD));
|
||||
|
@ -131,7 +136,7 @@ const Tools: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
setToolMode(null);
|
||||
setDeleteModels(false);
|
||||
setDeleteTool(false);
|
||||
setAddAction(null);
|
||||
setTransformMode(null);
|
||||
setMovePoint(false);
|
||||
|
@ -197,7 +202,7 @@ const Tools: React.FC = () => {
|
|||
if (toggleView) {
|
||||
setDeletePointOrLine(true);
|
||||
} else {
|
||||
setDeleteModels(true);
|
||||
setDeleteTool(true);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -409,10 +414,9 @@ const Tools: React.FC = () => {
|
|||
widgets3D,
|
||||
selectedZone,
|
||||
templates,
|
||||
visualizationSocket
|
||||
})
|
||||
}
|
||||
}
|
||||
visualizationSocket,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SaveTemplateIcon isActive={false} />
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
CleanPannel,
|
||||
EyeIcon,
|
||||
LockIcon,
|
||||
} from "../../icons/RealTimeVisulationIcons";
|
||||
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
|
||||
import { AddIcon } from "../../icons/ExportCommonIcons";
|
||||
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
|
||||
import { useSocketStore } from "../../../store/store";
|
||||
import { clearPanel } from "../../../services/realTimeVisulization/zoneData/clearPanel";
|
||||
import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockPanel";
|
||||
|
||||
// Define the type for `Side`
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||
interface HiddenPanels {
|
||||
[zoneId: string]: Side[];
|
||||
}
|
||||
|
||||
// Define the type for the props passed to the Buttons component
|
||||
interface ButtonsProps {
|
||||
selectedZone: {
|
||||
|
@ -35,7 +40,6 @@ interface ButtonsProps {
|
|||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
|
@ -49,8 +53,8 @@ interface ButtonsProps {
|
|||
}[];
|
||||
}>
|
||||
>;
|
||||
hiddenPanels: Side[]; // Add this prop for hidden panels
|
||||
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
|
||||
hiddenPanels: HiddenPanels; // Updated prop type
|
||||
setHiddenPanels: React.Dispatch<React.SetStateAction<HiddenPanels>>; // Updated prop type
|
||||
}
|
||||
|
||||
const AddButtons: React.FC<ButtonsProps> = ({
|
||||
|
@ -59,13 +63,38 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
setHiddenPanels,
|
||||
hiddenPanels,
|
||||
}) => {
|
||||
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
|
||||
// Local state to track hidden panels
|
||||
// Function to toggle visibility of a panel
|
||||
const toggleVisibility = (side: Side) => {
|
||||
const isHidden = hiddenPanels[selectedZone.zoneId]?.includes(side) ?? false;
|
||||
|
||||
if (isHidden) {
|
||||
// If the panel is already hidden, remove it from the hiddenPanels array for this zone
|
||||
setHiddenPanels((prevHiddenPanels) => ({
|
||||
...prevHiddenPanels,
|
||||
[selectedZone.zoneId]: prevHiddenPanels[selectedZone.zoneId].filter(
|
||||
(panel) => panel !== side
|
||||
),
|
||||
}));
|
||||
} else {
|
||||
// If the panel is visible, add it to the hiddenPanels array for this zone
|
||||
setHiddenPanels((prevHiddenPanels) => ({
|
||||
...prevHiddenPanels,
|
||||
[selectedZone.zoneId]: [
|
||||
...(prevHiddenPanels[selectedZone.zoneId] || []),
|
||||
side,
|
||||
],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Function to toggle lock/unlock a panel
|
||||
const toggleLockPanel = (side: Side) => {
|
||||
const toggleLockPanel = async (side: Side) => {
|
||||
// console.log('side: ', side);
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||
//add api
|
||||
const newLockedPanels = selectedZone.lockedPanels.includes(side)
|
||||
? selectedZone.lockedPanels.filter((panel) => panel !== side)
|
||||
: [...selectedZone.lockedPanels, side];
|
||||
|
@ -75,35 +104,65 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
lockedPanels: newLockedPanels,
|
||||
};
|
||||
|
||||
// Update the selectedZone state
|
||||
setSelectedZone(updatedZone);
|
||||
};
|
||||
|
||||
// Function to toggle visibility of a panel
|
||||
const toggleVisibility = (side: Side) => {
|
||||
const isHidden = hiddenPanels.includes(side);
|
||||
if (isHidden) {
|
||||
// If the panel is already hidden, remove it from the hiddenPanels array
|
||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
||||
} else {
|
||||
// If the panel is visible, add it to the hiddenPanels array
|
||||
setHiddenPanels([...hiddenPanels, side]);
|
||||
let lockedPanel = {
|
||||
organization: organization,
|
||||
lockedPanel: newLockedPanels,
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-panel:locked", lockedPanel);
|
||||
}
|
||||
|
||||
setSelectedZone(updatedZone);
|
||||
// let response = await lockPanel(selectedZone.zoneId, organization, newLockedPanels)
|
||||
// console.log('response: ', response);
|
||||
// if (response.message === 'locked panel updated successfully') {
|
||||
// // Update the selectedZone state
|
||||
// setSelectedZone(updatedZone);
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
// Function to clean all widgets from a panel
|
||||
const cleanPanel = (side: Side) => {
|
||||
const cleanPanel = async (side: Side) => {
|
||||
//add api
|
||||
// console.log('side: ', side);
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||
|
||||
let clearPanel = {
|
||||
organization: organization,
|
||||
panelName: side,
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-panel:clear", clearPanel);
|
||||
}
|
||||
const cleanedWidgets = selectedZone.widgets.filter(
|
||||
(widget) => widget.panel !== side
|
||||
);
|
||||
|
||||
const updatedZone = {
|
||||
...selectedZone,
|
||||
widgets: cleanedWidgets,
|
||||
};
|
||||
|
||||
// Update the selectedZone state
|
||||
// console.log('updatedZone: ', updatedZone);
|
||||
setSelectedZone(updatedZone);
|
||||
|
||||
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
||||
// console.log('response: ', response);
|
||||
// if (response.message === 'PanelWidgets cleared successfully') {
|
||||
|
||||
// const cleanedWidgets = selectedZone.widgets.filter(
|
||||
// (widget) => widget.panel !== side
|
||||
// );
|
||||
// const updatedZone = {
|
||||
// ...selectedZone,
|
||||
// widgets: cleanedWidgets,
|
||||
// };
|
||||
// // Update the selectedZone state
|
||||
// setSelectedZone(updatedZone);
|
||||
// }
|
||||
};
|
||||
|
||||
// Function to handle "+" button click
|
||||
|
@ -129,23 +188,23 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
let deletePanel = {
|
||||
organization: organization,
|
||||
panelName: side,
|
||||
zoneId: selectedZone.zoneId
|
||||
}
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-panel:delete", deletePanel)
|
||||
visualizationSocket.emit("v2:viz-panel:delete", deletePanel);
|
||||
}
|
||||
setSelectedZone(updatedZone);
|
||||
|
||||
// API call to delete the panel
|
||||
// try {
|
||||
// const response = await deletePanelApi(selectedZone.zoneId, side, organization);
|
||||
//
|
||||
//
|
||||
// if (response.message === "Panel deleted successfully") {
|
||||
// } else {
|
||||
//
|
||||
//
|
||||
// }
|
||||
// } catch (error) {
|
||||
//
|
||||
//
|
||||
// }
|
||||
} else {
|
||||
// Panel does not exist: Create panel
|
||||
|
@ -167,27 +226,23 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
let addPanel = {
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
panelOrder: newActiveSides
|
||||
}
|
||||
panelOrder: newActiveSides,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-panel:add", addPanel)
|
||||
visualizationSocket.emit("v2:viz-panel:add", addPanel);
|
||||
}
|
||||
setSelectedZone(updatedZone);
|
||||
// API call to create panels
|
||||
// const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
|
||||
//
|
||||
//
|
||||
|
||||
// if (response.message === "Panels created successfully") {
|
||||
// } else {
|
||||
//
|
||||
//
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
|
@ -214,15 +269,24 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
<div className="extra-Bs">
|
||||
{/* Hide Panel */}
|
||||
<div
|
||||
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
|
||||
}`}
|
||||
className={`icon ${
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
title={
|
||||
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||
? "Show Panel"
|
||||
: "Hide Panel"
|
||||
}
|
||||
onClick={() => toggleVisibility(side)}
|
||||
>
|
||||
<EyeIcon
|
||||
fill={hiddenPanels.includes(side) ? "var(--primary-color)" : "var(--text-color)"}
|
||||
fill={
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||
? "var(--primary-color)"
|
||||
: "var(--text-color)"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
|
||||
|
||||
const{setSelectedChartId}=useWidgetStore()
|
||||
|
||||
// Function to calculate overflow state
|
||||
|
@ -156,6 +157,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
let response = await getSelect2dZoneData(zoneId, organization);
|
||||
|
||||
|
||||
let res = await getFloatingZoneData(zoneId, organization);
|
||||
|
||||
setFloatingWidget(res);
|
||||
|
@ -178,7 +180,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
zoneViewPortPosition: response.viewPortposition || {},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData
|
|||
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||
import { useSocketStore } from "../../../store/store";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import OuterClick from "../../../utils/outerClick";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -87,7 +88,15 @@ export const DraggableWidget = ({
|
|||
|
||||
const chartWidget = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||
OuterClick({
|
||||
contextClassName: [
|
||||
"chart-container",
|
||||
"floating",
|
||||
"sidebar-right-wrapper",
|
||||
"card",
|
||||
],
|
||||
setMenuVisible: () => setSelectedChartId(null),
|
||||
});
|
||||
|
||||
const deleteSelectedChart = async () => {
|
||||
try {
|
||||
|
@ -96,16 +105,16 @@ export const DraggableWidget = ({
|
|||
let deleteWidget = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
widgetID: widget.id,
|
||||
organization: organization
|
||||
}
|
||||
console.log('deleteWidget: ', deleteWidget);
|
||||
organization: organization,
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget)
|
||||
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget);
|
||||
}
|
||||
const updatedWidgets = selectedZone.widgets.filter(
|
||||
(w: Widget) => w.id !== widget.id
|
||||
);
|
||||
console.log('updatedWidgets: ', updatedWidgets);
|
||||
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: updatedWidgets,
|
||||
|
@ -168,10 +177,10 @@ export const DraggableWidget = ({
|
|||
let duplicateWidget = {
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
widget: duplicatedWidget
|
||||
}
|
||||
widget: duplicatedWidget,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-widget:add", duplicateWidget)
|
||||
visualizationSocket.emit("v2:viz-widget:add", duplicateWidget);
|
||||
}
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
|
@ -245,21 +254,62 @@ export const DraggableWidget = ({
|
|||
// });
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
|
||||
|
||||
const [canvasDimensions, setCanvasDimensions] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
// Track canvas dimensions
|
||||
|
||||
// Current: Two identical useEffect hooks for canvas dimensions
|
||||
// Remove the duplicate and keep only one
|
||||
useEffect(() => {
|
||||
const canvas = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvas) return;
|
||||
|
||||
const updateCanvasDimensions = () => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
setCanvasDimensions({
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
};
|
||||
|
||||
updateCanvasDimensions();
|
||||
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
||||
resizeObserver.observe(canvas);
|
||||
|
||||
return () => resizeObserver.unobserve(canvas);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>
|
||||
{`
|
||||
:root {
|
||||
--realTimeViz-container-width: ${canvasDimensions.width}px;
|
||||
--realTimeViz-container-height: ${canvasDimensions.height}px;
|
||||
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<div
|
||||
draggable
|
||||
key={widget.id}
|
||||
className={`chart-container ${(selectedChartId?.id === widget.id) && !isPlaying && "activeChart"
|
||||
}`}
|
||||
className={`chart-container ${
|
||||
selectedChartId?.id === widget.id && !isPlaying && "activeChart"
|
||||
}`}
|
||||
onPointerDown={handlePointerDown}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
style={{
|
||||
pointerEvents: isPanelHidden ? "none" : "auto",
|
||||
width: ["top", "bottom"].includes(widget.panel)
|
||||
? `calc(${canvasDimensions.width}px / 6)`
|
||||
: undefined,
|
||||
height: ["left", "right"].includes(widget.panel)
|
||||
? `calc(${canvasDimensions.height - 10}px / 4)`
|
||||
: undefined,
|
||||
}}
|
||||
ref={chartWidget}
|
||||
onClick={() => setSelectedChartId(widget)}
|
||||
|
@ -273,8 +323,9 @@ export const DraggableWidget = ({
|
|||
{openKebabId === widget.id && (
|
||||
<div className="kebab-options" ref={widgetRef}>
|
||||
<div
|
||||
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
className={`edit btn ${
|
||||
isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
|
||||
>
|
||||
<div className="icon">
|
||||
|
@ -348,3 +399,5 @@ export const DraggableWidget = ({
|
|||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// by using canvasDimensions.height canvasDimensions.width dynamically div value insted of static 6 and 4 calculate according to canvasDimensions.width canvasDimensions.height
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { useThree } from "@react-three/fiber";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store";
|
||||
import {
|
||||
useAsset3dWidget,
|
||||
useSocketStore,
|
||||
useWidgetSubOption,
|
||||
} from "../../../store/store";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { ThreeState } from "../../../types/world/worldTypes";
|
||||
import * as THREE from "three";
|
||||
|
@ -13,11 +17,19 @@ import { generateUniqueId } from "../../../functions/generateUniqueId";
|
|||
import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget";
|
||||
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
|
||||
import { use3DWidget } from "../../../store/useDroppedObjectsStore";
|
||||
import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore";
|
||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||
import EditWidgetOption from "../menu/EditWidgetOption";
|
||||
import {
|
||||
useEditWidgetOptionsStore,
|
||||
useLeftData,
|
||||
useRightClickSelected,
|
||||
useRightSelected,
|
||||
useTopData,
|
||||
useZoneWidgetStore,
|
||||
} from "../../../store/useZone3DWidgetStore";
|
||||
import { delete3dWidgetApi } from "../../../services/realTimeVisulization/zoneData/delete3dWidget";
|
||||
import { update3dWidget, update3dWidgetRotation } from "../../../services/realTimeVisulization/zoneData/update3dWidget";
|
||||
import {
|
||||
update3dWidget,
|
||||
update3dWidgetRotation,
|
||||
} from "../../../services/realTimeVisulization/zoneData/update3dWidget";
|
||||
type WidgetData = {
|
||||
id: string;
|
||||
type: string;
|
||||
|
@ -26,7 +38,6 @@ type WidgetData = {
|
|||
tempPosition?: [number, number, number];
|
||||
};
|
||||
|
||||
|
||||
export default function Dropped3dWidgets() {
|
||||
const { widgetSelect } = useAsset3dWidget();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
@ -36,21 +47,18 @@ export default function Dropped3dWidgets() {
|
|||
const { top, setTop } = useTopData();
|
||||
const { left, setLeft } = useLeftData();
|
||||
const { rightSelect, setRightSelect } = useRightSelected();
|
||||
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore()
|
||||
const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore();
|
||||
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
|
||||
const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation, tempWidget, tempWidgetPosition } = useZoneWidgetStore();
|
||||
const { setWidgets3D } = use3DWidget();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
||||
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move
|
||||
const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move
|
||||
const planeIntersect = useRef(new THREE.Vector3());
|
||||
// const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
// const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
|
||||
// const planeIntersect = useRef(new THREE.Vector3());
|
||||
const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]);
|
||||
const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||
|
||||
|
||||
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (!selectedZone.zoneId) return;
|
||||
|
@ -59,8 +67,11 @@ export default function Dropped3dWidgets() {
|
|||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
async function get3dWidgetData() {
|
||||
const result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
|
||||
console.log('result: ', result);
|
||||
const result = await get3dWidgetZoneData(
|
||||
selectedZone.zoneId,
|
||||
organization
|
||||
);
|
||||
|
||||
setWidgets3D(result);
|
||||
|
||||
const formattedWidgets = result.map((widget: WidgetData) => ({
|
||||
|
@ -70,37 +81,50 @@ export default function Dropped3dWidgets() {
|
|||
rotation: widget.rotation || [0, 0, 0],
|
||||
}));
|
||||
|
||||
|
||||
setZoneWidgetData(selectedZone.zoneId, formattedWidgets);
|
||||
}
|
||||
|
||||
get3dWidgetData();
|
||||
}, [selectedZone.zoneId, activeModule]);
|
||||
|
||||
const createdWidgetRef = useRef<WidgetData | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (widgetSubOption === "Floating" || widgetSubOption === "2D") return;
|
||||
if (selectedZone.zoneName === "") return;
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
|
||||
const onDrop = async (event: DragEvent) => {
|
||||
if (!canvasElement) return;
|
||||
|
||||
const hasEntered = { current: false };
|
||||
|
||||
const handleDragEnter = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (hasEntered.current || !widgetSelect.startsWith("ui")) return;
|
||||
hasEntered.current = true;
|
||||
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
if (!widgetSelect.startsWith("ui")) return;
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
const rect = canvasElement.getBoundingClientRect();
|
||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children, true)
|
||||
.filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const { x, y, z } = intersects[0].point;
|
||||
|
@ -111,27 +135,100 @@ export default function Dropped3dWidgets() {
|
|||
rotation: [0, 0, 0],
|
||||
};
|
||||
|
||||
const add3dWidget = {
|
||||
organization: organization,
|
||||
widget: newWidget,
|
||||
zoneId: selectedZone.zoneId
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget);
|
||||
}
|
||||
|
||||
addWidget(selectedZone.zoneId, newWidget);
|
||||
createdWidgetRef.current = newWidget;
|
||||
tempWidget(selectedZone.zoneId, newWidget); // temp add in UI
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragOver = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.dataTransfer!.dropEffect = "move"; // ✅ Add this line
|
||||
const widget = createdWidgetRef.current;
|
||||
if (!widget) return;
|
||||
|
||||
const rect = canvasElement.getBoundingClientRect();
|
||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children, true)
|
||||
.filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
// Update widget's position in memory
|
||||
if (intersects.length > 0) {
|
||||
const { x, y, z } = intersects[0].point;
|
||||
tempWidgetPosition(selectedZone.zoneId, widget.id, [x, y, z]);
|
||||
widget.position = [x, y, z];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
console.log("onDrop called. hasEntered: ", hasEntered.current);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
hasEntered.current = false;
|
||||
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
const newWidget = createdWidgetRef.current;
|
||||
if (!newWidget || !widgetSelect.startsWith("ui")) return;
|
||||
|
||||
// ✅ Manual removal of the temp widget (same ID)
|
||||
const prevWidgets = useZoneWidgetStore.getState().zoneWidgetData[selectedZone.zoneId] || [];
|
||||
const cleanedWidgets = prevWidgets.filter(w => w.id !== newWidget.id);
|
||||
useZoneWidgetStore.setState((state) => ({
|
||||
zoneWidgetData: {
|
||||
...state.zoneWidgetData,
|
||||
[selectedZone.zoneId]: cleanedWidgets,
|
||||
},
|
||||
}));
|
||||
|
||||
// ✅ Now re-add it as final
|
||||
addWidget(selectedZone.zoneId, newWidget);
|
||||
|
||||
const add3dWidget = {
|
||||
organization,
|
||||
widget: newWidget,
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
let pointerDivs = document.getElementsByClassName("pointer-none");
|
||||
Array.from(pointerDivs).forEach((el) => {
|
||||
el.classList.remove("pointer-none");
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
createdWidgetRef.current = null;
|
||||
};
|
||||
|
||||
canvasElement.addEventListener("dragenter", handleDragEnter);
|
||||
canvasElement.addEventListener("dragover", handleDragOver);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("dragenter", handleDragEnter);
|
||||
canvasElement.removeEventListener("dragover", handleDragOver);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
};
|
||||
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
|
||||
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption, camera,]);
|
||||
|
||||
|
||||
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
|
||||
|
||||
useEffect(() => {
|
||||
if (!rightClickSelected) return;
|
||||
|
@ -140,7 +237,9 @@ export default function Dropped3dWidgets() {
|
|||
|
||||
if (rightSelect === "Duplicate") {
|
||||
async function duplicateWidget() {
|
||||
const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected);
|
||||
const widgetToDuplicate = activeZoneWidgets.find(
|
||||
(w: WidgetData) => w.id === rightClickSelected
|
||||
);
|
||||
if (!widgetToDuplicate) return;
|
||||
const newWidget: WidgetData = {
|
||||
id: generateUniqueId(),
|
||||
|
@ -155,19 +254,19 @@ export default function Dropped3dWidgets() {
|
|||
const adding3dWidget = {
|
||||
organization: organization,
|
||||
widget: newWidget,
|
||||
zoneId: selectedZone.zoneId
|
||||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:add", adding3dWidget);
|
||||
}
|
||||
// let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
|
||||
// console.log('response: ', response);
|
||||
//
|
||||
|
||||
addWidget(selectedZone.zoneId, newWidget);
|
||||
setRightSelect(null);
|
||||
setRightClickSelected(null);
|
||||
}
|
||||
duplicateWidget()
|
||||
duplicateWidget();
|
||||
}
|
||||
|
||||
if (rightSelect === "Delete") {
|
||||
|
@ -179,7 +278,6 @@ export default function Dropped3dWidgets() {
|
|||
zoneId: selectedZone.zoneId,
|
||||
};
|
||||
|
||||
console.log('deleteWidget: ', deleteWidget);
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget);
|
||||
}
|
||||
|
@ -187,10 +285,11 @@ export default function Dropped3dWidgets() {
|
|||
// const response = await delete3dWidgetApi(selectedZone.zoneId, organization, rightClickSelected);
|
||||
setZoneWidgetData(
|
||||
selectedZone.zoneId,
|
||||
activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected)
|
||||
activeZoneWidgets.filter(
|
||||
(w: WidgetData) => w.id !== rightClickSelected
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error deleting widget:", error);
|
||||
} finally {
|
||||
setRightClickSelected(null);
|
||||
setRightSelect(null);
|
||||
|
@ -202,7 +301,6 @@ export default function Dropped3dWidgets() {
|
|||
}, [rightSelect, rightClickSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const handleMouseDown = (event: MouseEvent) => {
|
||||
|
@ -211,13 +309,18 @@ export default function Dropped3dWidgets() {
|
|||
if (rightSelect === "RotateX" || rightSelect === "RotateY") {
|
||||
mouseStartRef.current = { x: event.clientX, y: event.clientY };
|
||||
|
||||
const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) =>
|
||||
zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected)
|
||||
const selectedZone = Object.keys(zoneWidgetData).find(
|
||||
(zoneId: string) =>
|
||||
zoneWidgetData[zoneId].some(
|
||||
(widget: WidgetData) => widget.id === rightClickSelected
|
||||
)
|
||||
);
|
||||
|
||||
if (!selectedZone) return;
|
||||
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected);
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find(
|
||||
(widget: WidgetData) => widget.id === rightClickSelected
|
||||
);
|
||||
if (selectedWidget) {
|
||||
rotationStartRef.current = selectedWidget.rotation || [0, 0, 0];
|
||||
}
|
||||
|
@ -227,11 +330,15 @@ export default function Dropped3dWidgets() {
|
|||
const handleMouseMove = (event: MouseEvent) => {
|
||||
if (!rightClickSelected || !rightSelect) return;
|
||||
const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) =>
|
||||
zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected)
|
||||
zoneWidgetData[zoneId].some(
|
||||
(widget: WidgetData) => widget.id === rightClickSelected
|
||||
)
|
||||
);
|
||||
if (!selectedZone) return;
|
||||
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected);
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find(
|
||||
(widget: WidgetData) => widget.id === rightClickSelected
|
||||
);
|
||||
if (!selectedWidget) return;
|
||||
|
||||
const rect = gl.domElement.getBoundingClientRect();
|
||||
|
@ -240,22 +347,29 @@ export default function Dropped3dWidgets() {
|
|||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) {
|
||||
if (
|
||||
rightSelect === "Horizontal Move" &&
|
||||
raycaster.ray.intersectPlane(plane.current, planeIntersect.current)
|
||||
) {
|
||||
const newPosition: [number, number, number] = [
|
||||
planeIntersect.current.x,
|
||||
selectedWidget.position[1],
|
||||
planeIntersect.current.z
|
||||
planeIntersect.current.z,
|
||||
];
|
||||
updateWidgetPosition(selectedZone, rightClickSelected, newPosition);
|
||||
|
||||
}
|
||||
|
||||
if (rightSelect === "Vertical Move") {
|
||||
if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) {
|
||||
if (
|
||||
raycaster.ray.intersectPlane(
|
||||
verticalPlane.current,
|
||||
planeIntersect.current
|
||||
)
|
||||
) {
|
||||
updateWidgetPosition(selectedZone, rightClickSelected, [
|
||||
selectedWidget.position[0],
|
||||
planeIntersect.current.y,
|
||||
selectedWidget.position[2]
|
||||
selectedWidget.position[2],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +380,7 @@ export default function Dropped3dWidgets() {
|
|||
const newRotation: [number, number, number] = [
|
||||
rotationStartRef.current[0] + deltaX * rotationSpeed,
|
||||
rotationStartRef.current[1],
|
||||
rotationStartRef.current[2]
|
||||
rotationStartRef.current[2],
|
||||
];
|
||||
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
|
||||
}
|
||||
|
@ -277,7 +391,7 @@ export default function Dropped3dWidgets() {
|
|||
const newRotation: [number, number, number] = [
|
||||
rotationStartRef.current[0],
|
||||
rotationStartRef.current[1] + deltaY * rotationSpeed,
|
||||
rotationStartRef.current[2]
|
||||
rotationStartRef.current[2],
|
||||
];
|
||||
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
|
||||
}
|
||||
|
@ -288,36 +402,42 @@ export default function Dropped3dWidgets() {
|
|||
const newRotation: [number, number, number] = [
|
||||
currentRotation[0],
|
||||
currentRotation[1],
|
||||
currentRotation[2] + deltaX * rotationSpeed
|
||||
currentRotation[2] + deltaX * rotationSpeed,
|
||||
];
|
||||
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (!rightClickSelected || !rightSelect) return;
|
||||
const selectedZone = Object.keys(zoneWidgetData).find(zoneId =>
|
||||
zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected)
|
||||
const selectedZone = Object.keys(zoneWidgetData).find((zoneId) =>
|
||||
zoneWidgetData[zoneId].some(
|
||||
(widget) => widget.id === rightClickSelected
|
||||
)
|
||||
);
|
||||
if (!selectedZone) return;
|
||||
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected);
|
||||
const selectedWidget = zoneWidgetData[selectedZone].find(
|
||||
(widget) => widget.id === rightClickSelected
|
||||
);
|
||||
if (!selectedWidget) return;
|
||||
|
||||
|
||||
|
||||
// Format values to 2 decimal places
|
||||
const formatValues = (vals: number[]) => vals.map(val => parseFloat(val.toFixed(2)));
|
||||
|
||||
if (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move") {
|
||||
console.log(`${rightSelect} Completed - Full Position:`, formatValues(selectedWidget.position));
|
||||
let lastPosition = formatValues(selectedWidget.position) as [number, number, number];
|
||||
const formatValues = (vals: number[]) =>
|
||||
vals.map((val) => parseFloat(val.toFixed(2)));
|
||||
if (
|
||||
rightSelect === "Horizontal Move" ||
|
||||
rightSelect === "Vertical Move"
|
||||
) {
|
||||
let lastPosition = formatValues(selectedWidget.position) as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
// (async () => {
|
||||
// let response = await update3dWidget(selectedZone, organization, rightClickSelected, lastPosition);
|
||||
// console.log('response: ', response);
|
||||
//
|
||||
// if (response) {
|
||||
// console.log("Widget position updated in API:", response);
|
||||
//
|
||||
// }
|
||||
// })();
|
||||
let updatingPosition = {
|
||||
|
@ -325,21 +445,22 @@ export default function Dropped3dWidgets() {
|
|||
zoneId: selectedZone,
|
||||
id: rightClickSelected,
|
||||
position: lastPosition,
|
||||
}
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingPosition);
|
||||
visualizationSocket.emit(
|
||||
"v2:viz-3D-widget:modifyPositionRotation",
|
||||
updatingPosition
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
else if (rightSelect.includes("Rotate")) {
|
||||
} else if (rightSelect.includes("Rotate")) {
|
||||
const rotation = selectedWidget.rotation || [0, 0, 0];
|
||||
console.log(`${rightSelect} Completed - Full Rotation:`, formatValues(rotation));
|
||||
|
||||
let lastRotation = formatValues(rotation) as [number, number, number];
|
||||
// (async () => {
|
||||
// let response = await update3dWidgetRotation(selectedZone, organization, rightClickSelected, lastRotation);
|
||||
// console.log('response: ', response);
|
||||
//
|
||||
// if (response) {
|
||||
// console.log("Widget position updated in API:", response);
|
||||
//
|
||||
// }
|
||||
// })();
|
||||
let updatingRotation = {
|
||||
|
@ -347,9 +468,12 @@ export default function Dropped3dWidgets() {
|
|||
zoneId: selectedZone,
|
||||
id: rightClickSelected,
|
||||
rotation: lastRotation,
|
||||
}
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingRotation);
|
||||
visualizationSocket.emit(
|
||||
"v2:viz-3D-widget:modifyPositionRotation",
|
||||
updatingRotation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,69 +496,73 @@ export default function Dropped3dWidgets() {
|
|||
|
||||
return (
|
||||
<>
|
||||
{activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => {
|
||||
const handleRightClick = (event: React.MouseEvent, id: string) => {
|
||||
event.preventDefault();
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) throw new Error("Canvas element not found");
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
setEditWidgetOptions(true);
|
||||
setRightClickSelected(id);
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
};
|
||||
{activeZoneWidgets.map(
|
||||
({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => {
|
||||
const handleRightClick = (event: React.MouseEvent, id: string) => {
|
||||
event.preventDefault();
|
||||
const canvasElement = document.getElementById(
|
||||
"real-time-vis-canvas"
|
||||
);
|
||||
if (!canvasElement) throw new Error("Canvas element not found");
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
setEditWidgetOptions(true);
|
||||
setRightClickSelected(id);
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case "ui-Widget 1":
|
||||
return (
|
||||
<ProductionCapacity
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 2":
|
||||
return (
|
||||
<ReturnOfInvestment
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 3":
|
||||
return (
|
||||
<StateWorking
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 4":
|
||||
return (
|
||||
<Throughput
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
switch (type) {
|
||||
case "ui-Widget 1":
|
||||
return (
|
||||
<ProductionCapacity
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 2":
|
||||
return (
|
||||
<ReturnOfInvestment
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 3":
|
||||
return (
|
||||
<StateWorking
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
case "ui-Widget 4":
|
||||
return (
|
||||
<Throughput
|
||||
key={id}
|
||||
id={id}
|
||||
type={type}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
onContextMenu={(e) => handleRightClick(e, id)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
})}
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ const DroppedObjects: React.FC = () => {
|
|||
);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
|
||||
const [activeEdges, setActiveEdges] = useState<{
|
||||
vertical: "top" | "bottom";
|
||||
horizontal: "left" | "right";
|
||||
|
@ -84,7 +85,6 @@ const DroppedObjects: React.FC = () => {
|
|||
// });
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
// Clean up animation frame on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -95,7 +95,10 @@ const DroppedObjects: React.FC = () => {
|
|||
}, []);
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (kebabRef.current && !kebabRef.current.contains(event.target as Node)) {
|
||||
if (
|
||||
kebabRef.current &&
|
||||
!kebabRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setOpenKebabId(null);
|
||||
}
|
||||
};
|
||||
|
@ -113,7 +116,6 @@ const DroppedObjects: React.FC = () => {
|
|||
if (zoneEntries.length === 0) return null;
|
||||
const [zoneName, zone] = zoneEntries[0];
|
||||
|
||||
|
||||
function handleDuplicate(zoneName: string, index: number) {
|
||||
setOpenKebabId(null);
|
||||
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
|
||||
|
@ -124,15 +126,14 @@ const DroppedObjects: React.FC = () => {
|
|||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
|
||||
let deleteFloatingWidget = {
|
||||
floatWidgetID: id,
|
||||
organization: organization,
|
||||
zoneId: zone.zoneId
|
||||
}
|
||||
zoneId: zone.zoneId,
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget)
|
||||
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget);
|
||||
}
|
||||
deleteObject(zoneName, id);
|
||||
|
||||
|
@ -142,13 +143,14 @@ const DroppedObjects: React.FC = () => {
|
|||
// if (res.message === "FloatingWidget deleted successfully") {
|
||||
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
||||
if ((event.target as HTMLElement).closest(".kebab-options") || (event.target as HTMLElement).closest(".kebab")) {
|
||||
if (
|
||||
(event.target as HTMLElement).closest(".kebab-options") ||
|
||||
(event.target as HTMLElement).closest(".kebab")
|
||||
) {
|
||||
return; // Prevent dragging when clicking on the kebab menu or its options
|
||||
}
|
||||
const obj = zone.objects[index];
|
||||
|
@ -449,7 +451,6 @@ const DroppedObjects: React.FC = () => {
|
|||
// position: boundedPosition,
|
||||
// });
|
||||
|
||||
|
||||
let updateFloatingWidget = {
|
||||
organization: organization,
|
||||
widget: {
|
||||
|
@ -457,15 +458,14 @@ const DroppedObjects: React.FC = () => {
|
|||
position: boundedPosition,
|
||||
},
|
||||
index: draggingIndex.index,
|
||||
zoneId: zone.zoneId
|
||||
}
|
||||
zoneId: zone.zoneId,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget)
|
||||
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget);
|
||||
}
|
||||
|
||||
// if (response.message === "Widget updated successfully") {
|
||||
|
||||
console.log('boundedPosition: ', boundedPosition);
|
||||
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
||||
// }
|
||||
|
||||
|
@ -479,7 +479,6 @@ const DroppedObjects: React.FC = () => {
|
|||
// animationRef.current = null;
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
// Clean up regardless of success or failure
|
||||
setDraggingIndex(null);
|
||||
|
@ -500,101 +499,124 @@ const DroppedObjects: React.FC = () => {
|
|||
setOpenKebabId((prevId) => (prevId === id ? null : id));
|
||||
};
|
||||
|
||||
const containerHeight = getComputedStyle(
|
||||
document.documentElement
|
||||
).getPropertyValue("--realTimeViz-container-height");
|
||||
const containerWidth = getComputedStyle(
|
||||
document.documentElement
|
||||
).getPropertyValue("--realTimeViz-container-width");
|
||||
|
||||
const heightMultiplier = parseFloat(containerHeight) * 0.14;
|
||||
|
||||
const widthMultiplier = parseFloat(containerWidth) * 0.13;
|
||||
|
||||
return (
|
||||
<div
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
className="floating-wrapper"
|
||||
>
|
||||
{zone.objects.map((obj, index) => (
|
||||
<div
|
||||
key={`${zoneName}-${index}`}
|
||||
className={`${obj.className} ${selectedChartId?.id === obj.id && "activeChart"}`}
|
||||
ref={chartWidget}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top:
|
||||
typeof obj.position.top === "number"
|
||||
? `calc(${obj.position.top}px + ${isPlaying && selectedZone.activeSides.includes("top")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
left:
|
||||
typeof obj.position.left === "number"
|
||||
? `calc(${obj.position.left}px + ${isPlaying && selectedZone.activeSides.includes("left")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
right:
|
||||
typeof obj.position.right === "number"
|
||||
? `calc(${obj.position.right}px + ${isPlaying && selectedZone.activeSides.includes("right")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
bottom:
|
||||
typeof obj.position.bottom === "number"
|
||||
? `calc(${obj.position.bottom}px + ${isPlaying && selectedZone.activeSides.includes("bottom")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
}}
|
||||
onPointerDown={(event) => {
|
||||
setSelectedChartId(obj);
|
||||
handlePointerDown(event, index);
|
||||
}}
|
||||
>
|
||||
{obj.className === "floating total-card" ? (
|
||||
<>
|
||||
<TotalCardComponent object={obj} />
|
||||
</>
|
||||
) : obj.className === "warehouseThroughput floating" ? (
|
||||
<>
|
||||
<WarehouseThroughputComponent object={obj} />
|
||||
</>
|
||||
) : obj.className === "fleetEfficiency floating" ? (
|
||||
<>
|
||||
<FleetEfficiencyComponent object={obj} />
|
||||
</>
|
||||
) : null}
|
||||
{zone.objects.map((obj, index) => {
|
||||
const topPosition =
|
||||
typeof obj.position.top === "number"
|
||||
? `calc(${obj.position.top}px + ${isPlaying && selectedZone.activeSides.includes("top")
|
||||
? `${heightMultiplier - 55}px`
|
||||
: "0px"
|
||||
})`
|
||||
: "auto";
|
||||
|
||||
const leftPosition =
|
||||
typeof obj.position.left === "number"
|
||||
? `calc(${obj.position.left}px + ${isPlaying && selectedZone.activeSides.includes("left")
|
||||
? `${widthMultiplier - 100}px`
|
||||
: "0px"
|
||||
})`
|
||||
: "auto";
|
||||
|
||||
const rightPosition =
|
||||
typeof obj.position.right === "number"
|
||||
? `calc(${obj.position.right}px + ${isPlaying && selectedZone.activeSides.includes("right")
|
||||
? `${widthMultiplier - 100}px`
|
||||
: "0px"
|
||||
})`
|
||||
: "auto";
|
||||
|
||||
const bottomPosition =
|
||||
typeof obj.position.bottom === "number"
|
||||
? `calc(${obj.position.bottom}px + ${isPlaying && selectedZone.activeSides.includes("bottom")
|
||||
? `${heightMultiplier - 55}px`
|
||||
: "0px"
|
||||
})`
|
||||
: "auto";
|
||||
|
||||
return (
|
||||
<div
|
||||
className="icon kebab"
|
||||
ref={kebabRef}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleKebabClick(obj.id, event)
|
||||
key={`${zoneName}-${index}`}
|
||||
className={`${obj.className} ${selectedChartId?.id === obj.id && "activeChart"
|
||||
}`}
|
||||
ref={chartWidget}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: topPosition,
|
||||
left: leftPosition,
|
||||
right: rightPosition,
|
||||
bottom: bottomPosition,
|
||||
}}
|
||||
onPointerDown={(event) => {
|
||||
setSelectedChartId(obj);
|
||||
handlePointerDown(event, index);
|
||||
}}
|
||||
>
|
||||
<KebabIcon />
|
||||
</div>
|
||||
{openKebabId === obj.id && (
|
||||
<div className="kebab-options" ref={kebabRef}>
|
||||
<div className="dublicate btn" onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDuplicate(zoneName, index); // Call the duplicate handler
|
||||
}}>
|
||||
<div className="icon">
|
||||
<DublicateIcon />
|
||||
</div>
|
||||
<div className="label">Duplicate</div>
|
||||
</div>
|
||||
<div className="edit btn" onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDelete(zoneName, obj.id); // Call the delete handler
|
||||
}}>
|
||||
<div className="icon">
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
<div className="label">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{obj.className === "floating total-card" ? (
|
||||
<TotalCardComponent object={obj} />
|
||||
) : obj.className === "warehouseThroughput floating" ? (
|
||||
<WarehouseThroughputComponent object={obj} />
|
||||
) : obj.className === "fleetEfficiency floating" ? (
|
||||
<FleetEfficiencyComponent object={obj} />
|
||||
) : null}
|
||||
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className="icon kebab"
|
||||
ref={kebabRef}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleKebabClick(obj.id, event);
|
||||
}}
|
||||
>
|
||||
<KebabIcon />
|
||||
</div>
|
||||
|
||||
{openKebabId === obj.id && (
|
||||
<div className="kebab-options" ref={kebabRef}>
|
||||
<div
|
||||
className="dublicate btn"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDuplicate(zoneName, index); // Call the duplicate handler
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
<DublicateIcon />
|
||||
</div>
|
||||
<div className="label">Duplicate</div>
|
||||
</div>
|
||||
<div
|
||||
className="edit btn"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDelete(zoneName, obj.id); // Call the delete handler
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
<div className="label">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Render DistanceLines component during drag */}
|
||||
{isPlaying === false &&
|
||||
|
@ -630,5 +652,3 @@ const DroppedObjects: React.FC = () => {
|
|||
};
|
||||
|
||||
export default DroppedObjects;
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ interface PanelProps {
|
|||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
|
@ -33,7 +32,6 @@ interface PanelProps {
|
|||
zoneName: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
|
@ -41,8 +39,8 @@ interface PanelProps {
|
|||
widgets: Widget[];
|
||||
}>
|
||||
>;
|
||||
hiddenPanels: string[];
|
||||
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
|
||||
hiddenPanels: any;
|
||||
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
||||
}
|
||||
|
||||
const generateUniqueId = () =>
|
||||
|
@ -60,10 +58,40 @@ const Panel: React.FC<PanelProps> = ({
|
|||
[side in Side]?: { width: number; height: number };
|
||||
}>({});
|
||||
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const [canvasDimensions, setCanvasDimensions] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
// Track canvas dimensions
|
||||
useEffect(() => {
|
||||
const canvas = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvas) return;
|
||||
|
||||
const updateCanvasDimensions = () => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
setCanvasDimensions({
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
};
|
||||
|
||||
updateCanvasDimensions();
|
||||
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
||||
resizeObserver.observe(canvas);
|
||||
|
||||
return () => resizeObserver.unobserve(canvas);
|
||||
}, []);
|
||||
|
||||
// Calculate panel size
|
||||
const panelSize = Math.max(
|
||||
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
||||
170 // Min 170px
|
||||
);
|
||||
|
||||
// Define getPanelStyle
|
||||
const getPanelStyle = useMemo(
|
||||
() => (side: Side) => {
|
||||
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
||||
|
@ -72,16 +100,17 @@ const Panel: React.FC<PanelProps> = ({
|
|||
const rightActive = previousPanels.includes("right");
|
||||
const topActive = previousPanels.includes("top");
|
||||
const bottomActive = previousPanels.includes("bottom");
|
||||
const panelSize = isPlaying ? 300 : 210;
|
||||
|
||||
switch (side) {
|
||||
case "top":
|
||||
case "bottom":
|
||||
return {
|
||||
minWidth: "170px",
|
||||
width: `calc(100% - ${
|
||||
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||
}px)`,
|
||||
height: `${panelSize - 2}px`,
|
||||
minHeight: "170px",
|
||||
height: `${panelSize}px`,
|
||||
left: leftActive ? `${panelSize}px` : "0",
|
||||
right: rightActive ? `${panelSize}px` : "0",
|
||||
[side]: "0",
|
||||
|
@ -89,7 +118,9 @@ const Panel: React.FC<PanelProps> = ({
|
|||
case "left":
|
||||
case "right":
|
||||
return {
|
||||
width: `${panelSize - 2}px`,
|
||||
minWidth: "170px",
|
||||
width: `${panelSize}px`,
|
||||
minHeight: "170px",
|
||||
height: `calc(100% - ${
|
||||
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||
}px)`,
|
||||
|
@ -101,83 +132,79 @@ const Panel: React.FC<PanelProps> = ({
|
|||
return {};
|
||||
}
|
||||
},
|
||||
[selectedZone.panelOrder, isPlaying]
|
||||
[selectedZone.panelOrder, panelSize]
|
||||
);
|
||||
|
||||
// Handle drop event
|
||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||
e.preventDefault();
|
||||
const { draggedAsset } = useWidgetStore.getState();
|
||||
if (!draggedAsset) return;
|
||||
if (isPanelLocked(panel)) return;
|
||||
if (
|
||||
!draggedAsset ||
|
||||
isPanelLocked(panel) ||
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(panel)
|
||||
)
|
||||
return;
|
||||
|
||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||
const maxCapacity = calculatePanelCapacity(panel);
|
||||
|
||||
if (currentWidgetsCount >= maxCapacity) return;
|
||||
addWidgetToPanel(draggedAsset, panel);
|
||||
if (currentWidgetsCount < maxCapacity) {
|
||||
addWidgetToPanel(draggedAsset, panel);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if panel is locked
|
||||
const isPanelLocked = (panel: Side) =>
|
||||
selectedZone.lockedPanels.includes(panel);
|
||||
|
||||
// Get current widget count in a panel
|
||||
const getCurrentWidgetCount = (panel: Side) =>
|
||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||
|
||||
// Calculate panel capacity
|
||||
const calculatePanelCapacity = (panel: Side) => {
|
||||
const CHART_WIDTH = 170;
|
||||
const CHART_HEIGHT = 170;
|
||||
const FALLBACK_HORIZONTAL_CAPACITY = 5;
|
||||
const FALLBACK_VERTICAL_CAPACITY = 3;
|
||||
const CHART_WIDTH = panelSize;
|
||||
const CHART_HEIGHT = panelSize;
|
||||
|
||||
const dimensions = panelDimensions[panel];
|
||||
if (!dimensions) {
|
||||
return panel === "top" || panel === "bottom"
|
||||
? FALLBACK_HORIZONTAL_CAPACITY
|
||||
: FALLBACK_VERTICAL_CAPACITY;
|
||||
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
||||
}
|
||||
|
||||
return panel === "top" || panel === "bottom"
|
||||
? Math.floor(dimensions.width / CHART_WIDTH)
|
||||
: Math.floor(dimensions.height / CHART_HEIGHT);
|
||||
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
|
||||
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
|
||||
};
|
||||
|
||||
// while dublicate check this and add
|
||||
// Add widget to panel
|
||||
const addWidgetToPanel = async (asset: any, panel: Side) => {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
const newWidget = {
|
||||
...asset,
|
||||
id: generateUniqueId(),
|
||||
panel,
|
||||
};
|
||||
|
||||
let addWidget = {
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
widget: newWidget
|
||||
}
|
||||
widget: newWidget,
|
||||
};
|
||||
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-widget:add", addWidget)
|
||||
visualizationSocket.emit("v2:viz-widget:add", addWidget);
|
||||
}
|
||||
|
||||
setSelectedZone((prev) => ({
|
||||
...prev,
|
||||
widgets: [...prev.widgets, newWidget],
|
||||
}));
|
||||
|
||||
try {
|
||||
// let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
|
||||
|
||||
// if (response.message === "Widget created successfully") {
|
||||
// setSelectedZone((prev) => ({
|
||||
// ...prev,
|
||||
// widgets: [...prev.widgets, newWidget],
|
||||
// }));
|
||||
// }
|
||||
} catch (error) {
|
||||
console.error("Error adding widget:", error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Observe panel dimensions
|
||||
useEffect(() => {
|
||||
const observers: ResizeObserver[] = [];
|
||||
const currentPanelRefs = panelRefs.current;
|
||||
|
@ -194,6 +221,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||
}));
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(element);
|
||||
observers.push(observer);
|
||||
}
|
||||
|
@ -204,22 +232,15 @@ const Panel: React.FC<PanelProps> = ({
|
|||
};
|
||||
}, [selectedZone.activeSides]);
|
||||
|
||||
// Handle widget reordering
|
||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
||||
|
||||
setSelectedZone((prev) => {
|
||||
if (!prev) return prev; // Ensure prev is not null
|
||||
|
||||
// Filter widgets for the specified panel
|
||||
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
|
||||
|
||||
// Reorder widgets within the same panel
|
||||
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
||||
|
||||
// Merge the reordered widgets back into the full list while preserving the order
|
||||
const updatedWidgets = prev.widgets
|
||||
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
|
||||
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
|
||||
.filter((widget) => widget.panel !== panel)
|
||||
.concat(reorderedWidgets);
|
||||
|
||||
return {
|
||||
...prev,
|
||||
|
@ -228,13 +249,41 @@ const Panel: React.FC<PanelProps> = ({
|
|||
});
|
||||
};
|
||||
|
||||
// Calculate capacities and dimensions
|
||||
const topWidth = getPanelStyle("top").width;
|
||||
const bottomWidth = getPanelStyle("bottom").height;
|
||||
const leftHeight = getPanelStyle("left").height;
|
||||
const rightHeight = getPanelStyle("right").height;
|
||||
|
||||
const topCapacity = calculatePanelCapacity("top");
|
||||
const bottomCapacity = calculatePanelCapacity("bottom");
|
||||
const leftCapacity = calculatePanelCapacity("left");
|
||||
const rightCapacity = calculatePanelCapacity("right");
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>
|
||||
{`
|
||||
:root {
|
||||
--topWidth: ${topWidth};
|
||||
--bottomWidth: ${bottomWidth} ;
|
||||
--leftHeight: ${leftHeight};
|
||||
--rightHeight: ${rightHeight};
|
||||
|
||||
--topCapacity: ${topCapacity};
|
||||
--bottomCapacity: ${bottomCapacity};
|
||||
--leftCapacity: ${leftCapacity};
|
||||
--rightCapacity: ${rightCapacity};
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
{selectedZone.activeSides.map((side) => (
|
||||
<div
|
||||
key={side}
|
||||
className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${
|
||||
hiddenPanels.includes(side) ? "hidePanel" : ""
|
||||
id="panel-wrapper"
|
||||
className={`panel ${side}-panel absolute ${
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(side) ? "hidePanel" : ""
|
||||
}`}
|
||||
style={getPanelStyle(side)}
|
||||
onDrop={(e) => handleDrop(e, side)}
|
||||
|
@ -250,9 +299,11 @@ const Panel: React.FC<PanelProps> = ({
|
|||
<div
|
||||
className={`panel-content ${isPlaying && "fullScreen"}`}
|
||||
style={{
|
||||
pointerEvents: selectedZone.lockedPanels.includes(side)
|
||||
? "none"
|
||||
: "auto",
|
||||
pointerEvents:
|
||||
selectedZone.lockedPanels.includes(side) ||
|
||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||
? "none"
|
||||
: "auto",
|
||||
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -7,7 +7,7 @@ import DisplayZone from "./DisplayZone";
|
|||
import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { useDroppedObjectsStore, useFloatingWidget } from "../../../store/useDroppedObjectsStore";
|
||||
import {
|
||||
useAsset3dWidget,
|
||||
useSocketStore,
|
||||
|
@ -23,8 +23,12 @@ import RenderOverlay from "../../templates/Overlay";
|
|||
import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup";
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
import EditWidgetOption from "../menu/EditWidgetOption";
|
||||
import { useEditWidgetOptionsStore, useRightClickSelected, useRightSelected } from "../../../store/useZone3DWidgetStore";
|
||||
|
||||
import {
|
||||
useEditWidgetOptionsStore,
|
||||
useRightClickSelected,
|
||||
useRightSelected,
|
||||
} from "../../../store/useZone3DWidgetStore";
|
||||
import Dropped3dWidgets from "./Dropped3dWidget";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -49,8 +53,13 @@ type Widget = {
|
|||
data: any;
|
||||
};
|
||||
|
||||
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||
interface HiddenPanels {
|
||||
[zoneId: string]: Side[];
|
||||
}
|
||||
|
||||
const RealTimeVisulization: React.FC = () => {
|
||||
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
|
||||
const [hiddenPanels, setHiddenPanels] = React.useState<HiddenPanels>({});
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
@ -58,27 +67,24 @@ const RealTimeVisulization: React.FC = () => {
|
|||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
|
||||
|
||||
const { rightSelect, setRightSelect } = useRightSelected()
|
||||
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore()
|
||||
const { rightClickSelected, setRightClickSelected } = useRightClickSelected()
|
||||
const { rightSelect, setRightSelect } = useRightSelected();
|
||||
const { editWidgetOptions, setEditWidgetOptions } =
|
||||
useEditWidgetOptionsStore();
|
||||
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
||||
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
|
||||
|
||||
const [floatingWidgets, setFloatingWidgets] = useState<
|
||||
Record<string, { zoneName: string; zoneId: string; objects: any[] }>
|
||||
>({});
|
||||
// const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
|
||||
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
|
||||
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
async function GetZoneData() {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
try {
|
||||
const response = await getZone2dData(organization);
|
||||
// console.log('response: ', response);
|
||||
|
||||
if (!Array.isArray(response)) {
|
||||
return;
|
||||
|
@ -99,7 +105,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||
{}
|
||||
);
|
||||
setZonesData(formattedData);
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
GetZoneData();
|
||||
|
@ -127,6 +133,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||
// useEffect(() => {}, [floatingWidgets]);
|
||||
|
||||
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
event.preventDefault();
|
||||
const email = localStorage.getItem("email") || "";
|
||||
|
@ -172,6 +179,24 @@ const RealTimeVisulization: React.FC = () => {
|
|||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
|
||||
}
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, newObject);
|
||||
|
||||
//I need to console here objects based on selectedZone.zoneId
|
||||
// Console the objects after adding
|
||||
const droppedObjectsStore = useDroppedObjectsStore.getState();
|
||||
const currentZone = droppedObjectsStore.zones[selectedZone.zoneName];
|
||||
|
||||
if (currentZone && currentZone.zoneId === selectedZone.zoneId) {
|
||||
console.log(
|
||||
`Objects for Zone ID: ${selectedZone.zoneId}`,
|
||||
currentZone.objects
|
||||
);
|
||||
setFloatingWidget(currentZone.objects)
|
||||
} else {
|
||||
console.warn("Zone not found or mismatched zoneId");
|
||||
}
|
||||
|
||||
// let response = await addingFloatingWidgets(
|
||||
// selectedZone.zoneId,
|
||||
|
@ -180,46 +205,14 @@ const RealTimeVisulization: React.FC = () => {
|
|||
// );
|
||||
// Add the dropped object to the zone if the API call is successful
|
||||
// if (response.message === "FloatWidget created successfully") {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, newObject);
|
||||
// }
|
||||
|
||||
// Update floating widgets state
|
||||
setFloatingWidgets((prevWidgets) => ({
|
||||
...prevWidgets,
|
||||
[selectedZone.zoneName]: {
|
||||
...prevWidgets[selectedZone.zoneName],
|
||||
zoneName: selectedZone.zoneName,
|
||||
zoneId: selectedZone.zoneId,
|
||||
objects: [
|
||||
...(prevWidgets[selectedZone.zoneName]?.objects || []),
|
||||
newObject,
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
} catch (error) { }
|
||||
|
||||
};
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
const editWidgetOptions = document.querySelector(
|
||||
".editWidgetOptions-wrapper"
|
||||
);
|
||||
if (
|
||||
editWidgetOptions &&
|
||||
!editWidgetOptions.contains(event.target as Node)
|
||||
) {
|
||||
setRightClickSelected(null);
|
||||
setRightSelect(null);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [setRightClickSelected]);
|
||||
|
||||
// Add this useEffect hook to your component
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
const editWidgetOptions = document.querySelector(
|
||||
|
@ -241,84 +234,88 @@ const RealTimeVisulization: React.FC = () => {
|
|||
}, [setRightClickSelected]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
id="real-time-vis-canvas"
|
||||
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
||||
style={{
|
||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
>
|
||||
{openConfirmationPopup && (
|
||||
<RenderOverlay>
|
||||
<ConfirmationPopup
|
||||
message={"Are you sure want to delete?"}
|
||||
onConfirm={() => console.log("confirm")}
|
||||
onCancel={() => setOpenConfirmationPopup(false)}
|
||||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<>
|
||||
<div
|
||||
className="scene-container"
|
||||
ref={containerRef}
|
||||
id="real-time-vis-canvas"
|
||||
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
borderRadius:
|
||||
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
<Scene />
|
||||
</div>
|
||||
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
|
||||
<DroppedObjects />
|
||||
)}
|
||||
{activeModule === "visualization" && <SocketRealTimeViz />}
|
||||
|
||||
{activeModule === "visualization" &&
|
||||
editWidgetOptions &&
|
||||
rightClickSelected && (
|
||||
<EditWidgetOption
|
||||
options={[
|
||||
"Duplicate",
|
||||
"Vertical Move",
|
||||
"Horizontal Move",
|
||||
"RotateX",
|
||||
"RotateY",
|
||||
"Delete",
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<DisplayZone
|
||||
zonesData={zonesData}
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
/>
|
||||
|
||||
{!isPlaying && selectedZone?.zoneName !== "" && (
|
||||
<AddButtons
|
||||
hiddenPanels={hiddenPanels}
|
||||
setHiddenPanels={setHiddenPanels}
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
/>
|
||||
<div className="realTime-viz-wrapper">
|
||||
{openConfirmationPopup && (
|
||||
<RenderOverlay>
|
||||
<ConfirmationPopup
|
||||
message={"Are you sure want to delete?"}
|
||||
onConfirm={() => console.log("confirm")}
|
||||
onCancel={() => setOpenConfirmationPopup(false)}
|
||||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<div
|
||||
className="scene-container"
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
borderRadius:
|
||||
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
}}
|
||||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
<Scene />
|
||||
</div>
|
||||
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
|
||||
<DroppedObjects />
|
||||
)}
|
||||
{activeModule === "visualization" && <SocketRealTimeViz />}
|
||||
|
||||
<Panel
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
hiddenPanels={hiddenPanels}
|
||||
setZonesData={setZonesData}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{activeModule === "visualization" &&
|
||||
editWidgetOptions &&
|
||||
rightClickSelected && (
|
||||
<EditWidgetOption
|
||||
options={[
|
||||
"Duplicate",
|
||||
"Vertical Move",
|
||||
"Horizontal Move",
|
||||
"RotateX",
|
||||
"RotateY",
|
||||
"Delete",
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<DisplayZone
|
||||
zonesData={zonesData}
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
/>
|
||||
|
||||
{!isPlaying && selectedZone?.zoneName !== "" && (
|
||||
<AddButtons
|
||||
hiddenPanels={hiddenPanels}
|
||||
setHiddenPanels={setHiddenPanels}
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Panel
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
hiddenPanels={hiddenPanels}
|
||||
setZonesData={setZonesData}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import React, { useEffect, useRef } from 'react'
|
||||
import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store';
|
||||
import * as THREE from "three";
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
export default function ZoneAssets() {
|
||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { raycaster, controls, scene }: any = useThree();
|
||||
useEffect(() => {
|
||||
// console.log('zoneAssetId: ', zoneAssetId);
|
||||
if (!zoneAssetId) return
|
||||
console.log('zoneAssetId: ', zoneAssetId);
|
||||
let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id);
|
||||
if (AssetMesh) {
|
||||
const bbox = new THREE.Box3().setFromObject(AssetMesh);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AssetMesh.localToWorld(front);
|
||||
front.sub(AssetMesh.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AssetMesh, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
|
||||
|
||||
setSelectedFloorItem(AssetMesh);
|
||||
} else {
|
||||
console.log('zoneAssetId: ', zoneAssetId)
|
||||
if (Array.isArray(zoneAssetId.position) && zoneAssetId.position.length >= 3) {
|
||||
let selectedAssetPosition = [
|
||||
zoneAssetId.position[0],
|
||||
10,
|
||||
zoneAssetId.position[2]
|
||||
];
|
||||
console.log('selectedAssetPosition: ', selectedAssetPosition);
|
||||
let selectedAssetTarget = [
|
||||
zoneAssetId.position[0],
|
||||
zoneAssetId.position[1],
|
||||
zoneAssetId.position[2]
|
||||
];
|
||||
console.log('selectedAssetTarget: ', selectedAssetTarget);
|
||||
const setCam = async () => {
|
||||
await controls?.setLookAt(...selectedAssetPosition, ...selectedAssetTarget, true);
|
||||
setTimeout(() => {
|
||||
let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id);
|
||||
if (AssetMesh) {
|
||||
const bbox = new THREE.Box3().setFromObject(AssetMesh);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AssetMesh.localToWorld(front);
|
||||
front.sub(AssetMesh.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AssetMesh, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
|
||||
|
||||
setSelectedFloorItem(AssetMesh);
|
||||
}
|
||||
}, 500)
|
||||
|
||||
};
|
||||
setCam();
|
||||
}
|
||||
}
|
||||
}, [zoneAssetId, scene, controls])
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -2,8 +2,9 @@ import React, { useEffect, useState } from "react";
|
|||
import List from "./List";
|
||||
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
|
||||
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
|
||||
import { useZones } from "../../../store/store";
|
||||
import { useFloorItems, useZones } from "../../../store/store";
|
||||
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
|
||||
|
||||
interface DropDownListProps {
|
||||
value?: string; // Value to display in the DropDownList
|
||||
|
@ -38,89 +39,63 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
|||
const handleToggle = () => {
|
||||
setIsOpen((prev) => !prev); // Toggle the state
|
||||
};
|
||||
|
||||
interface Asset {
|
||||
id: string;
|
||||
name: string;
|
||||
position: [number, number, number]; // x, y, z
|
||||
}
|
||||
|
||||
const [zoneDataList, setZoneDataList] = useState<
|
||||
{ id: string; name: string; assets: Asset[] }[]
|
||||
>([]);
|
||||
interface Zone {
|
||||
zoneId: string;
|
||||
zoneName: string;
|
||||
points: [number, number, number][]; // polygon vertices
|
||||
}
|
||||
interface ZoneData {
|
||||
id: string;
|
||||
name: string;
|
||||
assets: { id: string; name: string; position?: []; rotation?: {} }[];
|
||||
}
|
||||
const [zoneDataList, setZoneDataList] = useState<ZoneData[]>([]);
|
||||
const { floorItems } = useFloorItems();
|
||||
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const isPointInsidePolygon = (point: [number, number], polygon: [number, number][]) => {
|
||||
let inside = false;
|
||||
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
const xi = polygon[i][0], zi = polygon[i][1];
|
||||
const xj = polygon[j][0], zj = polygon[j][1];
|
||||
|
||||
const intersect = ((zi > point[1]) !== (zj > point[1])) &&
|
||||
(point[0] < (xj - xi) * (point[1] - zi) / (zj - zi + 0.000001) + xi);
|
||||
|
||||
if (intersect) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(zones);
|
||||
// setZoneDataList([
|
||||
// { id: "2e996073-546c-470c-8323-55bd3700c6aa", name: "zone1" },
|
||||
// { id: "3f473bf0-197c-471c-a71f-943fc9ca2b47", name: "zone2" },
|
||||
// { id: "905e8fb6-9e18-469b-9474-e0478fb9601b", name: "zone3" },
|
||||
// { id: "9d9efcbe-8e96-47eb-bfad-128a9e4c532e", name: "zone4" },
|
||||
// { id: "884f3d29-eb5a-49a5-abe9-d11971c08e85", name: "zone5" },
|
||||
// { id: "70fa55cd-b5c9-4f80-a8c4-6319af3bfb4e", name: "zone6" },
|
||||
// ])
|
||||
const updatedZoneList: ZoneData[] = zones?.map((zone: Zone) => {
|
||||
const polygon2D = zone.points.map((p: [number, number, number]) => [p[0], p[2]]) as [number, number][];
|
||||
|
||||
const value = (zones || []).map(
|
||||
(val: { zoneId: string; zoneName: string }) => ({
|
||||
id: val.zoneId,
|
||||
name: val.zoneName,
|
||||
})
|
||||
);
|
||||
setZoneDataList([
|
||||
{
|
||||
id: "zone1",
|
||||
name: "Zone 1",
|
||||
assets: [
|
||||
{
|
||||
id: "asset1",
|
||||
name: "Asset 1",
|
||||
},
|
||||
{
|
||||
id: "asset2",
|
||||
name: "Asset 2",
|
||||
},
|
||||
{
|
||||
id: "asset3",
|
||||
name: "Asset 3",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "zone2",
|
||||
name: "Zone 2",
|
||||
assets: [
|
||||
{
|
||||
id: "asset4",
|
||||
name: "Asset 4",
|
||||
},
|
||||
{
|
||||
id: "asset5",
|
||||
name: "Asset 5",
|
||||
},
|
||||
{
|
||||
id: "asset6",
|
||||
name: "Asset 6",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "zone3",
|
||||
name: "Zone 3",
|
||||
assets: [
|
||||
{
|
||||
id: "asset7",
|
||||
name: "Asset 7",
|
||||
},
|
||||
{
|
||||
id: "asset8",
|
||||
name: "Asset 8",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
}, [zones]);
|
||||
const assetsInZone = floorItems
|
||||
.filter((item: any) => {
|
||||
const [x, , z] = item.position;
|
||||
return isPointInsidePolygon([x, z], polygon2D);
|
||||
})
|
||||
.map((item: any) => ({
|
||||
id: item.modeluuid,
|
||||
name: item.modelname,
|
||||
position: item.position,
|
||||
rotation: item.rotation
|
||||
}));
|
||||
|
||||
return {
|
||||
id: zone.zoneId,
|
||||
name: zone.zoneName,
|
||||
assets: assetsInZone,
|
||||
};
|
||||
});
|
||||
setZoneDataList(updatedZoneList);
|
||||
}, [zones, floorItems]);
|
||||
|
||||
return (
|
||||
<div className="dropdown-list-container">
|
||||
|
|
|
@ -12,10 +12,16 @@ import {
|
|||
LockIcon,
|
||||
RmoveIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useFloorItems, useZoneAssetId } from "../../../store/store";
|
||||
import { zoneCameraUpdate } from "../../../services/realTimeVisulization/zoneData/zoneCameraUpdation";
|
||||
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||
|
||||
interface Asset {
|
||||
id: string;
|
||||
name: string;
|
||||
position?: [number, number, number]; // Proper 3D vector
|
||||
rotation?: { x: number; y: number; z: number }; // Proper rotation format
|
||||
}
|
||||
|
||||
interface ZoneItem {
|
||||
|
@ -33,10 +39,13 @@ interface ListProps {
|
|||
const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||
|
||||
const { setSubModule } = useSubModuleStore();
|
||||
const [expandedZones, setExpandedZones] = useState<Record<string, boolean>>(
|
||||
{}
|
||||
);
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
|
||||
useEffect(() => {
|
||||
useSelectedZoneStore.getState().setSelectedZone({
|
||||
|
@ -61,7 +70,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
async function handleSelectZone(id: string) {
|
||||
try {
|
||||
if (selectedZone?.zoneId === id) {
|
||||
console.log("Zone is already selected:", selectedZone.zoneName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,17 +92,52 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
zoneViewPortPosition: response?.viewPortposition || [],
|
||||
});
|
||||
|
||||
console.log("Zone selected:", response?.zoneName);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error selecting zone:", error);
|
||||
|
||||
}
|
||||
}
|
||||
function handleAssetClick(asset: Asset) {
|
||||
setZoneAssetId(asset)
|
||||
}
|
||||
async function handleZoneNameChange(newName: string) {
|
||||
//zone apiiiiii
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
let zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
zoneName: newName
|
||||
};
|
||||
let response = await zoneCameraUpdate(zonesdata, organization);
|
||||
if (response.message === "updated successfully") {
|
||||
setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
|
||||
}
|
||||
}
|
||||
async function handleZoneAssetName(newName: string) {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
if (zoneAssetId?.id) {
|
||||
let response = await setFloorItemApi(organization, zoneAssetId.id, newName)
|
||||
console.log('response: ', response);
|
||||
setFloorItems((prevFloorItems: any[]) =>
|
||||
prevFloorItems.map((floorItems) =>
|
||||
floorItems.modeluuid === zoneAssetId.id
|
||||
? { ...floorItems, modelname: response.modelname }
|
||||
: floorItems
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
console.log('newName: ', newName);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.length > 0 ? (
|
||||
{items?.length > 0 ? (
|
||||
<ul className="list-wrapper">
|
||||
{items.map((item) => (
|
||||
{items?.map((item) => (
|
||||
<React.Fragment key={`zone-${item.id}`}>
|
||||
<li className="list-container">
|
||||
<div className="list-item">
|
||||
|
@ -102,7 +146,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
className="value"
|
||||
onClick={() => handleSelectZone(item.id)}
|
||||
>
|
||||
<RenameInput value={item.name} />
|
||||
<RenameInput value={item.name} onRename={handleZoneNameChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="options-container">
|
||||
|
@ -139,8 +183,8 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||
className="list-container asset-item"
|
||||
>
|
||||
<div className="list-item">
|
||||
<div className="value">
|
||||
<RenameInput value={asset.name} />
|
||||
<div className="value" onClick={() => handleAssetClick(asset)} >
|
||||
<RenameInput value={asset.name} onRename={handleZoneAssetName} />
|
||||
</div>
|
||||
<div className="options-container">
|
||||
<div className="lock option">
|
||||
|
|
|
@ -5,7 +5,7 @@ interface SimpleCardProps {
|
|||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
||||
value: string;
|
||||
per: string; // Percentage change
|
||||
position?: [number, number]
|
||||
position?: [number, number];
|
||||
}
|
||||
|
||||
const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||
|
@ -15,7 +15,6 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
|||
per,
|
||||
position = [0, 0],
|
||||
}) => {
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||
const cardData = JSON.stringify({
|
||||
|
@ -23,7 +22,7 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
|||
value,
|
||||
per,
|
||||
icon: Icon,
|
||||
|
||||
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store position
|
||||
});
|
||||
|
|
|
@ -18,18 +18,21 @@ type PathPoints = {
|
|||
interface ProcessContainerProps {
|
||||
processes: any[];
|
||||
agvRef: any;
|
||||
MaterialRef: any;
|
||||
}
|
||||
|
||||
const Agv: React.FC<ProcessContainerProps> = ({ processes, agvRef }) => {
|
||||
const Agv: React.FC<ProcessContainerProps> = ({
|
||||
processes,
|
||||
agvRef,
|
||||
MaterialRef,
|
||||
}) => {
|
||||
const [pathPoints, setPathPoints] = useState<PathPoints[]>([]);
|
||||
const { simulationStates } = useSimulationStates();
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { PlayAgv, setPlayAgv } = usePlayAgv();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("agvRef: ", agvRef);
|
||||
}, [agvRef]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (simulationStates.length > 0) {
|
||||
|
@ -90,6 +93,7 @@ const Agv: React.FC<ProcessContainerProps> = ({ processes, agvRef }) => {
|
|||
hitCount={pair.hitCount}
|
||||
processes={processes}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
|
||||
{pair.points.slice(1).map((point, idx) => (
|
||||
|
|
|
@ -32,7 +32,7 @@ export default function NavMeshDetails({
|
|||
|
||||
const [positions, indices] = getPositionsAndIndices(meshes);
|
||||
|
||||
const cellSize = 0.35;
|
||||
const cellSize = 0.2;
|
||||
const cellHeight = 0.7;
|
||||
const walkableRadius = 0.5;
|
||||
const { success, navMesh } = generateSoloNavMesh(positions, indices, {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React, { useEffect, useState, useRef } from "react";
|
||||
import React, { useEffect, useState, useRef, useMemo } from "react";
|
||||
import * as THREE from "three";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { NavMeshQuery } from "@recast-navigation/core";
|
||||
import { Line } from "@react-three/drei";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import { usePlayAgv } from "../../../store/store";
|
||||
import crate from "../../../assets/gltf-glb/crate_box.glb";
|
||||
|
||||
interface PathNavigatorProps {
|
||||
navMesh: any;
|
||||
|
@ -15,6 +16,7 @@ interface PathNavigatorProps {
|
|||
hitCount: number;
|
||||
processes: any[];
|
||||
agvRef: any;
|
||||
MaterialRef: any;
|
||||
}
|
||||
interface AGVData {
|
||||
processId: string;
|
||||
|
@ -23,7 +25,7 @@ interface AGVData {
|
|||
totalHits: number;
|
||||
}
|
||||
type Phase = "initial" | "toDrop" | "toPickup";
|
||||
|
||||
type MaterialType = "Box" | "Crate";
|
||||
export default function PathNavigator({
|
||||
navMesh,
|
||||
pathPoints,
|
||||
|
@ -33,11 +35,13 @@ export default function PathNavigator({
|
|||
hitCount,
|
||||
processes,
|
||||
agvRef,
|
||||
MaterialRef,
|
||||
}: PathNavigatorProps) {
|
||||
const [currentPhase, setCurrentPhase] = useState<Phase>("initial");
|
||||
// console.log('agvRef: ', agvRef);
|
||||
//
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const PickUpDrop = useRef([]);
|
||||
|
||||
// const [currentPhase, setCurrentPhase] = useState<"initial" | "loop">(
|
||||
// "initial"
|
||||
|
@ -45,6 +49,7 @@ export default function PathNavigator({
|
|||
const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const [pickupDropPath, setPickupDropPath] = useState<
|
||||
[number, number, number][]
|
||||
>([]);
|
||||
|
@ -57,6 +62,7 @@ export default function PathNavigator({
|
|||
const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(
|
||||
null
|
||||
);
|
||||
const [boxVisible, setBoxVisible] = useState(false);
|
||||
|
||||
const distancesRef = useRef<number[]>([]);
|
||||
const totalDistanceRef = useRef(0);
|
||||
|
@ -123,6 +129,9 @@ export default function PathNavigator({
|
|||
if (!navMesh || pathPoints.length < 2) return;
|
||||
|
||||
const [pickup, drop] = pathPoints.slice(-2);
|
||||
|
||||
PickUpDrop.current = pathPoints.slice(-2);
|
||||
|
||||
const object = scene.getObjectByProperty("uuid", id);
|
||||
|
||||
if (!object) return;
|
||||
|
@ -171,13 +180,101 @@ export default function PathNavigator({
|
|||
// Add these refs outside the useFrame if not already present:
|
||||
const startPointReached = useRef(false);
|
||||
|
||||
const baseMaterials = useMemo(
|
||||
() => ({
|
||||
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
||||
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||
// Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||
Default: new THREE.MeshStandardMaterial(),
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
// Example usage:
|
||||
const targetModelUUID = id; // Replace with your target ID
|
||||
const matchedProcess = findProcessByTargetModelUUID(
|
||||
processes,
|
||||
targetModelUUID
|
||||
);
|
||||
|
||||
function findProcessByTargetModelUUID(processes: any, targetModelUUID: any) {
|
||||
for (const process of processes) {
|
||||
for (const path of process.paths) {
|
||||
for (const point of path.points) {
|
||||
if (
|
||||
point.connections?.targets?.some(
|
||||
(target: any) => target.modelUUID === targetModelUUID
|
||||
)
|
||||
) {
|
||||
//
|
||||
return process.id; // Return the process if a match is found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null; // Return null if no match is found
|
||||
}
|
||||
|
||||
useFrame((_, delta) => {});
|
||||
const boxRef = useRef<THREE.Mesh | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!scene || !boxRef || !processes) return;
|
||||
|
||||
// 1. Find the existing object by UUID
|
||||
const existingObject = scene.getObjectByProperty("uuid", id);
|
||||
if (!existingObject) return;
|
||||
|
||||
// 2. Find the process that targets this object's modelUUID
|
||||
if (boxVisible) {
|
||||
const matchedProcess = findProcessByTargetModelUUID(processes, id);
|
||||
|
||||
// 3. Determine the material (from materialref) if a process is matched
|
||||
let materialType = "Default";
|
||||
|
||||
if (matchedProcess) {
|
||||
const materialEntry = MaterialRef.current.find((item: any) =>
|
||||
item.objects.some((obj: any) => obj.processId === matchedProcess)
|
||||
);
|
||||
console.log("materialEntry: ", materialEntry);
|
||||
if (materialEntry) {
|
||||
materialType = materialEntry.material; // "Box" or "Crate"
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Create the box mesh with the assigned material
|
||||
const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
|
||||
const boxMaterial = baseMaterials[materialType as MaterialType];
|
||||
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
|
||||
boxMesh.position.y = 1;
|
||||
boxMesh.name = `box-${id}`;
|
||||
|
||||
// 5. Add to scene and cleanup
|
||||
existingObject.add(boxMesh);
|
||||
boxRef.current = boxMesh;
|
||||
}
|
||||
|
||||
if (!startPointReached.current && boxVisible) {
|
||||
setBoxVisible(false);
|
||||
}
|
||||
return () => {
|
||||
if (boxRef.current?.parent) {
|
||||
boxRef.current.parent.remove(boxRef.current);
|
||||
}
|
||||
boxRef.current = null;
|
||||
};
|
||||
}, [
|
||||
processes,
|
||||
MaterialRef,
|
||||
boxVisible,
|
||||
findProcessByTargetModelUUID,
|
||||
startPointReached,
|
||||
]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
const currentAgv = (agvRef.current || []).find(
|
||||
(agv: AGVData) => agv.vehicleId === id
|
||||
);
|
||||
console.log("currentAgv: ", currentAgv?.isplaying);
|
||||
|
||||
if (!scene || !id || !isPlaying) return;
|
||||
|
||||
|
@ -203,6 +300,7 @@ export default function PathNavigator({
|
|||
|
||||
// Step 1: Snap to start point on first play
|
||||
if (isPlaying && !hasStarted.current && toPickupPath.length > 0) {
|
||||
setBoxVisible(false);
|
||||
const startPoint = new THREE.Vector3(...toPickupPath[0]);
|
||||
object.position.copy(startPoint);
|
||||
|
||||
|
@ -216,23 +314,23 @@ export default function PathNavigator({
|
|||
startPointReached.current = true;
|
||||
progressRef.current = 0;
|
||||
|
||||
setToPickupPath(toPickupPath.slice(-1));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Wait at start point for AGV readiness (only if expected hit count is not met)
|
||||
if (
|
||||
isPlaying &&
|
||||
startPointReached.current &&
|
||||
path.length === 0 &&
|
||||
currentAgv?.isplaying
|
||||
) {
|
||||
// Step 2: Wait at start point for AGV readiness
|
||||
if (isPlaying && startPointReached.current && path.length === 0) {
|
||||
if (!isAgvReady()) {
|
||||
return; // Prevent transitioning to the next phase if AGV is not ready
|
||||
}
|
||||
|
||||
setBoxVisible(true);
|
||||
|
||||
setPath([...toPickupPath]); // Start path transition once the AGV is ready
|
||||
setCurrentPhase("toDrop");
|
||||
progressRef.current = 0;
|
||||
console.log("startPointReached: ", startPointReached.current);
|
||||
startPointReached.current = false;
|
||||
|
||||
return;
|
||||
|
@ -283,12 +381,44 @@ export default function PathNavigator({
|
|||
if (currentPhase === "toDrop") {
|
||||
nextPath = dropPickupPath;
|
||||
nextPhase = "toPickup";
|
||||
setBoxVisible(false);
|
||||
} else if (currentPhase === "toPickup") {
|
||||
nextPath = pickupDropPath;
|
||||
nextPhase = "toDrop";
|
||||
// When returning to start point (toPickup phase completed)
|
||||
// Set position to toPickupPath[1] instead of [0]
|
||||
if (toPickupPath.length > 1) {
|
||||
object.position.copy(new THREE.Vector3(...toPickupPath[1]));
|
||||
// Also set rotation towards the next point if available
|
||||
if (toPickupPath.length > 2) {
|
||||
const nextPoint = new THREE.Vector3(...toPickupPath[2]);
|
||||
const direction = nextPoint
|
||||
.clone()
|
||||
.sub(object.position)
|
||||
.normalize();
|
||||
object.rotation.y = Math.atan2(direction.x, direction.z);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the path and mark start point as reached
|
||||
setPath([]);
|
||||
startPointReached.current = true;
|
||||
progressRef.current = 0;
|
||||
distancesRef.current = [];
|
||||
|
||||
// Stop the AGV by setting isplaying to false
|
||||
if (currentAgv) {
|
||||
currentAgv.isplaying = false;
|
||||
}
|
||||
|
||||
// Clear timeout and return to prevent further movement
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
nextPath = pickupDropPath;
|
||||
nextPhase = "toDrop";
|
||||
setBoxVisible(true);
|
||||
}
|
||||
|
||||
setPath([...nextPath]);
|
||||
|
@ -297,28 +427,12 @@ export default function PathNavigator({
|
|||
isWaiting.current = false;
|
||||
distancesRef.current = [];
|
||||
|
||||
// Decrease the expected count if AGV is ready and has completed its path
|
||||
if (currentAgv) {
|
||||
currentAgv.expectedCount = Math.max(0, currentAgv.expectedCount - 1); // Decrease but ensure it's not negative
|
||||
console.log(
|
||||
"Decreased expected count to: ",
|
||||
currentAgv.expectedCount
|
||||
);
|
||||
}
|
||||
|
||||
// Reset hit count for the next cycle
|
||||
if (agvRef.current) {
|
||||
agvRef.current = agvRef.current.map((agv: AGVData) =>
|
||||
agv.vehicleId === id ? { ...agv, hitCount: null } : agv
|
||||
agv.vehicleId === id ? { ...agv, hitCount: 0 } : agv
|
||||
);
|
||||
}
|
||||
|
||||
// 🔁 Reset and wait again after reaching start
|
||||
if (currentPhase === "toPickup" && currentAgv) {
|
||||
currentAgv.isplaying = false;
|
||||
setPath([]);
|
||||
startPointReached.current = true;
|
||||
progressRef.current = 0;
|
||||
}
|
||||
}, bufferTime * 1000);
|
||||
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as THREE from "three";
|
||||
import { Geometry, Base, Subtraction } from "@react-three/csg";
|
||||
import { useDeleteModels } from "../../../store/store";
|
||||
import { useDeleteTool } from "../../../store/store";
|
||||
import { useRef } from "react";
|
||||
|
||||
export interface CsgProps {
|
||||
|
@ -11,19 +11,19 @@ export interface CsgProps {
|
|||
}
|
||||
|
||||
export const Csg: React.FC<CsgProps> = (props) => {
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { deleteTool } = useDeleteTool();
|
||||
const modelRef = useRef<THREE.Object3D>();
|
||||
const originalMaterials = useRef<Map<THREE.Mesh, THREE.Material>>(new Map());
|
||||
|
||||
const handleHover = (hovered: boolean, object: THREE.Mesh | null) => {
|
||||
if (modelRef.current && deleteModels) {
|
||||
if (modelRef.current && deleteTool) {
|
||||
modelRef.current.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
if (!originalMaterials.current.has(child)) {
|
||||
originalMaterials.current.set(child, child.material);
|
||||
}
|
||||
child.material = child.material.clone();
|
||||
child.material.color.set(hovered && deleteModels ? 0xff0000 : (originalMaterials.current.get(child) as any).color);
|
||||
child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -86,8 +86,8 @@ async function addAssetModel(
|
|||
} else {
|
||||
// console.log(`Added ${selectedItem.name} from Backend`);
|
||||
|
||||
loader.load(`${url_Backend_dwinzo}/api/v1/AssetFile/${selectedItem.id}`, async (gltf) => {
|
||||
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v1/AssetFile/${selectedItem.id}`).then((res) => res.blob());
|
||||
loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`, async (gltf) => {
|
||||
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob());
|
||||
await storeGLTF(selectedItem.id, modelBlob);
|
||||
THREE.Cache.add(selectedItem.id, gltf);
|
||||
await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationStates, socket);
|
||||
|
@ -220,7 +220,7 @@ async function handleModelLoad(
|
|||
eventData.position = newFloorItem.position;
|
||||
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
eventData as Types.ConveyorEventsSchema
|
||||
]);
|
||||
|
@ -237,6 +237,7 @@ async function handleModelLoad(
|
|||
points: {
|
||||
uuid: pointUUID,
|
||||
position: res.points.position as [number, number, number],
|
||||
rotation: res.points.rotation as [number, number, number],
|
||||
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: '', start: {}, hitCount: 1, end: {}, buffer: 0 },
|
||||
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
|
||||
speed: 2,
|
||||
|
@ -283,13 +284,141 @@ async function handleModelLoad(
|
|||
return updatedItems;
|
||||
});
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
eventData as Types.VehicleEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (res.type === "StaticMachine") {
|
||||
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
|
||||
const backendEventData: Extract<Types.EventData['eventData'], { type: 'StaticMachine' }> = {
|
||||
type: "StaticMachine",
|
||||
points: {
|
||||
uuid: pointUUID,
|
||||
position: res.points.position as [number, number, number],
|
||||
rotation: res.points.rotation as [number, number, number],
|
||||
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 0, material: 'Inherit' },
|
||||
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
|
||||
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
|
||||
}
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// newFloorItem.modeluuid,
|
||||
// newFloorItem.modelname,
|
||||
// newFloorItem.modelfileID,
|
||||
// newFloorItem.position,
|
||||
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id
|
||||
};
|
||||
|
||||
const eventData: any = backendEventData;
|
||||
eventData.modeluuid = newFloorItem.modeluuid;
|
||||
eventData.modelName = newFloorItem.modelname;
|
||||
eventData.position = newFloorItem.position;
|
||||
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
|
||||
|
||||
setFloorItems((prevItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
});
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
eventData as Types.StaticMachineEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (res.type === "ArmBot") {
|
||||
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
|
||||
const backendEventData: Extract<Types.EventData['eventData'], { type: 'ArmBot' }> = {
|
||||
type: "ArmBot",
|
||||
points: {
|
||||
uuid: pointUUID,
|
||||
position: res.points.position as [number, number, number],
|
||||
rotation: res.points.rotation as [number, number, number],
|
||||
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] },
|
||||
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
|
||||
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
|
||||
}
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// newFloorItem.modeluuid,
|
||||
// newFloorItem.modelname,
|
||||
// newFloorItem.modelfileID,
|
||||
// newFloorItem.position,
|
||||
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id
|
||||
};
|
||||
|
||||
const eventData: any = backendEventData;
|
||||
eventData.modeluuid = newFloorItem.modeluuid;
|
||||
eventData.modelName = newFloorItem.modelname;
|
||||
eventData.position = newFloorItem.position;
|
||||
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
|
||||
|
||||
setFloorItems((prevItems) => {
|
||||
const updatedItems = [...(prevItems || []), newFloorItem];
|
||||
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
|
||||
return updatedItems;
|
||||
});
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
eventData as Types.ArmBotEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else {
|
||||
|
||||
// API
|
||||
|
|
|
@ -53,7 +53,7 @@ export default async function assetManager(
|
|||
if (!activePromises.get(taskId)) return; // Stop processing if task is canceled
|
||||
|
||||
await new Promise<void>(async (resolve) => {
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v1/AssetFile/${item.modelfileID!}`;
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
|
||||
|
||||
// Check Three.js Cache
|
||||
const cachedModel = THREE.Cache.get(item.modelfileID!);
|
||||
|
|
|
@ -53,7 +53,7 @@ export default function addFloorToScene(
|
|||
const mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
mesh.receiveShadow = true;
|
||||
mesh.position.y = layer;
|
||||
mesh.position.y = (layer) * CONSTANTS.wallConfig.height;
|
||||
mesh.rotateX(Math.PI / 2);
|
||||
mesh.name = `Floor_Layer_${layer}`;
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ function loadOnlyFloors(
|
|||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
|
||||
mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height + 0.03;
|
||||
mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height;
|
||||
mesh.rotateX(Math.PI / 2);
|
||||
mesh.name = `Only_Floor_Line_${floor[0][0][2]}`;
|
||||
|
||||
|
|
|
@ -1,143 +1,143 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi";
|
||||
import * as THREE from "three";
|
||||
import {
|
||||
useActiveLayer,
|
||||
useDeletedLines,
|
||||
useNewLines,
|
||||
useToggleView,
|
||||
} from "../../../../store/store";
|
||||
import objectLinesToArray from "./lineConvertions/objectLinesToArray";
|
||||
import { Html } from "@react-three/drei";
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
||||
const DistanceText = () => {
|
||||
const [lines, setLines] = useState<
|
||||
{
|
||||
distance: string;
|
||||
position: THREE.Vector3;
|
||||
userData: Types.Line;
|
||||
layer: string;
|
||||
}[]
|
||||
>([]);
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { toggleView } = useToggleView();
|
||||
const { newLines, setNewLines } = useNewLines();
|
||||
const { deletedLines, setDeletedLines } = useDeletedLines();
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
if (!email) return;
|
||||
const organization = email.split("@")[1].split(".")[0];
|
||||
|
||||
getLines(organization).then((data) => {
|
||||
data = objectLinesToArray(data);
|
||||
|
||||
const lines = data
|
||||
.filter((line: Types.Line) => line[0][2] === activeLayer)
|
||||
.map((line: Types.Line) => {
|
||||
const point1 = new THREE.Vector3(
|
||||
line[0][0].x,
|
||||
line[0][0].y,
|
||||
line[0][0].z
|
||||
);
|
||||
const point2 = new THREE.Vector3(
|
||||
line[1][0].x,
|
||||
line[1][0].y,
|
||||
line[1][0].z
|
||||
);
|
||||
const distance = point1.distanceTo(point2);
|
||||
const midpoint = new THREE.Vector3()
|
||||
.addVectors(point1, point2)
|
||||
.divideScalar(2);
|
||||
return {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer,
|
||||
};
|
||||
});
|
||||
setLines(lines);
|
||||
});
|
||||
}, [activeLayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (newLines.length > 0) {
|
||||
if (newLines[0][0][2] !== activeLayer) return;
|
||||
const newLinesData = newLines.map((line: Types.Line) => {
|
||||
const point1 = new THREE.Vector3(
|
||||
line[0][0].x,
|
||||
line[0][0].y,
|
||||
line[0][0].z
|
||||
);
|
||||
const point2 = new THREE.Vector3(
|
||||
line[1][0].x,
|
||||
line[1][0].y,
|
||||
line[1][0].z
|
||||
);
|
||||
const distance = point1.distanceTo(point2);
|
||||
const midpoint = new THREE.Vector3()
|
||||
.addVectors(point1, point2)
|
||||
.divideScalar(2);
|
||||
|
||||
return {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer,
|
||||
};
|
||||
});
|
||||
setLines((prevLines) => [...prevLines, ...newLinesData]);
|
||||
setNewLines([]);
|
||||
}
|
||||
}, [newLines, activeLayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if ((deletedLines as Types.Lines).length > 0) {
|
||||
setLines((prevLines) =>
|
||||
prevLines.filter(
|
||||
(line) =>
|
||||
!deletedLines.some(
|
||||
(deletedLine: any) =>
|
||||
deletedLine[0][1] === line.userData[0][1] &&
|
||||
deletedLine[1][1] === line.userData[1][1]
|
||||
)
|
||||
)
|
||||
);
|
||||
setDeletedLines([]);
|
||||
}
|
||||
}, [deletedLines]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView && (
|
||||
<group name="Distance_Text">
|
||||
{lines.map((text) => (
|
||||
<Html
|
||||
// data
|
||||
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
|
||||
userData={text.userData}
|
||||
position={[text.position.x, 1, text.position.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[100, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
|
||||
className={`distance line-${text.userData[0][1]}_${text.userData[1][1]}_${text.layer}`}
|
||||
>
|
||||
{text.distance} m
|
||||
</div>
|
||||
</Html>
|
||||
))}
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DistanceText;
|
||||
import { useEffect, useState } from "react";
|
||||
import { getLines } from "../../../../../services/factoryBuilder/lines/getLinesApi";
|
||||
import * as THREE from "three";
|
||||
import {
|
||||
useActiveLayer,
|
||||
useDeletedLines,
|
||||
useNewLines,
|
||||
useToggleView,
|
||||
} from "../../../../../store/store";
|
||||
import objectLinesToArray from "../lineConvertions/objectLinesToArray";
|
||||
import { Html } from "@react-three/drei";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
|
||||
const DistanceText = () => {
|
||||
const [lines, setLines] = useState<
|
||||
{
|
||||
distance: string;
|
||||
position: THREE.Vector3;
|
||||
userData: Types.Line;
|
||||
layer: string;
|
||||
}[]
|
||||
>([]);
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { toggleView } = useToggleView();
|
||||
const { newLines, setNewLines } = useNewLines();
|
||||
const { deletedLines, setDeletedLines } = useDeletedLines();
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
if (!email) return;
|
||||
const organization = email.split("@")[1].split(".")[0];
|
||||
|
||||
getLines(organization).then((data) => {
|
||||
data = objectLinesToArray(data);
|
||||
|
||||
const lines = data
|
||||
.filter((line: Types.Line) => line[0][2] === activeLayer)
|
||||
.map((line: Types.Line) => {
|
||||
const point1 = new THREE.Vector3(
|
||||
line[0][0].x,
|
||||
line[0][0].y,
|
||||
line[0][0].z
|
||||
);
|
||||
const point2 = new THREE.Vector3(
|
||||
line[1][0].x,
|
||||
line[1][0].y,
|
||||
line[1][0].z
|
||||
);
|
||||
const distance = point1.distanceTo(point2);
|
||||
const midpoint = new THREE.Vector3()
|
||||
.addVectors(point1, point2)
|
||||
.divideScalar(2);
|
||||
return {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer,
|
||||
};
|
||||
});
|
||||
setLines(lines);
|
||||
});
|
||||
}, [activeLayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (newLines.length > 0) {
|
||||
if (newLines[0][0][2] !== activeLayer) return;
|
||||
const newLinesData = newLines.map((line: Types.Line) => {
|
||||
const point1 = new THREE.Vector3(
|
||||
line[0][0].x,
|
||||
line[0][0].y,
|
||||
line[0][0].z
|
||||
);
|
||||
const point2 = new THREE.Vector3(
|
||||
line[1][0].x,
|
||||
line[1][0].y,
|
||||
line[1][0].z
|
||||
);
|
||||
const distance = point1.distanceTo(point2);
|
||||
const midpoint = new THREE.Vector3()
|
||||
.addVectors(point1, point2)
|
||||
.divideScalar(2);
|
||||
|
||||
return {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer,
|
||||
};
|
||||
});
|
||||
setLines((prevLines) => [...prevLines, ...newLinesData]);
|
||||
setNewLines([]);
|
||||
}
|
||||
}, [newLines, activeLayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if ((deletedLines as Types.Lines).length > 0) {
|
||||
setLines((prevLines) =>
|
||||
prevLines.filter(
|
||||
(line) =>
|
||||
!deletedLines.some(
|
||||
(deletedLine: any) =>
|
||||
deletedLine[0][1] === line.userData[0][1] &&
|
||||
deletedLine[1][1] === line.userData[1][1]
|
||||
)
|
||||
)
|
||||
);
|
||||
setDeletedLines([]);
|
||||
}
|
||||
}, [deletedLines]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView && (
|
||||
<group name="Distance_Text">
|
||||
{lines.map((text) => (
|
||||
<Html
|
||||
// data
|
||||
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
|
||||
userData={text.userData}
|
||||
position={[text.position.x, 1, text.position.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
key={`${text.userData[0][1]}_${text.userData[1][1]}`}
|
||||
className={`distance line-${text.userData[0][1]}_${text.userData[1][1]}_${text.layer}`}
|
||||
>
|
||||
{text.distance} m
|
||||
</div>
|
||||
</Html>
|
||||
))}
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DistanceText;
|
|
@ -0,0 +1,71 @@
|
|||
import * as THREE from "three";
|
||||
import { Html } from "@react-three/drei";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useActiveLayer } from "../../../../../store/store";
|
||||
|
||||
const ReferenceDistanceText = ({ line }: { line: any }) => {
|
||||
interface TextState {
|
||||
distance: string;
|
||||
position: THREE.Vector3;
|
||||
userData: any;
|
||||
layer: any;
|
||||
}
|
||||
|
||||
const [text, setTexts] = useState<TextState | null>(null);
|
||||
const { activeLayer } = useActiveLayer();
|
||||
|
||||
useEffect(() => {
|
||||
if (line) {
|
||||
if (line.parent === null) {
|
||||
setTexts(null);
|
||||
return;
|
||||
}
|
||||
const distance = line.userData.linePoints.cursorPosition.distanceTo(
|
||||
line.userData.linePoints.startPoint
|
||||
);
|
||||
const midpoint = new THREE.Vector3()
|
||||
.addVectors(
|
||||
line.userData.linePoints.cursorPosition,
|
||||
line.userData.linePoints.startPoint
|
||||
)
|
||||
.divideScalar(2);
|
||||
const newTexts = {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer,
|
||||
};
|
||||
setTexts(newTexts);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group name="Reference_Distance_Text">
|
||||
<mesh>
|
||||
{text !== null && (
|
||||
<Html
|
||||
// data
|
||||
key={text.distance}
|
||||
userData={text.userData}
|
||||
position={[text.position.x, 1, text.position.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
className={`Reference_Distance line-${text.userData.userData}`}
|
||||
>
|
||||
{text.distance} m
|
||||
</div>
|
||||
</Html>
|
||||
)}
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceDistanceText;
|
|
@ -1,48 +0,0 @@
|
|||
import * as THREE from 'three';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useActiveLayer } from '../../../../store/store';
|
||||
|
||||
const ReferenceDistanceText = ({ line }: { line: any }) => {
|
||||
interface TextState {
|
||||
distance: string;
|
||||
position: THREE.Vector3;
|
||||
userData: any;
|
||||
layer: any;
|
||||
}
|
||||
|
||||
const [text, setTexts] = useState<TextState | null>(null);
|
||||
const { activeLayer } = useActiveLayer();
|
||||
|
||||
useEffect(() => {
|
||||
if (line) {
|
||||
if (line.parent === null) {
|
||||
setTexts(null);
|
||||
return;
|
||||
}
|
||||
const distance = line.userData.linePoints.cursorPosition.distanceTo(line.userData.linePoints.startPoint);
|
||||
const midpoint = new THREE.Vector3().addVectors(line.userData.linePoints.cursorPosition, line.userData.linePoints.startPoint).divideScalar(2);
|
||||
const newTexts = {
|
||||
distance: distance.toFixed(1),
|
||||
position: midpoint,
|
||||
userData: line,
|
||||
layer: activeLayer
|
||||
};
|
||||
setTexts(newTexts);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group name='Reference_Distance_Text'>
|
||||
<mesh>
|
||||
{text !== null &&
|
||||
< Html transform sprite key={text.distance} userData={text.userData} scale={5} position={[text.position.x, 1, text.position.z]} style={{ pointerEvents: 'none' }}>
|
||||
<div className={`Reference_Distance line-${text.userData.userData}`}>{text.distance} m</div>
|
||||
</Html>
|
||||
}
|
||||
</mesh>
|
||||
</group >
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceDistanceText;
|
|
@ -1,5 +1,5 @@
|
|||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useAddAction, useDeleteModels, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store";
|
||||
import { useAddAction, useDeleteTool, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store";
|
||||
import hideRoof from "../geomentries/roofs/hideRoof";
|
||||
import hideWalls from "../geomentries/walls/hideWalls";
|
||||
import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar";
|
||||
|
@ -16,7 +16,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
|
|||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { scene, camera, pointer, raycaster, gl } = useThree();
|
||||
const { addAction, setAddAction } = useAddAction();
|
||||
const { deleteModels, setDeleteModels } = useDeleteModels();
|
||||
const { deleteTool, setDeleteTool } = useDeleteTool();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -56,7 +56,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
|
|||
if (addAction === "pillar") {
|
||||
addPillar(referencePole, floorGroup);
|
||||
}
|
||||
if (deleteModels) {
|
||||
if (deleteTool) {
|
||||
DeletePillar(hoveredDeletablePillar, floorGroup);
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
|
|||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
};
|
||||
}, [deleteModels, addAction])
|
||||
}, [deleteTool, addAction])
|
||||
|
||||
useFrame(() => {
|
||||
hideRoof(roofVisibility, floorGroup, camera);
|
||||
|
@ -87,7 +87,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
|
|||
if (addAction === "pillar") {
|
||||
addAndUpdateReferencePillar(raycaster, floorGroup, referencePole);
|
||||
}
|
||||
if (deleteModels) {
|
||||
if (deleteTool) {
|
||||
DeletableHoveredPillar(state, floorGroup, hoveredDeletablePillar);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import {
|
||||
useActiveTool,
|
||||
useAsset3dWidget,
|
||||
useCamMode,
|
||||
useDeletableFloorItem,
|
||||
useDeleteModels,
|
||||
useFloorItems,
|
||||
useLoadingProgress,
|
||||
useRenderDistance,
|
||||
useselectedFloorItem,
|
||||
useSelectedItem,
|
||||
useSimulationStates,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
useTransformMode,
|
||||
} from "../../../store/store";
|
||||
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
|
||||
import assetVisibility from "../geomentries/assets/assetVisibility";
|
||||
import { useEffect } from "react";
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import assetManager, {
|
||||
cancelOngoingTasks,
|
||||
cancelOngoingTasks,
|
||||
} from "../geomentries/assets/assetManager";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
|
@ -31,413 +16,315 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
|
|||
import { getFloorAssets } 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 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 FloorItemsGroup = ({
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
AttachedObject,
|
||||
floorGroup,
|
||||
tempLoader,
|
||||
isTempLoader,
|
||||
plane,
|
||||
}: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setselectedFloorItem } = useselectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane, }: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteTool } = useDeleteTool();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
dracoLoader.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||
);
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
getFloorAssets(organization).then((data) => {
|
||||
if (data.length > 0) {
|
||||
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 });
|
||||
} else {
|
||||
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
|
||||
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);
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
getFloorAssets(organization).then((data) => {
|
||||
if (data.length > 0) {
|
||||
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 });
|
||||
} else {
|
||||
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
|
||||
useEffect(() => {
|
||||
if (toggleView) return;
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(event.data.modelID, gltf);
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = state.camera.position;
|
||||
loadedAssets++;
|
||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||
updateLoadingProgress(progress);
|
||||
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
}, [camMode, renderDistance]);
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
useEffect(() => {
|
||||
if (toggleView) return;
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = camera.position;
|
||||
itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
|
||||
const cameraPosition = state.camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
};
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
|
||||
}, [camMode, renderDistance]);
|
||||
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
if (toggleView) return;
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
|
||||
const cameraPosition = camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
|
||||
};
|
||||
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
}
|
||||
};
|
||||
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
controls.addEventListener("rest", handleChange);
|
||||
controls.addEventListener("rest", stopInterval);
|
||||
controls.addEventListener("control", startInterval);
|
||||
controls.addEventListener("controlend", stopInterval);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener("rest", handleChange);
|
||||
controls.removeEventListener("rest", stopInterval);
|
||||
controls.removeEventListener("control", startInterval);
|
||||
controls.removeEventListener("controlend", stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
controls.addEventListener("rest", handleChange);
|
||||
controls.addEventListener("rest", stopInterval);
|
||||
controls.addEventListener("control", startInterval);
|
||||
controls.addEventListener("controlend", stopInterval);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener("rest", handleChange);
|
||||
controls.removeEventListener("rest", stopInterval);
|
||||
controls.removeEventListener("control", startInterval);
|
||||
controls.removeEventListener("controlend", stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
if (deleteModels) {
|
||||
DeleteFloorItems(
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setFloorItems,
|
||||
setSimulationStates,
|
||||
socket
|
||||
);
|
||||
}
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
// let currentObject = intersects[0].object;
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setselectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
};
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(
|
||||
AttachedObject.current
|
||||
);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center
|
||||
.clone()
|
||||
.addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(
|
||||
newPosition.x,
|
||||
newPosition.y,
|
||||
newPosition.z,
|
||||
true
|
||||
);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
|
||||
setselectedFloorItem(AttachedObject.current!);
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
if (deleteTool) {
|
||||
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket);
|
||||
|
||||
// Remove EventData if there are any in the asset.
|
||||
}
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
// let currentObject = intersects[0].object;
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setSelectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setSelectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
|
||||
|
||||
setSelectedFloorItem(AttachedObject.current!);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setSelectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, setSimulationStates, plane);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [deleteTool, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(
|
||||
raycaster,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
floorGroup,
|
||||
setFloorItems,
|
||||
itemsGroup,
|
||||
isTempLoader,
|
||||
tempLoader,
|
||||
socket,
|
||||
selectedItem,
|
||||
setSelectedItem,
|
||||
setSimulationStates,
|
||||
plane
|
||||
);
|
||||
}
|
||||
};
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteTool && activeModule === "builder") {
|
||||
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
|
||||
} else if (!deleteTool) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
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);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [
|
||||
deleteModels,
|
||||
transformMode,
|
||||
controls,
|
||||
selectedItem,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
activeTool,
|
||||
activeModule,
|
||||
]);
|
||||
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteModels) {
|
||||
DeletableHoveredFloorItems(
|
||||
state,
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setDeletableFloorItem
|
||||
);
|
||||
} else if (!deleteModels) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return <group ref={itemsGroup} name="itemsGroup"></group>;
|
||||
return <group ref={itemsGroup} name="itemsGroup"></group>;
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect } from "react";
|
||||
import { useDeleteModels, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store";
|
||||
import { useDeleteTool, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store";
|
||||
import { Csg } from "../csg/csg";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
@ -9,20 +9,21 @@ import handleMeshMissed from "../eventFunctions/handleMeshMissed";
|
|||
import DeleteWallItems from "../geomentries/walls/deleteWallItems";
|
||||
import loadInitialWallItems from "../../scene/IntialLoad/loadInitialWallItems";
|
||||
import AddWallItems from "../geomentries/walls/addWallItems";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
|
||||
const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletableWallItem, selectedItemsIndex, setSelectedItemsIndex, CSGGroup }: any) => {
|
||||
const { deleteModels, setDeleteModels } = useDeleteModels();
|
||||
const state = useThree();
|
||||
const { socket } = useSocketStore();
|
||||
const { pointer, camera, raycaster } = state;
|
||||
const { deleteTool, setDeleteTool } = useDeleteTool();
|
||||
const { wallItems, setWallItems } = useWallItems();
|
||||
const { objectPosition, setObjectPosition } = useObjectPosition();
|
||||
const { objectScale, setObjectScale } = useObjectScale();
|
||||
const { objectRotation, setObjectRotation } = useObjectRotation();
|
||||
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
|
||||
const { socket } = useSocketStore();
|
||||
const state = useThree();
|
||||
const { pointer, camera, raycaster } = state;
|
||||
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
useEffect(() => {
|
||||
// Load Wall Items from the backend
|
||||
|
@ -209,7 +210,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
|
|||
const onMouseUp = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (!drag && deleteModels) {
|
||||
if (!drag && deleteTool && activeModule === "builder") {
|
||||
DeleteWallItems(hoveredDeletableWallItem, setWallItems, wallItems, socket);
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +225,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
|
|||
const onDrop = (event: any) => {
|
||||
|
||||
if (!event.dataTransfer?.files[0]) return
|
||||
|
||||
|
||||
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
|
@ -256,15 +257,15 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
|
|||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [deleteModels, wallItems])
|
||||
}, [deleteTool, wallItems])
|
||||
|
||||
useEffect(() => {
|
||||
if (deleteModels) {
|
||||
if (deleteTool && activeModule === "builder") {
|
||||
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
|
||||
setSelectedWallItem(null);
|
||||
setSelectedItemsIndex(null);
|
||||
}
|
||||
}, [deleteModels])
|
||||
}, [deleteTool])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Geometry } from "@react-three/csg";
|
||||
import { useDeleteModels, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store";
|
||||
import { useDeleteTool, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store";
|
||||
import handleMeshDown from "../eventFunctions/handleMeshDown";
|
||||
import handleMeshMissed from "../eventFunctions/handleMeshMissed";
|
||||
import WallsMesh from "./wallsMesh";
|
||||
|
@ -11,13 +11,13 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde
|
|||
const { walls, setWalls } = useWalls();
|
||||
const { wallItems, setWallItems } = useWallItems();
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { deleteModels, setDeleteModels } = useDeleteModels();
|
||||
const { deleteTool, setDeleteTool } = useDeleteTool();
|
||||
const { transformMode, setTransformMode } = useTransformMode();
|
||||
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
|
||||
|
||||
useEffect(() => {
|
||||
if (transformMode === null) {
|
||||
if (!deleteModels) {
|
||||
if (!deleteTool) {
|
||||
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
|
||||
setSelectedWallItem(null);
|
||||
setSelectedItemsIndex(null);
|
||||
|
@ -33,12 +33,12 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde
|
|||
receiveShadow
|
||||
visible={!toggleView}
|
||||
onClick={(event) => {
|
||||
if (!deleteModels && transformMode !== null) {
|
||||
if (!deleteTool && transformMode !== null) {
|
||||
handleMeshDown(event, currentWallItem, setSelectedWallItem, setSelectedItemsIndex, wallItems, toggleView);
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (!deleteModels) {
|
||||
if (!deleteTool) {
|
||||
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
|
||||
setSelectedWallItem(null);
|
||||
setSelectedItemsIndex(null);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@ import { useEffect } from "react";
|
|||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { useActiveLayer, useSocketStore, useDeleteModels, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store";
|
||||
import { useActiveLayer, useSocketStore, useDeleteTool, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import arrayLineToObject from "../geomentries/lines/lineConvertions/arrayLineToObject";
|
||||
import addPointToScene from "../geomentries/points/addPointToScene";
|
||||
|
@ -14,7 +14,7 @@ import loadZones from "../geomentries/zones/loadZones";
|
|||
|
||||
const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { deleteModels, setDeleteModels } = useDeleteModels();
|
||||
const { deleteTool, setDeleteTool } = useDeleteTool();
|
||||
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { toolMode, setToolMode } = useToolMode();
|
||||
const { movePoint, setMovePoint } = useMovePoint();
|
||||
|
@ -35,7 +35,7 @@ const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint,
|
|||
if (toolMode === "Zone") {
|
||||
setDeletePointOrLine(false);
|
||||
setMovePoint(false);
|
||||
setDeleteModels(false);
|
||||
setDeleteTool(false);
|
||||
} else {
|
||||
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
|
||||
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
|
||||
|
|
|
@ -185,10 +185,9 @@ const CamModelsGroup = () => {
|
|||
position={[-0.015, 0, 0.7]}
|
||||
>
|
||||
<CollabUserIcon
|
||||
userImage={""}
|
||||
userImage={cam.userData.userImage ||""}
|
||||
userName={cam.userData.userName}
|
||||
index={index}
|
||||
color={getAvatarColor(index)}
|
||||
color={getAvatarColor(index, cam.userData.userName)}
|
||||
/>
|
||||
</Html>
|
||||
</primitive>
|
||||
|
|
|
@ -4,14 +4,12 @@ import CustomAvatar from "./users/Avatar";
|
|||
interface CollabUserIconProps {
|
||||
userName: string;
|
||||
userImage?: string;
|
||||
index?: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
||||
userImage,
|
||||
userName,
|
||||
index = 0,
|
||||
color,
|
||||
}) => {
|
||||
return (
|
||||
|
@ -20,24 +18,7 @@ const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
|||
{userImage ? (
|
||||
<img className="user-image" src={userImage} alt={userName} />
|
||||
) : (
|
||||
<CustomAvatar name={userName} index={index} color={color} />
|
||||
// <div
|
||||
// className="user-image"
|
||||
// style={{
|
||||
// lineHeight: "30px",
|
||||
// textTransform: "uppercase",
|
||||
// textAlign: "center",
|
||||
// fontSize: "16px",
|
||||
// borderRadius: "50%",
|
||||
// backgroundColor: color,
|
||||
// overflow: "hidden",
|
||||
// backgroundSize: "cover",
|
||||
// backgroundPosition: "center",
|
||||
// color: "white",
|
||||
// fontWeight: "bold",
|
||||
// }}>
|
||||
// {userName[0]}
|
||||
// </div>
|
||||
<CustomAvatar name={userName} color={color} />
|
||||
)}
|
||||
</div>
|
||||
<div className="user-name" style={{ backgroundColor: color }}>
|
||||
|
|
|
@ -105,7 +105,7 @@ export default function SocketResponses({
|
|||
// console.log(`Getting ${data.data.modelname} from cache`);
|
||||
const model = cachedModel.scene.clone();
|
||||
model.uuid = data.data.modeluuid;
|
||||
model.userData = { name: data.data.modelname, modelId: data.data.modelFileID, modeluuid: data.data.modeluuid };
|
||||
model.userData = { name: data.data.modelname, modelId: data.data.modelfileID, modeluuid: data.data.modeluuid };
|
||||
model.position.set(...data.data.position as [number, number, number]);
|
||||
model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z);
|
||||
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
|
||||
|
@ -159,7 +159,7 @@ export default function SocketResponses({
|
|||
url = URL.createObjectURL(indexedDBModel);
|
||||
} else {
|
||||
// console.log(`Getting ${data.data.modelname} from Backend`);
|
||||
url = `${url_Backend_dwinzo}/api/v1/AssetFile/${data.data.modelfileID}`;
|
||||
url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`;
|
||||
const modelBlob = await fetch(url).then((res) => res.blob());
|
||||
await storeGLTF(data.data.modelfileID, modelBlob);
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ export default function SocketResponses({
|
|||
THREE.Cache.remove(url);
|
||||
const model = gltf.scene;
|
||||
model.uuid = data.data.modeluuid;
|
||||
model.userData = { name: data.data.modelname, modelId: data.data.modelFileID, modeluuid: data.data.modeluuid };
|
||||
model.userData = { name: data.data.modelname, modelId: data.data.modelfileID, modeluuid: data.data.modeluuid };
|
||||
model.position.set(...data.data.position as [number, number, number]);
|
||||
model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z);
|
||||
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
|
||||
|
|
|
@ -5,7 +5,6 @@ import { getAvatarColor } from "./functions/getAvatarColor";
|
|||
interface AvatarProps {
|
||||
name: string; // Name can be a full name or initials
|
||||
size?: number;
|
||||
index?: number;
|
||||
textColor?: string;
|
||||
color?: string; // Optional color prop for future use
|
||||
}
|
||||
|
@ -13,7 +12,6 @@ interface AvatarProps {
|
|||
const CustomAvatar: React.FC<AvatarProps> = ({
|
||||
name,
|
||||
size = 100,
|
||||
index = 0,
|
||||
textColor = "#ffffff",
|
||||
color, // Optional color prop for future use
|
||||
}) => {
|
||||
|
@ -28,7 +26,7 @@ const CustomAvatar: React.FC<AvatarProps> = ({
|
|||
const initials = getInitials(name); // Convert name to initials if needed
|
||||
|
||||
// Draw background
|
||||
ctx.fillStyle = color || getAvatarColor(index); // Use color prop or generate color based on index
|
||||
ctx.fillStyle = color || "#323232"; // Use color prop or generate color based on index
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// Draw initials
|
||||
|
@ -42,7 +40,7 @@ const CustomAvatar: React.FC<AvatarProps> = ({
|
|||
const dataURL = canvas.toDataURL("image/png");
|
||||
setImageSrc(dataURL);
|
||||
}
|
||||
}, [name, size, textColor, index]);
|
||||
}, [name, size, textColor]);
|
||||
|
||||
if (!imageSrc) {
|
||||
return null; // Return null while the image is being generated
|
||||
|
@ -55,18 +53,6 @@ const CustomAvatar: React.FC<AvatarProps> = ({
|
|||
alt="User Avatar"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
/>
|
||||
// <div
|
||||
// className="user-image"
|
||||
// style={{
|
||||
// width: size,
|
||||
// height: size,
|
||||
// borderRadius: "50%",
|
||||
// overflow: "hidden",
|
||||
// backgroundSize: "cover",
|
||||
// backgroundPosition: "center",
|
||||
// }}>
|
||||
// {name[0]}
|
||||
// </div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,67 @@
|
|||
const avatarColors: string[] = [
|
||||
"#FF5733", // Red Orange
|
||||
"#FF5733", // Vivid Orange
|
||||
"#48ac2a", // Leaf Green
|
||||
"#0050eb", // Royal Blue
|
||||
"#0050eb", // Bright Blue
|
||||
"#FF33A1", // Hot Pink
|
||||
"#FF8C33", // Deep Orange
|
||||
"#8C33FF", // Violet
|
||||
"#FF3333", // Bright Red
|
||||
"#FF8C33", // Sunset Orange
|
||||
"#8C33FF", // Violet Purple
|
||||
"#FF3333", // Fiery Red
|
||||
"#43c06d", // Emerald Green
|
||||
"#A133FF", // Amethyst Purple
|
||||
"#C70039", // Crimson
|
||||
"#900C3F", // Maroon
|
||||
"#581845", // Plum
|
||||
"#3498DB", // Sky Blue
|
||||
"#2ECC71", // Green Mint
|
||||
"#E74C3C", // Tomato Red
|
||||
"#00adff", // Azure
|
||||
"#DBAD05", // Amber Yellow
|
||||
"#FF5733", // Red Orange
|
||||
"#FF33A1", // Hot Pink
|
||||
"#900C3F", // Maroon
|
||||
"#A133FF", // Royal Purple
|
||||
"#C70039", // Crimson Red
|
||||
"#900C3F", // Deep Burgundy
|
||||
"#581845", // Plum Purple
|
||||
"#3859AD", // Steel Blue
|
||||
"#08873E", // Forest Green
|
||||
"#E74C3C", // Cherry Red
|
||||
"#00adff", // Sky Blue
|
||||
"#DBAD05", // Golden Yellow
|
||||
"#A13E31", // Brick Red
|
||||
"#94C40E", // Lime Green
|
||||
"#060C47", // Midnight Blue
|
||||
"#2FAFAF", // Teal
|
||||
];
|
||||
|
||||
export function getAvatarColor(index: number): string {
|
||||
export function getAvatarColor(index: number, name?: string): string {
|
||||
// Check if the color is already stored in localStorage
|
||||
const localStorageKey = "userAvatarColors";
|
||||
// Helper function to check if local storage is available
|
||||
function isLocalStorageAvailable(): boolean {
|
||||
try {
|
||||
const testKey = "__test__";
|
||||
localStorage.setItem(testKey, "test");
|
||||
localStorage.removeItem(testKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if local storage is available
|
||||
if (isLocalStorageAvailable() && name) {
|
||||
let userColors = JSON.parse(localStorage.getItem(localStorageKey) || "{}");
|
||||
|
||||
// Check if the user already has an assigned color
|
||||
if (userColors[name]) {
|
||||
return userColors[name];
|
||||
}
|
||||
|
||||
// Find a new color not already assigned
|
||||
const usedColors = Object.values(userColors);
|
||||
const availableColors = avatarColors.filter(color => !usedColors.includes(color));
|
||||
|
||||
// Assign a new color
|
||||
const assignedColor = availableColors.length > 0
|
||||
? availableColors[0]
|
||||
: avatarColors[index % avatarColors.length];
|
||||
|
||||
userColors[name] = assignedColor;
|
||||
|
||||
// Save back to local storage
|
||||
localStorage.setItem(localStorageKey, JSON.stringify(userColors));
|
||||
|
||||
return assignedColor;
|
||||
}
|
||||
|
||||
// Fallback: Assign a color using the index if no name or local storage is unavailable
|
||||
return avatarColors[index % avatarColors.length];
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAss
|
|||
async function loadInitialFloorItems(
|
||||
itemsGroup: Types.RefGroup,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
setSimulationStates: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => void
|
||||
setSimulationStates: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => void
|
||||
): Promise<void> {
|
||||
if (!itemsGroup.current) return;
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
@ -83,16 +83,14 @@ async function loadInitialFloorItems(
|
|||
if (indexedDBModel) {
|
||||
// console.log(`[IndexedDB] Fetching ${item.modelname}`);
|
||||
const blobUrl = URL.createObjectURL(indexedDBModel);
|
||||
loader.load(
|
||||
blobUrl,
|
||||
(gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(item.modelfileID!, gltf);
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates);
|
||||
modelsLoaded++;
|
||||
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
||||
},
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(item.modelfileID!, gltf);
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates);
|
||||
modelsLoaded++;
|
||||
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
toast.error(`[IndexedDB] Error loading ${item.modelname}:`);
|
||||
|
@ -105,17 +103,15 @@ async function loadInitialFloorItems(
|
|||
|
||||
// Fetch from Backend
|
||||
// console.log(`[Backend] Fetching ${item.modelname}`);
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v1/AssetFile/${item.modelfileID!}`;
|
||||
loader.load(
|
||||
modelUrl,
|
||||
async (gltf) => {
|
||||
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||
await storeGLTF(item.modelfileID!, modelBlob);
|
||||
THREE.Cache.add(item.modelfileID!, gltf);
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates);
|
||||
modelsLoaded++;
|
||||
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
||||
},
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
|
||||
loader.load(modelUrl, async (gltf) => {
|
||||
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||
await storeGLTF(item.modelfileID!, modelBlob);
|
||||
THREE.Cache.add(item.modelfileID!, gltf);
|
||||
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates);
|
||||
modelsLoaded++;
|
||||
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
toast.error(`[Backend] Error loading ${item.modelname}:`);
|
||||
|
@ -138,7 +134,7 @@ async function loadInitialFloorItems(
|
|||
},
|
||||
]);
|
||||
|
||||
if (item.eventData || item.modelfileID === '67e3db95c2e8f37134526fb2') {
|
||||
if (item.eventData) {
|
||||
processEventData(item, setSimulationStates);
|
||||
}
|
||||
|
||||
|
@ -158,7 +154,7 @@ function processLoadedModel(
|
|||
item: Types.EventData,
|
||||
itemsGroup: Types.RefGroup,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
setSimulationStates: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => void
|
||||
setSimulationStates: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => void
|
||||
) {
|
||||
const model = gltf;
|
||||
model.uuid = item.modeluuid;
|
||||
|
@ -193,7 +189,7 @@ function processLoadedModel(
|
|||
},
|
||||
]);
|
||||
|
||||
if (item.eventData || item.modelfileID === '67e3db95c2e8f37134526fb2') {
|
||||
if (item.eventData) {
|
||||
processEventData(item, setSimulationStates);
|
||||
}
|
||||
|
||||
|
@ -211,7 +207,7 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
|
|||
data.position = item.position;
|
||||
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
data as Types.ConveyorEventsSchema
|
||||
]);
|
||||
|
@ -222,35 +218,39 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
|
|||
data.modeluuid = item.modeluuid;
|
||||
data.modelName = item.modelname;
|
||||
data.position = item.position;
|
||||
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
data as Types.VehicleEventsSchema
|
||||
]);
|
||||
|
||||
} else if (item.modelfileID === '67e3db95c2e8f37134526fb2') {
|
||||
} else if (item.eventData?.type === 'StaticMachine') {
|
||||
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
const pointPosition = new THREE.Vector3(0, 1.75, 0);
|
||||
const data: any = item.eventData;
|
||||
data.modeluuid = item.modeluuid;
|
||||
data.modelName = item.modelname;
|
||||
data.position = item.position;
|
||||
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
|
||||
|
||||
const staticMachine: Types.StaticMachineEventsSchema = {
|
||||
modeluuid: item.modeluuid,
|
||||
modelName: item.modelname,
|
||||
type: "StaticMachine",
|
||||
points: {
|
||||
uuid: pointUUID,
|
||||
position: [pointPosition.x, pointPosition.y, pointPosition.z],
|
||||
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 'Inherit', material: 'Inherit', isUsed: false },
|
||||
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
|
||||
connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
|
||||
},
|
||||
position: item.position
|
||||
};
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
staticMachine as Types.StaticMachineEventsSchema
|
||||
data as Types.StaticMachineEventsSchema
|
||||
]);
|
||||
|
||||
} else if (item.eventData?.type === 'ArmBot') {
|
||||
|
||||
const data: any = item.eventData;
|
||||
data.modeluuid = item.modeluuid;
|
||||
data.modelName = item.modelname;
|
||||
data.position = item.position;
|
||||
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
data as Types.ArmBotEventsSchema
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
return updatedItems;
|
||||
});
|
||||
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "default";
|
||||
|
@ -181,7 +181,10 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
uuid: THREE.MathUtils.generateUUID()
|
||||
}))
|
||||
: [defaultAction],
|
||||
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers,
|
||||
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
|
||||
...trigger,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
})),
|
||||
connections: {
|
||||
source: { modelUUID: obj.uuid, pointUUID },
|
||||
targets: []
|
||||
|
@ -234,7 +237,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.ConveyorEventsSchema
|
||||
]);
|
||||
|
@ -260,6 +263,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
|
@ -312,14 +316,206 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
|
|||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.VehicleEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'StaticMachine' && eventData) {
|
||||
const createStaticMachinePoint = () => {
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points;
|
||||
const hasActions = vehiclePoint?.actions !== undefined;
|
||||
|
||||
const defaultAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: 'Action 1',
|
||||
buffer: 0,
|
||||
material: 'Inherit',
|
||||
};
|
||||
|
||||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
}
|
||||
: defaultAction,
|
||||
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
|
||||
connections: {
|
||||
source: { modelUUID: obj.uuid, pointUUID },
|
||||
targets: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const backendEventData = {
|
||||
type: 'StaticMachine',
|
||||
points: createStaticMachinePoint()
|
||||
};
|
||||
|
||||
// API
|
||||
|
||||
// setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.StaticMachineEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'ArmBot' && eventData) {
|
||||
const createArmBotPoint = () => {
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points;
|
||||
const hasActions = vehiclePoint?.actions !== undefined;
|
||||
|
||||
const defaultAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: 'Action 1',
|
||||
buffer: 0,
|
||||
material: 'Inherit',
|
||||
};
|
||||
|
||||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
}
|
||||
: defaultAction,
|
||||
triggers: {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: vehiclePoint.triggers.name,
|
||||
type: vehiclePoint.triggers.type,
|
||||
},
|
||||
connections: {
|
||||
source: { modelUUID: obj.uuid, pointUUID },
|
||||
targets: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const backendEventData = {
|
||||
type: 'ArmBot',
|
||||
points: createArmBotPoint()
|
||||
};
|
||||
|
||||
// API
|
||||
|
||||
// setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.ArmBotEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else {
|
||||
|
||||
//REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// obj.userData.modelId,
|
||||
// false,
|
||||
// true,
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
return updatedItems;
|
||||
});
|
||||
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "default";
|
||||
|
@ -163,7 +163,10 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
uuid: THREE.MathUtils.generateUUID()
|
||||
}))
|
||||
: [defaultAction],
|
||||
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers,
|
||||
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
|
||||
...trigger,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
})),
|
||||
connections: {
|
||||
source: { modelUUID: newFloorItem.modeluuid, pointUUID },
|
||||
targets: []
|
||||
|
@ -174,9 +177,9 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
const backendEventData = {
|
||||
type: 'Conveyor',
|
||||
points: [
|
||||
createConveyorPoint(0), // point1
|
||||
createConveyorPoint(1), // middlePoint
|
||||
createConveyorPoint(2) // point2
|
||||
createConveyorPoint(0),
|
||||
createConveyorPoint(1),
|
||||
createConveyorPoint(2)
|
||||
],
|
||||
speed: (eventData as Types.ConveyorEventsSchema)?.speed
|
||||
};
|
||||
|
@ -216,7 +219,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.ConveyorEventsSchema
|
||||
]);
|
||||
|
@ -242,6 +245,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
|
@ -294,14 +298,206 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
|
|||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.VehicleEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'StaticMachine' && eventData) {
|
||||
const createStaticMachinePoint = () => {
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points;
|
||||
const hasActions = vehiclePoint?.actions !== undefined;
|
||||
|
||||
const defaultAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: 'Action 1',
|
||||
buffer: 0,
|
||||
material: 'Inherit',
|
||||
};
|
||||
|
||||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
}
|
||||
: defaultAction,
|
||||
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
|
||||
connections: {
|
||||
source: { modelUUID: obj.uuid, pointUUID },
|
||||
targets: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const backendEventData = {
|
||||
type: 'StaticMachine',
|
||||
points: createStaticMachinePoint()
|
||||
};
|
||||
|
||||
// API
|
||||
|
||||
// setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.StaticMachineEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'ArmBot' && eventData) {
|
||||
const createArmBotPoint = () => {
|
||||
const pointUUID = THREE.MathUtils.generateUUID();
|
||||
const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points;
|
||||
const hasActions = vehiclePoint?.actions !== undefined;
|
||||
|
||||
const defaultAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: 'Action 1',
|
||||
buffer: 0,
|
||||
material: 'Inherit',
|
||||
};
|
||||
|
||||
return {
|
||||
uuid: pointUUID,
|
||||
position: vehiclePoint?.position,
|
||||
// rotation: vehiclePoint?.rotation,
|
||||
actions: hasActions
|
||||
? {
|
||||
...vehiclePoint.actions,
|
||||
uuid: THREE.MathUtils.generateUUID()
|
||||
}
|
||||
: defaultAction,
|
||||
triggers: {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: vehiclePoint.triggers.name,
|
||||
type: vehiclePoint.triggers.type,
|
||||
},
|
||||
connections: {
|
||||
source: { modelUUID: obj.uuid, pointUUID },
|
||||
targets: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const backendEventData = {
|
||||
type: 'ArmBot',
|
||||
points: createArmBotPoint()
|
||||
};
|
||||
|
||||
// API
|
||||
|
||||
// setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
|
||||
...(prevEvents || []),
|
||||
newEventData as Types.ArmBotEventsSchema
|
||||
]);
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else {
|
||||
|
||||
//REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// obj.userData.modelId,
|
||||
// false,
|
||||
// true,
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
|
@ -180,12 +180,12 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
return updatedItems;
|
||||
});
|
||||
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "default";
|
||||
|
||||
if (eventData && eventData.type !== 'StaticMachine') {
|
||||
if (eventData) {
|
||||
if (eventData.type === 'Conveyor' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
|
@ -229,7 +229,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
|
@ -279,8 +279,115 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
: event
|
||||
);
|
||||
return updatedEvents;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'StaticMachine' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
type: 'StaticMachine',
|
||||
points: eventData.points,
|
||||
};
|
||||
|
||||
// REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
: event
|
||||
);
|
||||
return updatedEvents;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'ArmBot' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
type: 'ArmBot',
|
||||
points: eventData.points,
|
||||
};
|
||||
|
||||
// REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
|
|
|
@ -184,13 +184,12 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
return updatedItems;
|
||||
});
|
||||
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
console.log('eventData: ', eventData);
|
||||
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "default";
|
||||
|
||||
if (eventData && eventData.type !== 'StaticMachine') {
|
||||
if (eventData) {
|
||||
if (eventData.type === 'Conveyor' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
|
@ -233,9 +232,8 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
console.log('newEventData: ', newEventData);
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
|
@ -286,8 +284,115 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
|
|||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
: event
|
||||
);
|
||||
return updatedEvents;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'StaticMachine' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
type: 'StaticMachine',
|
||||
points: eventData.points,
|
||||
};
|
||||
|
||||
// REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
: event
|
||||
);
|
||||
return updatedEvents;
|
||||
});
|
||||
|
||||
socket.emit("v2:model-asset:add", data);
|
||||
|
||||
} else if (eventData.type === 'ArmBot' && eventData) {
|
||||
|
||||
const backendEventData = {
|
||||
type: 'ArmBot',
|
||||
points: eventData.points,
|
||||
};
|
||||
|
||||
// REST
|
||||
|
||||
// await setFloorItemApi(
|
||||
// organization,
|
||||
// obj.uuid,
|
||||
// obj.userData.name,
|
||||
// obj.userData.modelId,
|
||||
// [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
|
||||
// false,
|
||||
// true,
|
||||
// { type: backendEventData.type, points: backendEventData.points }
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modeluuid: newFloorItem.modeluuid,
|
||||
modelname: newFloorItem.modelname,
|
||||
modelfileID: newFloorItem.modelfileID,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
eventData: { type: backendEventData.type, points: backendEventData.points },
|
||||
socketId: socket.id,
|
||||
};
|
||||
|
||||
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
|
||||
newEventData.modeluuid = newFloorItem.modeluuid;
|
||||
newEventData.modelName = newFloorItem.modelname;
|
||||
newEventData.position = newFloorItem.position;
|
||||
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).map(event =>
|
||||
event.modeluuid === newFloorItem.modeluuid
|
||||
? { ...event, ...newEventData }
|
||||
|
|
|
@ -122,10 +122,15 @@ const SelectionControls: React.FC = () => {
|
|||
|
||||
if (!toggleView && activeModule === "builder") {
|
||||
helper.enabled = true;
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
if (duplicatedObjects.length === 0 && pastedObjects.length === 0) {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
} else {
|
||||
helper.enabled = false;
|
||||
helper.dispose();
|
||||
}
|
||||
canvasElement.addEventListener("contextmenu", onContextMenu);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("keydown", onKeyDown);
|
||||
} else {
|
||||
helper.enabled = false;
|
||||
|
@ -240,7 +245,7 @@ const SelectionControls: React.FC = () => {
|
|||
}
|
||||
});
|
||||
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updatedEvents = (prevEvents || []).filter(event => event.modeluuid !== selectedMesh.uuid);
|
||||
return updatedEvents;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TransformControls } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
import { useselectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store";
|
||||
import { useSelectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
|
@ -8,7 +8,7 @@ import { useEffect } from "react";
|
|||
|
||||
export default function TransformControl() {
|
||||
const state = useThree();
|
||||
const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { objectPosition, setObjectPosition } = useObjectPosition();
|
||||
const { objectScale, setObjectScale } = useObjectScale();
|
||||
const { objectRotation, setObjectRotation } = useObjectRotation();
|
||||
|
@ -96,7 +96,7 @@ export default function TransformControl() {
|
|||
const target = (state.controls as any).getTarget(new THREE.Vector3());
|
||||
(state.controls as any).setTarget(target.x, 0, target.z, true);
|
||||
}
|
||||
setselectedFloorItem(null);
|
||||
setSelectedFloorItem(null);
|
||||
{
|
||||
setObjectPosition({ x: undefined, y: undefined, z: undefined });
|
||||
setObjectScale({ x: undefined, y: undefined, z: undefined });
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
useSelectedActionSphere,
|
||||
useSelectedPath,
|
||||
useSelectedWallItem,
|
||||
useselectedFloorItem,
|
||||
useSelectedFloorItem,
|
||||
} from "../../../store/store";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
@ -15,7 +15,7 @@ import { useEffect } from "react";
|
|||
export default function PostProcessing() {
|
||||
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
|
||||
const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { selectedPath } = useSelectedPath();
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import Simulation from "../simulation/simulation";
|
|||
// import Simulation from "./simulationtemp/simulation";
|
||||
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
|
||||
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
|
||||
import ZoneAssets from "../../components/ui/componets/zoneAssets";
|
||||
|
||||
export default function Scene() {
|
||||
const map = useMemo(
|
||||
|
@ -48,6 +49,7 @@ export default function Scene() {
|
|||
<MeasurementTool />
|
||||
<World />
|
||||
<ZoneCentreTarget />
|
||||
<ZoneAssets />
|
||||
<Simulation />
|
||||
<PostProcessing />
|
||||
{savedTheme !== "dark" ? <Sun /> : <></>}
|
||||
|
|
|
@ -1,190 +1,244 @@
|
|||
import * as THREE from 'three';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useThree, useFrame } from '@react-three/fiber';
|
||||
import { useToolMode } from '../../../store/store';
|
||||
import { Html } from '@react-three/drei';
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import { useToolMode } from "../../../store/store";
|
||||
import { Html } from "@react-three/drei";
|
||||
|
||||
const MeasurementTool = () => {
|
||||
const { gl, raycaster, pointer, camera, scene } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
const { gl, raycaster, pointer, camera, scene } = useThree();
|
||||
const { toolMode } = useToolMode();
|
||||
|
||||
const [points, setPoints] = useState<THREE.Vector3[]>([]);
|
||||
const [tubeGeometry, setTubeGeometry] = useState<THREE.TubeGeometry | null>(null);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const [startConePosition, setStartConePosition] = useState<THREE.Vector3 | null>(null);
|
||||
const [endConePosition, setEndConePosition] = useState<THREE.Vector3 | null>(null);
|
||||
const [startConeQuaternion, setStartConeQuaternion] = useState(new THREE.Quaternion());
|
||||
const [endConeQuaternion, setEndConeQuaternion] = useState(new THREE.Quaternion());
|
||||
const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 });
|
||||
const [points, setPoints] = useState<THREE.Vector3[]>([]);
|
||||
const [tubeGeometry, setTubeGeometry] = useState<THREE.TubeGeometry | null>(
|
||||
null
|
||||
);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const [startConePosition, setStartConePosition] =
|
||||
useState<THREE.Vector3 | null>(null);
|
||||
const [endConePosition, setEndConePosition] = useState<THREE.Vector3 | null>(
|
||||
null
|
||||
);
|
||||
const [startConeQuaternion, setStartConeQuaternion] = useState(
|
||||
new THREE.Quaternion()
|
||||
);
|
||||
const [endConeQuaternion, setEndConeQuaternion] = useState(
|
||||
new THREE.Quaternion()
|
||||
);
|
||||
const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 });
|
||||
|
||||
const MIN_RADIUS = 0.001,
|
||||
MAX_RADIUS = 0.1;
|
||||
const MIN_CONE_RADIUS = 0.01,
|
||||
MAX_CONE_RADIUS = 0.4;
|
||||
const MIN_CONE_HEIGHT = 0.035,
|
||||
MAX_CONE_HEIGHT = 2.0;
|
||||
|
||||
const MIN_RADIUS = 0.001, MAX_RADIUS = 0.1;
|
||||
const MIN_CONE_RADIUS = 0.01, MAX_CONE_RADIUS = 0.4;
|
||||
const MIN_CONE_HEIGHT = 0.035, MAX_CONE_HEIGHT = 2.0;
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = () => {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
isLeftMouseDown = false;
|
||||
if (evt.button === 0 && !drag) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper"));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersectionPoint = intersects[0].point.clone();
|
||||
if (points.length < 2) {
|
||||
setPoints([...points, intersectionPoint]);
|
||||
} else {
|
||||
setPoints([intersectionPoint]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) drag = true;
|
||||
};
|
||||
|
||||
const onContextMenu = (evt: any) => {
|
||||
evt.preventDefault();
|
||||
if (!drag) {
|
||||
evt.preventDefault();
|
||||
setPoints([]);
|
||||
setTubeGeometry(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (toolMode === "MeasurementScale") {
|
||||
canvasElement.addEventListener("pointerdown", onMouseDown);
|
||||
canvasElement.addEventListener("pointermove", onMouseMove);
|
||||
canvasElement.addEventListener("pointerup", onMouseUp);
|
||||
canvasElement.addEventListener("contextmenu", onContextMenu);
|
||||
} else {
|
||||
resetMeasurement();
|
||||
setPoints([]);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onMouseDown);
|
||||
canvasElement.removeEventListener("pointermove", onMouseMove);
|
||||
canvasElement.removeEventListener("pointerup", onMouseUp);
|
||||
canvasElement.removeEventListener("contextmenu", onContextMenu);
|
||||
};
|
||||
}, [toolMode, camera, raycaster, pointer, scene, points]);
|
||||
|
||||
useFrame(() => {
|
||||
if (points.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper"));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
updateMeasurement(points[0], intersects[0].point);
|
||||
}
|
||||
} else if (points.length === 2) {
|
||||
updateMeasurement(points[0], points[1]);
|
||||
} else {
|
||||
resetMeasurement();
|
||||
}
|
||||
});
|
||||
|
||||
const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => {
|
||||
const distance = start.distanceTo(end);
|
||||
|
||||
const radius = THREE.MathUtils.clamp(distance * 0.02, MIN_RADIUS, MAX_RADIUS);
|
||||
const coneRadius = THREE.MathUtils.clamp(distance * 0.05, MIN_CONE_RADIUS, MAX_CONE_RADIUS);
|
||||
const coneHeight = THREE.MathUtils.clamp(distance * 0.2, MIN_CONE_HEIGHT, MAX_CONE_HEIGHT);
|
||||
|
||||
setConeSize({ radius: coneRadius, height: coneHeight });
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
|
||||
const offset = direction.clone().multiplyScalar(coneHeight * 0.5);
|
||||
|
||||
let tubeStart = start.clone().add(offset);
|
||||
let tubeEnd = end.clone().sub(offset);
|
||||
|
||||
tubeStart.y = Math.max(tubeStart.y, 0);
|
||||
tubeEnd.y = Math.max(tubeEnd.y, 0);
|
||||
|
||||
const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]);
|
||||
setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false));
|
||||
|
||||
setStartConePosition(tubeStart);
|
||||
setEndConePosition(tubeEnd);
|
||||
setStartConeQuaternion(getArrowOrientation(start, end));
|
||||
setEndConeQuaternion(getArrowOrientation(end, start));
|
||||
const onMouseDown = () => {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
};
|
||||
|
||||
const resetMeasurement = () => {
|
||||
const onMouseUp = (evt: any) => {
|
||||
isLeftMouseDown = false;
|
||||
if (evt.button === 0 && !drag) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children, true)
|
||||
.filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersectionPoint = intersects[0].point.clone();
|
||||
if (points.length < 2) {
|
||||
setPoints([...points, intersectionPoint]);
|
||||
} else {
|
||||
setPoints([intersectionPoint]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) drag = true;
|
||||
};
|
||||
|
||||
const onContextMenu = (evt: any) => {
|
||||
evt.preventDefault();
|
||||
if (!drag) {
|
||||
evt.preventDefault();
|
||||
setPoints([]);
|
||||
setTubeGeometry(null);
|
||||
setStartConePosition(null);
|
||||
setEndConePosition(null);
|
||||
}
|
||||
};
|
||||
|
||||
const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => {
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize().negate();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction);
|
||||
return quaternion;
|
||||
if (toolMode === "MeasurementScale") {
|
||||
canvasElement.addEventListener("pointerdown", onMouseDown);
|
||||
canvasElement.addEventListener("pointermove", onMouseMove);
|
||||
canvasElement.addEventListener("pointerup", onMouseUp);
|
||||
canvasElement.addEventListener("contextmenu", onContextMenu);
|
||||
} else {
|
||||
resetMeasurement();
|
||||
setPoints([]);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onMouseDown);
|
||||
canvasElement.removeEventListener("pointermove", onMouseMove);
|
||||
canvasElement.removeEventListener("pointerup", onMouseUp);
|
||||
canvasElement.removeEventListener("contextmenu", onContextMenu);
|
||||
};
|
||||
}, [toolMode, camera, raycaster, pointer, scene, points]);
|
||||
|
||||
useEffect(() => {
|
||||
if (points.length === 2) {
|
||||
console.log(points[0].distanceTo(points[1]));
|
||||
}
|
||||
}, [points])
|
||||
useFrame(() => {
|
||||
if (points.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children, true)
|
||||
.filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
|
||||
return (
|
||||
<group ref={groupRef} name="MeasurementGroup">
|
||||
{startConePosition && (
|
||||
<mesh name='MeasurementReference' position={startConePosition} quaternion={startConeQuaternion}>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{endConePosition && (
|
||||
<mesh name='MeasurementReference' position={endConePosition} quaternion={endConeQuaternion}>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{tubeGeometry && (
|
||||
<mesh name='MeasurementReference' geometry={tubeGeometry}>
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
if (intersects.length > 0) {
|
||||
updateMeasurement(points[0], intersects[0].point);
|
||||
}
|
||||
} else if (points.length === 2) {
|
||||
updateMeasurement(points[0], points[1]);
|
||||
} else {
|
||||
resetMeasurement();
|
||||
}
|
||||
});
|
||||
|
||||
{startConePosition && endConePosition && (
|
||||
<Html
|
||||
as="div"
|
||||
center
|
||||
zIndexRange={[1, 0]}
|
||||
style={{
|
||||
padding: "10px",
|
||||
color: "white",
|
||||
borderRadius: "8px",
|
||||
textAlign: "center",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
}}
|
||||
transform
|
||||
sprite
|
||||
scale={THREE.MathUtils.clamp(startConePosition.distanceTo(endConePosition) * 0.25, 0, 10)}
|
||||
position={[(startConePosition.x + endConePosition.x) / 2, (startConePosition.y + endConePosition.y) / 2, (startConePosition.z + endConePosition.z) / 2]}
|
||||
>
|
||||
<div style={{ color: "black" }} >{startConePosition.distanceTo(endConePosition).toFixed(2)} m</div>
|
||||
</Html>
|
||||
)}
|
||||
</group>
|
||||
const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => {
|
||||
const distance = start.distanceTo(end);
|
||||
|
||||
const radius = THREE.MathUtils.clamp(
|
||||
distance * 0.02,
|
||||
MIN_RADIUS,
|
||||
MAX_RADIUS
|
||||
);
|
||||
const coneRadius = THREE.MathUtils.clamp(
|
||||
distance * 0.05,
|
||||
MIN_CONE_RADIUS,
|
||||
MAX_CONE_RADIUS
|
||||
);
|
||||
const coneHeight = THREE.MathUtils.clamp(
|
||||
distance * 0.2,
|
||||
MIN_CONE_HEIGHT,
|
||||
MAX_CONE_HEIGHT
|
||||
);
|
||||
|
||||
setConeSize({ radius: coneRadius, height: coneHeight });
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
|
||||
const offset = direction.clone().multiplyScalar(coneHeight * 0.5);
|
||||
|
||||
let tubeStart = start.clone().add(offset);
|
||||
let tubeEnd = end.clone().sub(offset);
|
||||
|
||||
tubeStart.y = Math.max(tubeStart.y, 0);
|
||||
tubeEnd.y = Math.max(tubeEnd.y, 0);
|
||||
|
||||
const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]);
|
||||
setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false));
|
||||
|
||||
setStartConePosition(tubeStart);
|
||||
setEndConePosition(tubeEnd);
|
||||
setStartConeQuaternion(getArrowOrientation(start, end));
|
||||
setEndConeQuaternion(getArrowOrientation(end, start));
|
||||
};
|
||||
|
||||
const resetMeasurement = () => {
|
||||
setTubeGeometry(null);
|
||||
setStartConePosition(null);
|
||||
setEndConePosition(null);
|
||||
};
|
||||
|
||||
const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => {
|
||||
const direction = new THREE.Vector3()
|
||||
.subVectors(end, start)
|
||||
.normalize()
|
||||
.negate();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction);
|
||||
return quaternion;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (points.length === 2) {
|
||||
console.log(points[0].distanceTo(points[1]));
|
||||
}
|
||||
}, [points]);
|
||||
|
||||
return (
|
||||
<group ref={groupRef} name="MeasurementGroup">
|
||||
{startConePosition && (
|
||||
<mesh
|
||||
name="MeasurementReference"
|
||||
position={startConePosition}
|
||||
quaternion={startConeQuaternion}
|
||||
>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{endConePosition && (
|
||||
<mesh
|
||||
name="MeasurementReference"
|
||||
position={endConePosition}
|
||||
quaternion={endConeQuaternion}
|
||||
>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{tubeGeometry && (
|
||||
<mesh name="MeasurementReference" geometry={tubeGeometry}>
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
|
||||
{startConePosition && endConePosition && (
|
||||
<Html
|
||||
scale={THREE.MathUtils.clamp(
|
||||
startConePosition.distanceTo(endConePosition) * 0.25,
|
||||
0,
|
||||
10
|
||||
)}
|
||||
position={[
|
||||
(startConePosition.x + endConePosition.x) / 2,
|
||||
(startConePosition.y + endConePosition.y) / 2,
|
||||
(startConePosition.z + endConePosition.z) / 2,
|
||||
]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div>
|
||||
{startConePosition.distanceTo(endConePosition).toFixed(2)} m
|
||||
</div>
|
||||
</Html>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
export default MeasurementTool;
|
||||
|
|
|
@ -6,8 +6,8 @@ import { useThree, useFrame } from "@react-three/fiber";
|
|||
|
||||
////////// Component Imports //////////
|
||||
|
||||
import DistanceText from "../../builder/geomentries/lines/distanceText";
|
||||
import ReferenceDistanceText from "../../builder/geomentries/lines/referenceDistanceText";
|
||||
import DistanceText from "../../builder/geomentries/lines/distanceText/distanceText";
|
||||
import ReferenceDistanceText from "../../builder/geomentries/lines/distanceText/referenceDistanceText";
|
||||
|
||||
////////// Assests Imports //////////
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import { QuadraticBezierLine } from '@react-three/drei';
|
||||
import { useIsConnecting, useSimulationStates, useSocketStore } from '../../../store/store';
|
||||
import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
|
||||
import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt';
|
||||
|
@ -11,28 +11,23 @@ import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/
|
|||
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { gl, raycaster, scene, pointer, camera } = useThree();
|
||||
const { deleteTool } = useDeleteTool();
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { setIsConnecting } = useIsConnecting();
|
||||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { socket } = useSocketStore();
|
||||
const groupRefs = useRef<{ [key: string]: any }>({});
|
||||
|
||||
const [firstSelected, setFirstSelected] = useState<{
|
||||
modelUUID: string;
|
||||
sphereUUID: string;
|
||||
position: THREE.Vector3;
|
||||
isCorner: boolean;
|
||||
} | null>(null);
|
||||
const [firstSelected, setFirstSelected] = useState<{ modelUUID: 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 [helperlineColor, setHelperLineColor] = useState<string>('red');
|
||||
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
|
||||
|
||||
const updatePathConnections = (
|
||||
fromModelUUID: string,
|
||||
fromPointUUID: string,
|
||||
toModelUUID: string,
|
||||
toPointUUID: string
|
||||
) => {
|
||||
const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => {
|
||||
const updatedPaths = simulationStates.map(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
// Handle outgoing connections from Conveyor
|
||||
if (path.modeluuid === fromModelUUID) {
|
||||
return {
|
||||
...path,
|
||||
|
@ -61,6 +56,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
})
|
||||
};
|
||||
}
|
||||
// Handle incoming connections to Conveyor
|
||||
else if (path.modeluuid === toModelUUID) {
|
||||
return {
|
||||
...path,
|
||||
|
@ -167,82 +163,170 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
}
|
||||
return path;
|
||||
}
|
||||
// else if (path.type === 'StaticMachine') {
|
||||
// if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
|
||||
// const newTarget = {
|
||||
// modelUUID: toModelUUID,
|
||||
// pointUUID: toPointUUID
|
||||
// };
|
||||
// const existingTargets = path.points.connections.targets || [];
|
||||
else if (path.type === 'StaticMachine') {
|
||||
// Handle outgoing connections from StaticMachine
|
||||
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
|
||||
const newTarget = {
|
||||
modelUUID: toModelUUID,
|
||||
pointUUID: toPointUUID
|
||||
};
|
||||
|
||||
// // Check if target is an ArmBot
|
||||
// const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
|
||||
// if (toPath?.type !== 'ArmBot') {
|
||||
// console.log("StaticMachine can only connect to ArmBot");
|
||||
// return path;
|
||||
// }
|
||||
// Ensure target is an ArmBot
|
||||
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
|
||||
if (toPath?.type !== 'ArmBot') {
|
||||
console.log("StaticMachine can only connect to ArmBot");
|
||||
return path;
|
||||
}
|
||||
|
||||
// // Check if already has a connection
|
||||
// if (existingTargets.length >= 1) {
|
||||
// console.log("StaticMachine can have only one connection");
|
||||
// return path;
|
||||
// }
|
||||
const existingTargets = path.points.connections.targets || [];
|
||||
|
||||
// if (!existingTargets.some(target =>
|
||||
// target.modelUUID === newTarget.modelUUID &&
|
||||
// target.pointUUID === newTarget.pointUUID
|
||||
// )) {
|
||||
// return {
|
||||
// ...path,
|
||||
// points: {
|
||||
// ...path.points,
|
||||
// connections: {
|
||||
// ...path.points.connections,
|
||||
// targets: [...existingTargets, newTarget]
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// // Handle incoming connections to StaticMachine
|
||||
// else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
|
||||
// const reverseTarget = {
|
||||
// modelUUID: fromModelUUID,
|
||||
// pointUUID: fromPointUUID
|
||||
// };
|
||||
// const existingTargets = path.points.connections.targets || [];
|
||||
// Allow only one connection
|
||||
if (existingTargets.length >= 1) {
|
||||
console.log("StaticMachine can only have one connection");
|
||||
return path;
|
||||
}
|
||||
|
||||
// // Check if source is an ArmBot
|
||||
// const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
|
||||
// if (fromPath?.type !== 'ArmBot') {
|
||||
// console.log("StaticMachine can only connect to ArmBot");
|
||||
// return path;
|
||||
// }
|
||||
if (!existingTargets.some(target =>
|
||||
target.modelUUID === newTarget.modelUUID &&
|
||||
target.pointUUID === newTarget.pointUUID
|
||||
)) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
connections: {
|
||||
...path.points.connections,
|
||||
targets: [...existingTargets, newTarget]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// // Check if already has a connection
|
||||
// if (existingTargets.length >= 1) {
|
||||
// console.log("StaticMachine can have only one connection");
|
||||
// return path;
|
||||
// }
|
||||
// Handle incoming connections to StaticMachine
|
||||
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
|
||||
const reverseTarget = {
|
||||
modelUUID: fromModelUUID,
|
||||
pointUUID: fromPointUUID
|
||||
};
|
||||
|
||||
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
|
||||
if (fromPath?.type !== 'ArmBot') {
|
||||
console.log("StaticMachine can only be connected from ArmBot");
|
||||
return path;
|
||||
}
|
||||
|
||||
const existingTargets = path.points.connections.targets || [];
|
||||
|
||||
if (existingTargets.length >= 1) {
|
||||
console.log("StaticMachine can only have one connection");
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!existingTargets.some(target =>
|
||||
target.modelUUID === reverseTarget.modelUUID &&
|
||||
target.pointUUID === reverseTarget.pointUUID
|
||||
)) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
connections: {
|
||||
...path.points.connections,
|
||||
targets: [...existingTargets, reverseTarget]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
else if (path.type === 'ArmBot') {
|
||||
// Handle outgoing connections from ArmBot
|
||||
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
|
||||
const newTarget = {
|
||||
modelUUID: toModelUUID,
|
||||
pointUUID: toPointUUID
|
||||
};
|
||||
|
||||
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
|
||||
if (!toPath) return path;
|
||||
|
||||
const existingTargets = path.points.connections.targets || [];
|
||||
|
||||
// Check if connecting to a StaticMachine and already connected to one
|
||||
const alreadyConnectedToStatic = existingTargets.some(target => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
return targetPath?.type === 'StaticMachine';
|
||||
});
|
||||
|
||||
if (toPath.type === 'StaticMachine') {
|
||||
if (alreadyConnectedToStatic) {
|
||||
console.log("ArmBot can only connect to one StaticMachine");
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingTargets.some(target =>
|
||||
target.modelUUID === newTarget.modelUUID &&
|
||||
target.pointUUID === newTarget.pointUUID
|
||||
)) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
connections: {
|
||||
...path.points.connections,
|
||||
targets: [...existingTargets, newTarget]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Handle incoming connections to ArmBot
|
||||
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
|
||||
const reverseTarget = {
|
||||
modelUUID: fromModelUUID,
|
||||
pointUUID: fromPointUUID
|
||||
};
|
||||
|
||||
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
|
||||
if (!fromPath) return path;
|
||||
|
||||
const existingTargets = path.points.connections.targets || [];
|
||||
|
||||
const alreadyConnectedFromStatic = existingTargets.some(target => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
return targetPath?.type === 'StaticMachine';
|
||||
});
|
||||
|
||||
if (fromPath.type === 'StaticMachine') {
|
||||
if (alreadyConnectedFromStatic) {
|
||||
console.log("ArmBot can only be connected from one StaticMachine");
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingTargets.some(target =>
|
||||
target.modelUUID === reverseTarget.modelUUID &&
|
||||
target.pointUUID === reverseTarget.pointUUID
|
||||
)) {
|
||||
return {
|
||||
...path,
|
||||
points: {
|
||||
...path.points,
|
||||
connections: {
|
||||
...path.points.connections,
|
||||
targets: [...existingTargets, reverseTarget]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// if (!existingTargets.some(target =>
|
||||
// target.modelUUID === reverseTarget.modelUUID &&
|
||||
// target.pointUUID === reverseTarget.pointUUID
|
||||
// )) {
|
||||
// return {
|
||||
// ...path,
|
||||
// points: {
|
||||
// ...path.points,
|
||||
// connections: {
|
||||
// ...path.points.connections,
|
||||
// targets: [...existingTargets, reverseTarget]
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// return path;
|
||||
// }
|
||||
return path;
|
||||
});
|
||||
|
||||
|
@ -255,7 +339,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
updateBackend(updatedPathDetails);
|
||||
};
|
||||
|
||||
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
|
||||
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
if (updatedPaths.length === 0) return;
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||
|
@ -293,6 +377,38 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
|
||||
socket.emit('v2:model-asset:updateEventData', data);
|
||||
|
||||
} else if (updatedPath.type === 'StaticMachine') {
|
||||
|
||||
// await setEventApi(
|
||||
// organization,
|
||||
// updatedPath.modeluuid,
|
||||
// { type: "StaticMachine", points: updatedPath.points }
|
||||
// );
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modeluuid: updatedPath.modeluuid,
|
||||
eventData: { type: "StaticMachine", points: updatedPath.points }
|
||||
}
|
||||
|
||||
socket.emit('v2:model-asset:updateEventData', data);
|
||||
|
||||
} else if (updatedPath.type === 'ArmBot') {
|
||||
|
||||
// await setEventApi(
|
||||
// organization,
|
||||
// updatedPath.modeluuid,
|
||||
// { type: "ArmBot", points: updatedPath.points }
|
||||
// );
|
||||
|
||||
const data = {
|
||||
organization: organization,
|
||||
modeluuid: updatedPath.modeluuid,
|
||||
eventData: { type: "ArmBot", points: updatedPath.points }
|
||||
}
|
||||
|
||||
socket.emit('v2:model-asset:updateEventData', data);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -397,7 +513,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
|
||||
// For Vehicles, check if they're already connected to anything
|
||||
if (intersected.userData.path.type === 'Vehicle') {
|
||||
console.log('intersected: ', intersected);
|
||||
const vehicleConnections = intersected.userData.path.points.connections.targets.length;
|
||||
if (vehicleConnections >= 1) {
|
||||
console.log("Vehicle can only have one connection");
|
||||
|
@ -437,6 +552,69 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if StaticMachine is involved in the connection
|
||||
if ((firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
|
||||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot')) {
|
||||
console.log("StaticMachine can only connect to ArmBot");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if StaticMachine already has a connection
|
||||
if (firstPath?.type === 'StaticMachine') {
|
||||
const staticConnections = firstPath.points.connections.targets.length;
|
||||
if (staticConnections >= 1) {
|
||||
console.log("StaticMachine can only have one connection");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (secondPath?.type === 'StaticMachine') {
|
||||
const staticConnections = secondPath.points.connections.targets.length;
|
||||
if (staticConnections >= 1) {
|
||||
console.log("StaticMachine can only have one connection");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if ArmBot is involved
|
||||
if ((firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
|
||||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine')) {
|
||||
|
||||
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
|
||||
const staticPath = firstPath?.type === 'StaticMachine' ? firstPath : secondPath;
|
||||
|
||||
const armBotConnections = armBotPath.points.connections.targets || [];
|
||||
const alreadyConnectedToStatic = armBotConnections.some(target => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
return targetPath?.type === 'StaticMachine';
|
||||
});
|
||||
|
||||
if (alreadyConnectedToStatic) {
|
||||
console.log("ArmBot can only connect to one StaticMachine");
|
||||
return;
|
||||
}
|
||||
|
||||
const staticConnections = staticPath.points.connections.targets.length;
|
||||
if (staticConnections >= 1) {
|
||||
console.log("StaticMachine can only have one connection");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent ArmBot ↔ ArmBot
|
||||
if (firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot') {
|
||||
console.log("Cannot connect two ArmBots together");
|
||||
return;
|
||||
}
|
||||
|
||||
// If one is ArmBot, ensure the other is StaticMachine or Conveyor
|
||||
if (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') {
|
||||
const otherType = firstPath?.type === 'ArmBot' ? secondPath?.type : firstPath?.type;
|
||||
if (otherType !== 'StaticMachine' && otherType !== 'Conveyor') {
|
||||
console.log("ArmBot can only connect to Conveyors or one StaticMachine");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At least one must be start/end point
|
||||
if (!firstSelected.isCorner && !isStartOrEnd) {
|
||||
console.log("At least one of the selected spheres must be a start or end point.");
|
||||
|
@ -444,20 +622,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
}
|
||||
|
||||
// All checks passed - make the connection
|
||||
handleAddConnection(
|
||||
firstSelected.modelUUID,
|
||||
firstSelected.sphereUUID,
|
||||
modelUUID,
|
||||
sphereUUID
|
||||
);
|
||||
handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID);
|
||||
} else {
|
||||
// First selection - just store it
|
||||
setFirstSelected({
|
||||
modelUUID,
|
||||
sphereUUID,
|
||||
position: worldPosition,
|
||||
isCorner: isStartOrEnd
|
||||
});
|
||||
setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd });
|
||||
setIsConnecting(true);
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +638,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
}
|
||||
};
|
||||
|
||||
if (activeModule === 'simulation') {
|
||||
if (activeModule === 'simulation' && !deleteTool) {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
|
@ -487,7 +655,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("contextmenu", onContextMenu);
|
||||
};
|
||||
}, [camera, scene, raycaster, firstSelected, simulationStates]);
|
||||
}, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]);
|
||||
|
||||
useFrame(() => {
|
||||
Object.values(groupRefs.current).forEach((group) => {
|
||||
if (group) {
|
||||
const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position);
|
||||
group.visible = ((distance <= renderDistance) && !isPlaying);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
useFrame(() => {
|
||||
if (firstSelected) {
|
||||
|
@ -511,9 +688,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
}
|
||||
}
|
||||
|
||||
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
|
||||
obj.object.name.includes("events-sphere")
|
||||
);
|
||||
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere"));
|
||||
|
||||
if (sphereIntersects.length > 0) {
|
||||
const sphere = sphereIntersects[0].object;
|
||||
|
@ -528,15 +703,21 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
|
||||
|
||||
// Inside the useFrame hook, where we check for snapped spheres:
|
||||
const isConnectable = (pathData.type === 'Vehicle' ||
|
||||
const isConnectable = (
|
||||
pathData.type === 'Vehicle' ||
|
||||
pathData.type === 'ArmBot' ||
|
||||
(pathData.points.length > 0 && (
|
||||
sphereUUID === pathData.points[0].uuid ||
|
||||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
|
||||
))) &&
|
||||
sphereUUID === pathData.points[pathData.points.length - 1].uuid ||
|
||||
(pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points
|
||||
))
|
||||
) &&
|
||||
!isVehicleToVehicle &&
|
||||
!(firstPath?.type === 'Conveyor' &&
|
||||
!(
|
||||
firstPath?.type === 'Conveyor' &&
|
||||
pathData.type === 'Conveyor' &&
|
||||
!firstSelected.isCorner);
|
||||
!firstSelected.isCorner
|
||||
);
|
||||
|
||||
// Check for duplicate connection (regardless of path type)
|
||||
const isDuplicateConnection = simulationStates.some(path => {
|
||||
|
@ -574,12 +755,50 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
(firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
|
||||
(secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor');
|
||||
|
||||
// Check if StaticMachine is connecting to non-ArmBot
|
||||
const isStaticMachineToNonArmBot =
|
||||
(firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
|
||||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot');
|
||||
|
||||
// Check if StaticMachine already has a connection
|
||||
const isStaticMachineAtMaxConnections =
|
||||
(firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) ||
|
||||
(secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1);
|
||||
|
||||
// Check if ArmBot is connecting to StaticMachine
|
||||
const isArmBotToStaticMachine =
|
||||
(firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
|
||||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine');
|
||||
|
||||
// Prevent multiple StaticMachine connections to ArmBot
|
||||
let isArmBotAlreadyConnectedToStatic = false;
|
||||
if (isArmBotToStaticMachine) {
|
||||
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
|
||||
isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
return targetPath?.type === 'StaticMachine';
|
||||
});
|
||||
}
|
||||
|
||||
// Prevent ArmBot to ArmBot
|
||||
const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot';
|
||||
|
||||
// If ArmBot is involved, other must be Conveyor or StaticMachine
|
||||
const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') &&
|
||||
!(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' ||
|
||||
secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine');
|
||||
|
||||
if (
|
||||
!isDuplicateConnection &&
|
||||
!isVehicleToVehicle &&
|
||||
!isNonVehicleAlreadyConnected &&
|
||||
!isVehicleAtMaxConnections &&
|
||||
!isVehicleConnectingToNonConveyor &&
|
||||
!isStaticMachineToNonArmBot &&
|
||||
!isStaticMachineAtMaxConnections &&
|
||||
!isArmBotToArmBot &&
|
||||
!isArmBotToInvalidType &&
|
||||
!isArmBotAlreadyConnectedToStatic &&
|
||||
firstSelected.sphereUUID !== sphereUUID &&
|
||||
firstSelected.modelUUID !== modelUUID &&
|
||||
(firstSelected.isCorner || isConnectable) &&
|
||||
|
@ -596,6 +815,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
} else {
|
||||
isInvalidConnection = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (snappedSphere) {
|
||||
|
@ -632,14 +852,144 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
}
|
||||
});
|
||||
|
||||
const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => {
|
||||
const updatedStates = simulationStates.map(state => {
|
||||
// Handle Conveyor (which has multiple points)
|
||||
if (state.type === 'Conveyor') {
|
||||
const updatedConveyor: Types.ConveyorEventsSchema = {
|
||||
...state,
|
||||
points: state.points.map(point => {
|
||||
// Check if this point is either connection1 or connection2
|
||||
if ((state.modeluuid === connection1.model && point.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && point.uuid === connection2.point)) {
|
||||
|
||||
return {
|
||||
...point,
|
||||
connections: {
|
||||
...point.connections,
|
||||
targets: point.connections.targets.filter(target => {
|
||||
// Remove the target that matches the other connection
|
||||
return !(
|
||||
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
|
||||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
|
||||
);
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
return point;
|
||||
})
|
||||
};
|
||||
return updatedConveyor;
|
||||
}
|
||||
// Handle Vehicle
|
||||
else if (state.type === 'Vehicle') {
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedVehicle: Types.VehicleEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
connections: {
|
||||
...state.points.connections,
|
||||
targets: state.points.connections.targets.filter(target => {
|
||||
return !(
|
||||
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
|
||||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
|
||||
);
|
||||
})
|
||||
},
|
||||
// Ensure all required Vehicle point properties are included
|
||||
speed: state.points.speed,
|
||||
actions: state.points.actions
|
||||
}
|
||||
};
|
||||
return updatedVehicle;
|
||||
}
|
||||
}
|
||||
// Handle StaticMachine
|
||||
else if (state.type === 'StaticMachine') {
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedStaticMachine: Types.StaticMachineEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
connections: {
|
||||
...state.points.connections,
|
||||
targets: state.points.connections.targets.filter(target => {
|
||||
return !(
|
||||
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
|
||||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
|
||||
);
|
||||
})
|
||||
},
|
||||
// Ensure all required StaticMachine point properties are included
|
||||
actions: state.points.actions,
|
||||
triggers: state.points.triggers
|
||||
}
|
||||
};
|
||||
return updatedStaticMachine;
|
||||
}
|
||||
}
|
||||
// Handle ArmBot
|
||||
else if (state.type === 'ArmBot') {
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedArmBot: Types.ArmBotEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
connections: {
|
||||
...state.points.connections,
|
||||
targets: state.points.connections.targets.filter(target => {
|
||||
return !(
|
||||
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
|
||||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
|
||||
);
|
||||
})
|
||||
},
|
||||
actions: {
|
||||
...state.points.actions,
|
||||
processes: state.points.actions.processes?.filter(process => {
|
||||
return !(
|
||||
process.startPoint === connection1.point ||
|
||||
process.endPoint === connection1.point ||
|
||||
process.startPoint === connection2.point ||
|
||||
process.endPoint === connection2.point
|
||||
);
|
||||
}) || []
|
||||
},
|
||||
triggers: state.points.triggers
|
||||
}
|
||||
};
|
||||
return updatedArmBot;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
|
||||
const updatedPaths = updatedStates.filter(state =>
|
||||
state.modeluuid === connection1.model || state.modeluuid === connection2.model
|
||||
);
|
||||
|
||||
updateBackend(updatedPaths);
|
||||
|
||||
setSimulationStates(updatedStates);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<group name='simulationConnectionGroup' visible={!isPlaying} >
|
||||
<group name='simulationConnectionGroup' visible={!isPlaying}>
|
||||
{simulationStates.flatMap(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
return path.points.flatMap(point =>
|
||||
point.connections.targets.map((target, index) => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
if (targetPath?.type === 'Vehicle') return null;
|
||||
if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null;
|
||||
|
||||
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
|
||||
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
|
||||
|
@ -652,31 +1002,48 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
|
||||
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={`${point.uuid}-${target.pointUUID}-${index}`}
|
||||
ref={(el) => (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)}
|
||||
start={fromWorldPosition.toArray()}
|
||||
end={toWorldPosition.toArray()}
|
||||
mid={midPoint.toArray()}
|
||||
color="white"
|
||||
color={
|
||||
deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`
|
||||
? 'red'
|
||||
: targetPath?.type === 'ArmBot'
|
||||
? '#42a5f5'
|
||||
: 'white'
|
||||
}
|
||||
lineWidth={4}
|
||||
dashed
|
||||
dashed={(deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`) ? false : true}
|
||||
dashSize={0.75}
|
||||
dashScale={20}
|
||||
onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)}
|
||||
onPointerOut={() => setHoveredLineKey(null)}
|
||||
onClick={() => {
|
||||
if (deleteTool) {
|
||||
|
||||
const connection1 = { model: path.modeluuid, point: point.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
|
||||
}
|
||||
}}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
} else if (path.type === 'Vehicle') {
|
||||
}
|
||||
|
||||
if (path.type === 'Vehicle') {
|
||||
return path.points.connections.targets.map((target, index) => {
|
||||
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
|
||||
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
|
||||
|
@ -689,30 +1056,97 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
|
||||
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={`${path.points.uuid}-${target.pointUUID}-${index}`}
|
||||
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
|
||||
start={fromWorldPosition.toArray()}
|
||||
end={toWorldPosition.toArray()}
|
||||
mid={midPoint.toArray()}
|
||||
color="orange"
|
||||
color={
|
||||
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
|
||||
? 'red'
|
||||
: 'orange'
|
||||
}
|
||||
lineWidth={4}
|
||||
dashed
|
||||
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
|
||||
dashSize={0.75}
|
||||
dashScale={20}
|
||||
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
|
||||
onPointerOut={() => setHoveredLineKey(null)}
|
||||
onClick={() => {
|
||||
if (deleteTool) {
|
||||
|
||||
const connection1 = { model: path.modeluuid, point: path.points.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
}
|
||||
}}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (path.type === 'StaticMachine') {
|
||||
return path.points.connections.targets.map((target, index) => {
|
||||
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
|
||||
if (targetPath?.type !== 'ArmBot') return null;
|
||||
|
||||
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.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);
|
||||
|
||||
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);
|
||||
|
||||
return (
|
||||
<QuadraticBezierLine
|
||||
key={`${path.points.uuid}-${target.pointUUID}-${index}`}
|
||||
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
|
||||
start={fromWorldPosition.toArray()}
|
||||
end={toWorldPosition.toArray()}
|
||||
mid={midPoint.toArray()}
|
||||
color={
|
||||
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
|
||||
? 'red'
|
||||
: '#42a5f5'
|
||||
}
|
||||
lineWidth={4}
|
||||
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
|
||||
dashSize={0.75}
|
||||
dashScale={20}
|
||||
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
|
||||
onPointerOut={() => setHoveredLineKey(null)}
|
||||
onClick={() => {
|
||||
if (deleteTool) {
|
||||
|
||||
const connection1 = { model: path.modeluuid, point: path.points.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
|
||||
}
|
||||
}}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
})}
|
||||
|
||||
|
@ -730,6 +1164,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
)}
|
||||
</group>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default PathConnector;
|
|
@ -206,6 +206,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
|||
return (
|
||||
<group
|
||||
name={`${path.modeluuid}-${path.type}-path`}
|
||||
uuid={path.modeluuid}
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
|
@ -271,10 +272,11 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
|||
})}
|
||||
</group>
|
||||
);
|
||||
} else if (path.type === "Vehicle" || path.type === "StaticMachine") {
|
||||
} else if (path.type === "Vehicle") {
|
||||
return (
|
||||
<group
|
||||
name={`${path.modeluuid}-${path.type}-path`}
|
||||
uuid={path.modeluuid}
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
|
@ -323,6 +325,112 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
|||
</Sphere>
|
||||
</group>
|
||||
);
|
||||
} else if (path.type === "StaticMachine") {
|
||||
return (
|
||||
<group
|
||||
name={`${path.modeluuid}-${path.type}-path`}
|
||||
uuid={path.modeluuid}
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
setSelectedPath({
|
||||
path,
|
||||
group: groupRefs.current[path.modeluuid],
|
||||
});
|
||||
setSelectedActionSphere(null);
|
||||
setTransformMode(null);
|
||||
setSubModule("mechanics");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (eyeDropMode) return;
|
||||
setSelectedPath(null);
|
||||
setSubModule("properties");
|
||||
}}
|
||||
>
|
||||
<Sphere
|
||||
key={path.points.uuid}
|
||||
uuid={path.points.uuid}
|
||||
position={path.points.position}
|
||||
args={[0.15, 32, 32]}
|
||||
name="events-sphere"
|
||||
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
setSelectedActionSphere({
|
||||
path,
|
||||
points: sphereRefs.current[path.points.uuid],
|
||||
});
|
||||
setSubModule("mechanics");
|
||||
setSelectedPath(null);
|
||||
}}
|
||||
userData={{ points: path.points, path }}
|
||||
onPointerMissed={() => {
|
||||
if (eyeDropMode) return;
|
||||
setSubModule("properties");
|
||||
setSelectedActionSphere(null);
|
||||
}}
|
||||
>
|
||||
<meshStandardMaterial color="yellow" />
|
||||
</Sphere>
|
||||
</group>
|
||||
);
|
||||
} else if (path.type === "ArmBot") {
|
||||
return (
|
||||
<group
|
||||
name={`${path.modeluuid}-${path.type}-path`}
|
||||
uuid={path.modeluuid}
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
setSelectedPath({
|
||||
path,
|
||||
group: groupRefs.current[path.modeluuid],
|
||||
});
|
||||
setSelectedActionSphere(null);
|
||||
setTransformMode(null);
|
||||
setSubModule("mechanics");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (eyeDropMode) return;
|
||||
setSelectedPath(null);
|
||||
setSubModule("properties");
|
||||
}}
|
||||
>
|
||||
<Sphere
|
||||
key={path.points.uuid}
|
||||
uuid={path.points.uuid}
|
||||
position={path.points.position}
|
||||
args={[0.15, 32, 32]}
|
||||
name="events-sphere"
|
||||
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
setSelectedActionSphere({
|
||||
path,
|
||||
points: sphereRefs.current[path.points.uuid],
|
||||
});
|
||||
setSubModule("mechanics");
|
||||
setSelectedPath(null);
|
||||
}}
|
||||
userData={{ points: path.points, path }}
|
||||
onPointerMissed={() => {
|
||||
if (eyeDropMode) return;
|
||||
setSubModule("properties");
|
||||
setSelectedActionSphere(null);
|
||||
}}
|
||||
>
|
||||
<meshStandardMaterial color="pink" />
|
||||
</Sphere>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
|
|
|
@ -9,17 +9,20 @@ import { useProcessAnimation } from "./useProcessAnimations";
|
|||
import ProcessObject from "./processObject";
|
||||
import { ProcessData } from "./types";
|
||||
import { useSimulationStates } from "../../../store/store";
|
||||
import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
||||
|
||||
interface ProcessContainerProps {
|
||||
processes: ProcessData[];
|
||||
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
|
||||
agvRef: any;
|
||||
MaterialRef: any;
|
||||
}
|
||||
|
||||
const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
||||
processes,
|
||||
setProcesses,
|
||||
agvRef,
|
||||
MaterialRef,
|
||||
}) => {
|
||||
const gltf = useLoader(GLTFLoader, crate) as GLTF;
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
|
@ -44,11 +47,57 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||
() => ({
|
||||
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
||||
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||
Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||
// Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||
Default: new THREE.MeshStandardMaterial(),
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Update material references for all spawned objects
|
||||
Object.entries(animationStates).forEach(([processId, processState]) => {
|
||||
Object.keys(processState.spawnedObjects).forEach((objectId) => {
|
||||
const entry = {
|
||||
processId,
|
||||
objectId,
|
||||
};
|
||||
|
||||
const materialType =
|
||||
processState.spawnedObjects[objectId]?.currentMaterialType;
|
||||
|
||||
if (!materialType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matRefArray = MaterialRef.current;
|
||||
|
||||
// Find existing material group
|
||||
const existing = matRefArray.find(
|
||||
(entryGroup: { material: string; objects: any[] }) =>
|
||||
entryGroup.material === materialType
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
// Check if this processId + objectId already exists
|
||||
const alreadyExists = existing.objects.some(
|
||||
(o: any) =>
|
||||
o.processId === entry.processId && o.objectId === entry.objectId
|
||||
);
|
||||
|
||||
if (!alreadyExists) {
|
||||
existing.objects.push(entry);
|
||||
}
|
||||
} else {
|
||||
// Create new group for this material type
|
||||
matRefArray.push({
|
||||
material: materialType,
|
||||
objects: [entry],
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [animationStates, MaterialRef, agvRef]);
|
||||
|
||||
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
|
||||
|
||||
useFrame(() => {
|
||||
|
@ -252,14 +301,39 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||
const nextPointIdx = stateRef.currentIndex + 1;
|
||||
const isLastPoint = nextPointIdx >= path.length;
|
||||
|
||||
// if (isLastPoint) {
|
||||
// if (currentPointData?.actions) {
|
||||
// const shouldStop = !hasNonInheritActions(
|
||||
// currentPointData.actions
|
||||
// );
|
||||
// if (shouldStop) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (isLastPoint) {
|
||||
if (currentPointData?.actions) {
|
||||
const shouldStop = !hasNonInheritActions(
|
||||
const hasNonInherit = hasNonInheritActions(
|
||||
currentPointData.actions
|
||||
);
|
||||
if (shouldStop) {
|
||||
if (!hasNonInherit) {
|
||||
// Remove the object if all actions are inherit
|
||||
updatedObjects[objectId] = {
|
||||
...obj,
|
||||
visible: false,
|
||||
state: { ...stateRef, isAnimating: false },
|
||||
};
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// No actions at last point - remove the object
|
||||
updatedObjects[objectId] = {
|
||||
...obj,
|
||||
visible: false,
|
||||
state: { ...stateRef, isAnimating: false },
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +403,7 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||
.filter(([_, obj]) => obj.visible)
|
||||
.map(([objectId, obj]) => {
|
||||
const process = processedProcesses.find((p) => p.id === processId);
|
||||
|
||||
const renderAs = process?.renderAs || "custom";
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,18 +6,24 @@ interface ProcessContainerProps {
|
|||
processes: any[];
|
||||
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
|
||||
agvRef: any;
|
||||
MaterialRef: any;
|
||||
}
|
||||
|
||||
const ProcessContainer: React.FC<ProcessContainerProps> = ({
|
||||
processes,
|
||||
setProcesses,
|
||||
agvRef
|
||||
agvRef,
|
||||
MaterialRef,
|
||||
}) => {
|
||||
console.log("processes: ", processes);
|
||||
return (
|
||||
<>
|
||||
<ProcessCreator onProcessesCreated={setProcesses} />
|
||||
<ProcessAnimator processes={processes} setProcesses={setProcesses} agvRef={agvRef}/>
|
||||
<ProcessAnimator
|
||||
processes={processes}
|
||||
setProcesses={setProcesses}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -25,7 +25,6 @@ interface EnhancedProcessAnimationState extends ProcessAnimationState {
|
|||
pointId: string;
|
||||
objectId: string;
|
||||
triggerId: string;
|
||||
hasSpawnedZeroIntervalObject?: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
|
@ -139,7 +138,6 @@ export const useProcessAnimation = (
|
|||
processDelayDuration: 0,
|
||||
triggerCounts,
|
||||
triggerLogs: [],
|
||||
hasSpawnedZeroIntervalObject: false, // Initialize the new property
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -453,7 +451,7 @@ export const useProcessAnimation = (
|
|||
|
||||
// Increment expectedHitCount if not playing
|
||||
if (!vehicleEntry.isplaying) {
|
||||
vehicleEntry.expectedHitCount = processTotalHits + 1;
|
||||
vehicleEntry.expectedHitCount = processTotalHits;
|
||||
}
|
||||
|
||||
// Set vehicle's hitCount to the processTotalHits
|
||||
|
|
|
@ -18,6 +18,7 @@ function Simulation() {
|
|||
const { simulationStates, setSimulationStates } = useSimulationStates();
|
||||
const [processes, setProcesses] = useState<any[]>([]);
|
||||
const agvRef = useRef([]);
|
||||
const MaterialRef = useRef([]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -26,8 +27,17 @@ function Simulation() {
|
|||
<>
|
||||
<PathCreation pathsGroupRef={pathsGroupRef} />
|
||||
<PathConnector pathsGroupRef={pathsGroupRef} />
|
||||
<ProcessContainer processes={processes} setProcesses={setProcesses} agvRef={agvRef} />
|
||||
<Agv processes={processes} agvRef={agvRef} />
|
||||
<ProcessContainer
|
||||
processes={processes}
|
||||
setProcesses={setProcesses}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
<Agv
|
||||
processes={processes}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -1,55 +1,22 @@
|
|||
// import html2canvas from "html2canvas";
|
||||
|
||||
export const captureVisualization = async (): Promise<string | null> => {
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return null;
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return null;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return null;
|
||||
try {
|
||||
// Use html2canvas to capture the container
|
||||
// const canvas = await html2canvas(container, {
|
||||
// scale: 1, // Adjust scale for higher/lower resolution
|
||||
// });
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
canvas.width = rect.width;
|
||||
canvas.height = rect.height;
|
||||
// // Convert the canvas to a data URL (PNG format)
|
||||
// const dataUrl = canvas.toDataURL("image/png");
|
||||
// return dataUrl;
|
||||
|
||||
// Draw background
|
||||
ctx.fillStyle = getComputedStyle(container).backgroundColor;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Capture all canvas elements
|
||||
const canvases = container.querySelectorAll("canvas");
|
||||
canvases.forEach((childCanvas) => {
|
||||
const childRect = childCanvas.getBoundingClientRect();
|
||||
const x = childRect.left - rect.left;
|
||||
const y = childRect.top - rect.top;
|
||||
ctx.drawImage(childCanvas, x, y, childRect.width, childRect.height);
|
||||
});
|
||||
|
||||
// Capture SVG elements
|
||||
const svgs = container.querySelectorAll("svg");
|
||||
for (const svg of Array.from(svgs)) {
|
||||
const svgString = new XMLSerializer().serializeToString(svg);
|
||||
const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
|
||||
const url = URL.createObjectURL(svgBlob);
|
||||
|
||||
try {
|
||||
const img = await new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.onload = () => resolve(image);
|
||||
image.onerror = reject;
|
||||
image.src = url;
|
||||
});
|
||||
|
||||
const svgRect = svg.getBoundingClientRect();
|
||||
ctx.drawImage(
|
||||
img,
|
||||
svgRect.left - rect.left,
|
||||
svgRect.top - rect.top,
|
||||
svgRect.width,
|
||||
svgRect.height
|
||||
);
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
return canvas.toDataURL("image/png");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error capturing visualization:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,10 +28,10 @@ export const handleSaveTemplate = async ({
|
|||
templates = [],
|
||||
visualizationSocket,
|
||||
}: HandleSaveTemplateProps): Promise<void> => {
|
||||
console.log('floatingWidget: ', floatingWidget);
|
||||
try {
|
||||
// Check if the selected zone has any widgets
|
||||
if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) {
|
||||
console.warn("No widgets found in the selected zone.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,8 @@ export const handleSaveTemplate = async ({
|
|||
}
|
||||
|
||||
// Capture visualization snapshot
|
||||
const snapshot = await captureVisualization();
|
||||
// const snapshot = await captureVisualization();
|
||||
const snapshot = null;
|
||||
|
||||
// if (!snapshot) {
|
||||
// return;
|
||||
|
@ -72,7 +73,6 @@ export const handleSaveTemplate = async ({
|
|||
: "";
|
||||
|
||||
if (!organization) {
|
||||
console.error("Organization could not be determined from email.");
|
||||
return;
|
||||
}
|
||||
let saveTemplate = {
|
||||
|
@ -87,13 +87,13 @@ export const handleSaveTemplate = async ({
|
|||
try {
|
||||
addTemplate(newTemplate);
|
||||
// const response = await saveTemplateApi(organization, newTemplate);
|
||||
// console.log("Save API Response:", response);
|
||||
//
|
||||
|
||||
// Add template only if API call succeeds
|
||||
} catch (apiError) {
|
||||
// console.error("Error saving template to API:", apiError);
|
||||
//
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error("Error in handleSaveTemplate:", error);
|
||||
//
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,22 +15,19 @@ type WidgetData = {
|
|||
|
||||
export default function SocketRealTimeViz() {
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { setSelectedZone } = useSelectedZoneStore();
|
||||
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
|
||||
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const { addWidget } = useZoneWidgetStore()
|
||||
const { templates, removeTemplate } = useTemplateStore();
|
||||
const { removeTemplate } = useTemplateStore();
|
||||
const { setTemplates } = useTemplateStore();
|
||||
const { zoneWidgetData, setZoneWidgetData, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore();
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
if (visualizationSocket) {
|
||||
//add panel response
|
||||
visualizationSocket.on("viz-panel:response:updates", (addPanel: any) => {
|
||||
console.log('addPanel: ', addPanel);
|
||||
|
||||
if (addPanel.success) {
|
||||
let addPanelData = addPanel.data.data
|
||||
setSelectedZone(addPanelData)
|
||||
|
@ -38,15 +35,33 @@ export default function SocketRealTimeViz() {
|
|||
})
|
||||
//delete panel response
|
||||
visualizationSocket.on("viz-panel:response:delete", (deletePanel: any) => {
|
||||
console.log('deletePanel: ', deletePanel);
|
||||
|
||||
if (deletePanel.success) {
|
||||
let deletePanelData = deletePanel.data.data
|
||||
setSelectedZone(deletePanelData)
|
||||
}
|
||||
})
|
||||
//clear Panel response
|
||||
visualizationSocket.on("viz-panel:response:clear", (clearPanel: any) => {
|
||||
|
||||
if (clearPanel.success && clearPanel.message === "PanelWidgets cleared successfully") {
|
||||
|
||||
let clearPanelData = clearPanel.data.data
|
||||
setSelectedZone(clearPanelData)
|
||||
}
|
||||
})
|
||||
//lock Panel response
|
||||
visualizationSocket.on("viz-panel:response:locked", (lockPanel: any) => {
|
||||
|
||||
if (lockPanel.success && lockPanel.message === "locked panel updated successfully") {
|
||||
|
||||
let lockPanelData = lockPanel.data.data
|
||||
setSelectedZone(lockPanelData)
|
||||
}
|
||||
})
|
||||
// add 2dWidget response
|
||||
visualizationSocket.on("viz-widget:response:updates", (add2dWidget: any) => {
|
||||
console.log('add2dWidget: ', add2dWidget);
|
||||
|
||||
|
||||
if (add2dWidget.success && add2dWidget.data) {
|
||||
setSelectedZone((prev) => {
|
||||
|
@ -65,7 +80,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//delete 2D Widget response
|
||||
visualizationSocket.on("viz-widget:response:delete", (deleteWidget: any) => {
|
||||
console.log('deleteWidget: ', deleteWidget);
|
||||
|
||||
|
||||
if (deleteWidget?.success && deleteWidget.data) {
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
|
@ -78,7 +93,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//add Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:updates", (addFloatingWidget: any) => {
|
||||
console.log('addFloatingWidget: ', addFloatingWidget);
|
||||
|
||||
|
||||
if (addFloatingWidget.success) {
|
||||
if (addFloatingWidget.success && addFloatingWidget.message === "FloatWidget created successfully") {
|
||||
|
@ -105,7 +120,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//duplicate Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:addDuplicate", (duplicateFloatingWidget: any) => {
|
||||
console.log('duplicateFloatingWidget: ', duplicateFloatingWidget);
|
||||
|
||||
|
||||
if (duplicateFloatingWidget.success && duplicateFloatingWidget.message === "duplicate FloatWidget created successfully") {
|
||||
useDroppedObjectsStore.setState((state) => {
|
||||
|
@ -133,7 +148,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//delete Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:delete", (deleteFloatingWidget: any) => {
|
||||
console.log('deleteFloatingWidget: ', deleteFloatingWidget);
|
||||
|
||||
|
||||
if (deleteFloatingWidget.success) {
|
||||
deleteObject(deleteFloatingWidget.data.zoneName, deleteFloatingWidget.data.floatWidgetID);
|
||||
|
@ -142,9 +157,9 @@ export default function SocketRealTimeViz() {
|
|||
//add 3D Widget response
|
||||
visualizationSocket.on("viz-widget3D:response:updates", (add3DWidget: any) => {
|
||||
|
||||
console.log('add3DWidget: ', add3DWidget);
|
||||
|
||||
if (add3DWidget.success) {
|
||||
console.log('add3DWidget: ', add3DWidget);
|
||||
|
||||
if (add3DWidget.message === "Widget created successfully") {
|
||||
addWidget(add3DWidget.data.zoneId, add3DWidget.data.widget);
|
||||
}
|
||||
|
@ -152,7 +167,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//delete 3D Widget response
|
||||
visualizationSocket.on("viz-widget3D:response:delete", (delete3DWidget: any) => {
|
||||
console.log('delete3DWidget: ', delete3DWidget);
|
||||
|
||||
// "3DWidget delete unsuccessfull"
|
||||
if (delete3DWidget.success && delete3DWidget.message === "3DWidget delete successfull") {
|
||||
const activeZoneWidgets = zoneWidgetData[delete3DWidget.data.zoneId] || [];
|
||||
|
@ -164,16 +179,16 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//update3D widget response
|
||||
visualizationSocket.on("viz-widget3D:response:modifyPositionRotation", (update3DWidget: any) => {
|
||||
console.log('update3DWidget: ', update3DWidget);
|
||||
|
||||
if (update3DWidget.success && update3DWidget.message==="widget update successfully") {
|
||||
|
||||
|
||||
if (update3DWidget.success && update3DWidget.message === "widget update successfully") {
|
||||
updateWidgetPosition(update3DWidget.data.zoneId, update3DWidget.data.widget.id, update3DWidget.data.widget.position);
|
||||
updateWidgetRotation(update3DWidget.data.zoneId, update3DWidget.data.widget.id, update3DWidget.data.widget.rotation);
|
||||
}
|
||||
});
|
||||
// add Template response
|
||||
visualizationSocket.on("viz-template:response:add", (addingTemplate: any) => {
|
||||
console.log('addingTemplate: ', addingTemplate);
|
||||
|
||||
|
||||
if (addingTemplate.success) {
|
||||
if (addingTemplate.message === "Template saved successfully") {
|
||||
|
@ -183,7 +198,7 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//load Template response
|
||||
visualizationSocket.on("viz-template:response:addTemplateZone", (loadTemplate: any) => {
|
||||
console.log('loadTemplate: ', loadTemplate);
|
||||
|
||||
|
||||
if (loadTemplate.success) {
|
||||
if (loadTemplate.message === "Template placed in Zone") {
|
||||
|
@ -206,14 +221,12 @@ export default function SocketRealTimeViz() {
|
|||
});
|
||||
//delete Template response
|
||||
visualizationSocket.on("viz-template:response:delete", (deleteTemplate: any) => {
|
||||
console.log('deleteTemplate: ', deleteTemplate);
|
||||
|
||||
|
||||
if (deleteTemplate.success) {
|
||||
if (deleteTemplate.message === 'Template deleted successfully') {
|
||||
removeTemplate(deleteTemplate.data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,17 +22,17 @@ import LoadingPage from "../components/templates/LoadingPage";
|
|||
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
|
||||
import RenderOverlay from "../components/templates/Overlay";
|
||||
import MenuBar from "../components/ui/menu/menu";
|
||||
import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys";
|
||||
|
||||
const Project: React.FC = () => {
|
||||
let navigate = useNavigate();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { loadingProgress, setLoadingProgress } = useLoadingProgress();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
const { setUserName } = useUserName();
|
||||
const { setOrganization } = useOrganization();
|
||||
const { setFloorItems } = useFloorItems();
|
||||
const { setWallItems } = useWallItems();
|
||||
const { setZones } = useZones();
|
||||
const [openMenu, setOpenMenu] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setFloorItems([]);
|
||||
|
@ -56,6 +56,7 @@ const Project: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="project-main">
|
||||
<KeyPressListener />
|
||||
{loadingProgress && <LoadingPage progress={loadingProgress} />}
|
||||
{!isPlaying && (
|
||||
<>
|
||||
|
|
|
@ -2,7 +2,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}
|
|||
|
||||
export const getAssetModel = async (modelId: string) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v1/AssetFile/${modelId}`, {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${modelId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
|
||||
export const getFloorAssets = async (organization: string) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/floorAssets/${organization}`, {
|
||||
|
@ -14,6 +15,7 @@ export const getFloorAssets = async (organization: string) => {
|
|||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const setFloorItemApi = async (
|
||||
organization: string,
|
||||
modeluuid: string,
|
||||
modelname: string,
|
||||
modelfileID: string,
|
||||
position: Object,
|
||||
rotation: Object,
|
||||
isLocked: boolean,
|
||||
isVisible: boolean,
|
||||
modeluuid?: string,
|
||||
modelname?: string,
|
||||
modelfileID?: string,
|
||||
position?: Object,
|
||||
rotation?: Object,
|
||||
isLocked?: boolean,
|
||||
isVisible?: boolean,
|
||||
eventData?: any
|
||||
) => {
|
||||
try {
|
||||
|
|
|
@ -26,7 +26,7 @@ onmessage = async (event) => {
|
|||
const message = "gltfLoaded";
|
||||
postMessage({ message, modelID, modelBlob });
|
||||
} else {
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v1/AssetFile/${modelID}`;
|
||||
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${modelID}`;
|
||||
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||
await storeGLTF(modelID, modelBlob);
|
||||
const message = "gltfLoaded";
|
||||
|
|
|
@ -15,7 +15,7 @@ export const deleteZonesApi = async (userId: string, organization: string, zoneI
|
|||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log('result: ', result);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
|
||||
export const getZonesApi = async (organization: string) => {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const clearPanel = async (
|
||||
zoneId: string,
|
||||
organization: string,
|
||||
panelName: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/clearpanel`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneId, panelName }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to clearPanel in the 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");
|
||||
}
|
||||
}
|
||||
};
|
|
@ -2,8 +2,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
|
|||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
|
||||
export const getZoneData = async (zoneId: string, organization: string) => {
|
||||
console.log("organization: ", organization);
|
||||
console.log("zoneId: ", zoneId);
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/A_zone/${zoneId}/${organization}`,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const lockPanel = async (
|
||||
zoneId: string,
|
||||
organization: string,
|
||||
lockedPanel: any
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/zones/lockedPanels`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneId, lockedPanel }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to Lock Panel in the 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");
|
||||
}
|
||||
}
|
||||
};
|
|
@ -9,20 +9,16 @@ export const update3dWidget = async (
|
|||
console.log("organization: ", organization);
|
||||
console.log("zoneId: ", zoneId);
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
organization,
|
||||
zoneId,
|
||||
id,
|
||||
position,
|
||||
}),
|
||||
}
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/modifyPR/widget3D`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
body: JSON.stringify({
|
||||
organization,
|
||||
zoneId,
|
||||
id,
|
||||
position,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
|
@ -68,7 +68,10 @@ export const useWalls = create<any>((set: any) => ({
|
|||
|
||||
export const useZones = create<any>((set: any) => ({
|
||||
zones: [],
|
||||
setZones: (x: any) => set(() => ({ zones: x })),
|
||||
setZones: (callback: any) =>
|
||||
set((state: any) => ({
|
||||
zones: typeof callback === "function" ? callback(state.zones) : callback,
|
||||
})),
|
||||
}));
|
||||
|
||||
interface ZonePointsState {
|
||||
|
@ -106,9 +109,9 @@ export const useMenuVisible = create<any>((set: any) => ({
|
|||
setMenuVisible: (x: any) => set(() => ({ menuVisible: x })),
|
||||
}));
|
||||
|
||||
export const useDeleteModels = create<any>((set: any) => ({
|
||||
deleteModels: false,
|
||||
setDeleteModels: (x: any) => set(() => ({ deleteModels: x })),
|
||||
export const useDeleteTool = create<any>((set: any) => ({
|
||||
deleteTool: false,
|
||||
setDeleteTool: (x: any) => set(() => ({ deleteTool: x })),
|
||||
}));
|
||||
|
||||
export const useToolMode = create<any>((set: any) => ({
|
||||
|
@ -164,9 +167,9 @@ export const useSelectedWallItem = create<any>((set: any) => ({
|
|||
setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })),
|
||||
}));
|
||||
|
||||
export const useselectedFloorItem = create<any>((set: any) => ({
|
||||
export const useSelectedFloorItem = create<any>((set: any) => ({
|
||||
selectedFloorItem: null,
|
||||
setselectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
|
||||
setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
|
||||
}));
|
||||
|
||||
export const useDeletableFloorItem = create<any>((set: any) => ({
|
||||
|
@ -248,6 +251,11 @@ export const useActiveTool = create<any>((set: any) => ({
|
|||
setActiveTool: (x: any) => set({ activeTool: x }),
|
||||
}));
|
||||
|
||||
export const useActiveSubTool = create<any>((set: any) => ({
|
||||
activeSubTool: "cursor",
|
||||
setActiveSubTool: (x: any) => set({ activeSubTool: x }),
|
||||
}));
|
||||
|
||||
export const use2DUndoRedo = create<any>((set: any) => ({
|
||||
is2DUndoRedo: null,
|
||||
set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }),
|
||||
|
@ -349,6 +357,7 @@ interface SimulationPathsStore {
|
|||
| Types.ConveyorEventsSchema
|
||||
| Types.VehicleEventsSchema
|
||||
| Types.StaticMachineEventsSchema
|
||||
| Types.ArmBotEventsSchema
|
||||
)[];
|
||||
setSimulationStates: (
|
||||
paths:
|
||||
|
@ -356,17 +365,20 @@ interface SimulationPathsStore {
|
|||
| Types.ConveyorEventsSchema
|
||||
| Types.VehicleEventsSchema
|
||||
| Types.StaticMachineEventsSchema
|
||||
| Types.ArmBotEventsSchema
|
||||
)[]
|
||||
| ((
|
||||
prev: (
|
||||
| Types.ConveyorEventsSchema
|
||||
| Types.VehicleEventsSchema
|
||||
| Types.StaticMachineEventsSchema
|
||||
| Types.ArmBotEventsSchema
|
||||
)[]
|
||||
) => (
|
||||
| Types.ConveyorEventsSchema
|
||||
| Types.VehicleEventsSchema
|
||||
| Types.StaticMachineEventsSchema
|
||||
| Types.ArmBotEventsSchema
|
||||
)[])
|
||||
) => void;
|
||||
}
|
||||
|
@ -467,3 +479,22 @@ export const usePlayAgv = create<any>((set, get) => ({
|
|||
setPlayAgv: (updateFn: (prev: any[]) => any[]) =>
|
||||
set({ PlayAgv: updateFn(get().PlayAgv) }),
|
||||
}));
|
||||
// Define the Asset type
|
||||
type Asset = {
|
||||
id: string;
|
||||
name: string;
|
||||
position?: [number, number, number]; // Optional: 3D position
|
||||
rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation
|
||||
};
|
||||
|
||||
// Zustand store type
|
||||
type ZoneAssetState = {
|
||||
zoneAssetId: Asset | null;
|
||||
setZoneAssetId: (asset: Asset | null) => void;
|
||||
};
|
||||
|
||||
// Zustand store
|
||||
export const useZoneAssetId = create<ZoneAssetState>((set) => ({
|
||||
zoneAssetId: null,
|
||||
setZoneAssetId: (asset) => set({ zoneAssetId: asset }),
|
||||
}));
|
||||
|
|
|
@ -12,7 +12,9 @@ type ZoneWidgetStore = {
|
|||
zoneWidgetData: Record<string, WidgetData[]>;
|
||||
setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void;
|
||||
addWidget: (zoneId: string, widget: WidgetData) => void;
|
||||
tempWidget: (zoneId: string, widget: WidgetData) => void;
|
||||
updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void;
|
||||
tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void;
|
||||
updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void;
|
||||
};
|
||||
|
||||
|
@ -31,6 +33,13 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
|
|||
[zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }],
|
||||
},
|
||||
})),
|
||||
tempWidget: (zoneId: string, widget: WidgetData) =>
|
||||
set((state: ZoneWidgetStore) => ({
|
||||
zoneWidgetData: {
|
||||
...state.zoneWidgetData,
|
||||
[zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }],
|
||||
},
|
||||
})),
|
||||
|
||||
updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) =>
|
||||
set((state: ZoneWidgetStore) => {
|
||||
|
@ -44,6 +53,18 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
|
|||
},
|
||||
};
|
||||
}),
|
||||
tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) =>
|
||||
set((state: ZoneWidgetStore) => {
|
||||
const widgets = state.zoneWidgetData[zoneId] || [];
|
||||
return {
|
||||
zoneWidgetData: {
|
||||
...state.zoneWidgetData,
|
||||
[zoneId]: widgets.map((widget: WidgetData) =>
|
||||
widget.id === widgetId ? { ...widget, position: newPosition } : widget
|
||||
),
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) =>
|
||||
set((state: ZoneWidgetStore) => {
|
||||
|
@ -59,13 +80,6 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
// export type WidgetData = {
|
||||
// id: string;
|
||||
// type: string;
|
||||
// position: [number, number, number];
|
||||
// rotation?: [number, number, number];
|
||||
// tempPosition?: [number, number, number];
|
||||
// };
|
||||
|
||||
interface RightClickStore {
|
||||
rightClickSelected: string | null;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.tools-container {
|
||||
@include flex-center;
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
bottom: 32px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
padding: 8px;
|
||||
|
|
|
@ -81,9 +81,12 @@
|
|||
|
||||
.returnOfInvestment {
|
||||
gap: 10px;
|
||||
min-width: 150px;
|
||||
|
||||
.charts {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.returns-wrapper {
|
||||
|
@ -126,6 +129,12 @@
|
|||
gap: 6px;
|
||||
border-radius: 5.2px;
|
||||
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.header {
|
||||
font-size: $small;
|
||||
text-align: start;
|
||||
|
|
|
@ -146,5 +146,6 @@
|
|||
font-size: var(--font-size-regulaar);
|
||||
font-weight: var(--font-size-regulaar);
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,67 @@
|
|||
position: relative;
|
||||
overflow: auto;
|
||||
|
||||
.template-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
min-height: 50vh;
|
||||
max-height: 60vh;
|
||||
}
|
||||
|
||||
.template-item {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.template-image-container {
|
||||
position: relative;
|
||||
padding-bottom: 56.25%; // 16:9 aspect ratio
|
||||
}
|
||||
|
||||
.template-image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.template-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.template-name {
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: #ff4444;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.no-templates {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 2rem;
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
|
||||
.widget-left-sideBar {
|
||||
min-height: 50vh;
|
||||
max-height: 60vh;
|
||||
|
@ -472,6 +533,15 @@
|
|||
font-size: var(--font-weight-regular);
|
||||
color: #4a4a4a;
|
||||
|
||||
.reviewChart {
|
||||
width: 100%;
|
||||
|
||||
.floating {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.selectedWidget {
|
||||
padding: 6px 12px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
border-radius: 20px;
|
||||
box-shadow: $box-shadow-medium;
|
||||
width: calc(100% - (320px + 270px + 90px));
|
||||
height: calc(100% - (200px + 80px));
|
||||
height: calc(100% - (250px));
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(270px + 45px);
|
||||
|
@ -16,10 +16,25 @@
|
|||
transition: all 0.2s;
|
||||
z-index: #{$z-index-default};
|
||||
|
||||
.floating {
|
||||
.realTime-viz-wrapper {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
min-height: 83px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.floating {
|
||||
|
||||
|
||||
width: calc(var(--realTimeViz-container-width) * 0.2);
|
||||
height: calc(var(--realTimeViz-container-width) * 0.05);
|
||||
|
||||
min-width: 250px;
|
||||
max-width: 300px;
|
||||
|
||||
min-height: 83px !important;
|
||||
// max-height: 100px !important;
|
||||
|
||||
background: var(--background-color);
|
||||
border: 1.23px solid var(--border-color);
|
||||
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
|
||||
|
@ -53,15 +68,16 @@
|
|||
display: flex;
|
||||
background-color: var(--background-color);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
bottom: 0px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
gap: 6px;
|
||||
|
||||
border-radius: 8px;
|
||||
max-width: 80%;
|
||||
overflow: auto;
|
||||
max-width: calc(100% - 500px);
|
||||
min-width: 150px;
|
||||
z-index: 3;
|
||||
transform: translate(-50%, -10%);
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
|
@ -108,7 +124,8 @@
|
|||
}
|
||||
|
||||
.zone-wrapper.bottom {
|
||||
bottom: 210px;
|
||||
bottom: var(--bottomWidth);
|
||||
// bottom: 200px;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
|
@ -129,7 +146,7 @@
|
|||
display: flex;
|
||||
background-color: rgba(224, 223, 255, 0.5);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
// bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
gap: 6px;
|
||||
|
@ -173,12 +190,15 @@
|
|||
overflow: auto;
|
||||
z-index: $z-index-tools;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -191,15 +211,15 @@
|
|||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 25% !important;
|
||||
min-height: 150px;
|
||||
|
||||
max-height: 100%;
|
||||
// border: 1px dashed var(--background-color-gray);
|
||||
border: 1px dashed var(--background-color-gray);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--box-shadow-medium);
|
||||
padding: 6px 0;
|
||||
background-color: var(--background-color);
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
|
||||
.kebab {
|
||||
width: 30px;
|
||||
|
@ -284,16 +304,17 @@
|
|||
&.bottom-panel {
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 150px;
|
||||
|
||||
.panel-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
|
||||
.chart-container {
|
||||
height: 100% !important;
|
||||
width: 20%;
|
||||
min-width: 150px;
|
||||
min-width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,29 +331,48 @@
|
|||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
&.right-panel {
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&.left-panel,
|
||||
&.right-panel {
|
||||
min-width: 150px;
|
||||
|
||||
.panel-content {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
gap: 6px;
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
max-height: 100%;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--box-shadow-medium);
|
||||
padding: 6px 0;
|
||||
background-color: var(--background-color);
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel.hidePanel {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.playingFlase {
|
||||
.zone-wrapper.bottom {
|
||||
bottom: 300px;
|
||||
bottom: var(--bottomWidth);
|
||||
// bottom: 210px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,9 +661,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -714,19 +751,18 @@
|
|||
}
|
||||
|
||||
.activeChart {
|
||||
outline: 1px solid var(--accent-color);
|
||||
outline: 2px solid var(--accent-color);
|
||||
z-index: 2 !important;
|
||||
}
|
||||
|
||||
.connectionSuccess {
|
||||
outline-color: #43C06D;
|
||||
outline-color: #43c06d;
|
||||
}
|
||||
|
||||
.connectionFails {
|
||||
outline-color: #ffe3e0;
|
||||
}
|
||||
|
||||
|
||||
.editWidgetOptions {
|
||||
position: absolute;
|
||||
// top: 50%;
|
||||
|
@ -739,7 +775,8 @@
|
|||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
min-width: 150px;
|
||||
min-width: 150px;
|
||||
|
||||
.option {
|
||||
padding: 8px 10px;
|
||||
color: var(--text-color);
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
}
|
||||
.distance-text {
|
||||
pointer-events: none !important;
|
||||
.distance {
|
||||
div {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%) scale(.8);
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
pointer-events: none !important;
|
||||
white-space: nowrap;
|
||||
// style
|
||||
|
@ -21,3 +21,7 @@
|
|||
box-shadow: var(--box-shadow-light);
|
||||
}
|
||||
}
|
||||
|
||||
.pointer-none {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
@ -455,6 +455,7 @@ export type EventData = {
|
|||
points: {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: {
|
||||
uuid: string;
|
||||
name: string;
|
||||
|
@ -470,5 +471,48 @@ export type EventData = {
|
|||
};
|
||||
speed: number;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "StaticMachine";
|
||||
points: {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: {
|
||||
uuid: string;
|
||||
name: string;
|
||||
buffer: number;
|
||||
material: string;
|
||||
};
|
||||
triggers: { uuid: string; name: string; type: string };
|
||||
connections: {
|
||||
source: { modelUUID: string; pointUUID: string };
|
||||
targets: { modelUUID: string; pointUUID: string }[];
|
||||
};
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "ArmBot";
|
||||
points: {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: {
|
||||
uuid: string;
|
||||
name: string;
|
||||
speed: number;
|
||||
processes: {
|
||||
triggerId: string;
|
||||
startPoint: string;
|
||||
endPoint: string;
|
||||
}[];
|
||||
};
|
||||
triggers: { uuid: string; name: string; type: string };
|
||||
connections: {
|
||||
source: { modelUUID: string; pointUUID: string };
|
||||
targets: { modelUUID: string; pointUUID: string }[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
|
||||
interface OuterClickProps {
|
||||
contextClassName: string;
|
||||
contextClassName: string[]; // Make sure this is an array of strings
|
||||
setMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,12 @@ export default function OuterClick({
|
|||
}: OuterClickProps) {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
const targets = event.target as HTMLElement;
|
||||
// Check if the click is outside the selectable-dropdown-wrapper
|
||||
if (!targets.closest(`.${contextClassName}`)) {
|
||||
// Check if the click is outside of any of the provided class names
|
||||
const isOutside = contextClassName.every(
|
||||
(className) => !targets.closest(`.${className}`)
|
||||
);
|
||||
|
||||
if (isOutside) {
|
||||
setMenuVisible(false); // Close the menu by updating the state
|
||||
}
|
||||
};
|
||||
|
@ -23,7 +27,7 @@ export default function OuterClick({
|
|||
return () => {
|
||||
document.removeEventListener("click", handleClick);
|
||||
};
|
||||
}, []);
|
||||
}, [contextClassName]); // Add contextClassName to dependency array to handle any changes
|
||||
|
||||
return null; // This component doesn't render anything
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
// Importing React and useEffect from React library
|
||||
import React, { useEffect } from "react";
|
||||
// Importing the necessary hooks and types from React and TypeScript
|
||||
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import { useActiveSubTool, useActiveTool, useAddAction, useDeleteTool, useSelectedWallItem, useToggleView, useToolMode } from "../../store/store";
|
||||
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
||||
|
||||
const KeyPressListener: React.FC = () => {
|
||||
// Function to detect if Shift, Ctrl, Alt, or combinations are pressed
|
||||
const detectModifierKeys = (event: KeyboardEvent): string => {
|
||||
const modifiers = [
|
||||
event.ctrlKey ? "Ctrl" : "",
|
||||
event.altKey ? "Alt" : "",
|
||||
event.shiftKey ? "Shift" : "",
|
||||
event.metaKey ? "Meta" : "" // Add support for Command/Win key
|
||||
].filter(Boolean);
|
||||
|
||||
// Ignore modifier keys when they're pressed alone
|
||||
const isModifierKey = [
|
||||
"Control", "Shift", "Alt", "Meta",
|
||||
"Ctrl", "AltGraph", "OS" // Additional modifier key aliases
|
||||
].includes(event.key);
|
||||
|
||||
const mainKey = isModifierKey ? "" : event.key.toUpperCase();
|
||||
|
||||
// Handle special cases for keys with different representations
|
||||
const normalizedKey = mainKey === " " ? "Space" : mainKey;
|
||||
|
||||
// Build the combination string
|
||||
if (modifiers.length > 0 && normalizedKey) {
|
||||
return `${modifiers.join("+")}+${normalizedKey}`;
|
||||
} else if (modifiers.length > 0) {
|
||||
return modifiers.join("+");
|
||||
} else {
|
||||
return normalizedKey;
|
||||
}
|
||||
};
|
||||
|
||||
// Importing the necessary hooks from the store
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { setActiveSubTool } = useActiveSubTool();
|
||||
const { toggleUI, setToggleUI } = useToggleStore();
|
||||
const { setToggleThreeD } = useThreeDStore();
|
||||
const { setToolMode } = useToolMode();
|
||||
const { setIsPlaying } = usePlayButtonStore();
|
||||
|
||||
// wall options
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { setDeleteTool } = useDeleteTool();
|
||||
const { setAddAction } = useAddAction();
|
||||
const { setSelectedWallItem } = useSelectedWallItem();
|
||||
const { setActiveTool } = useActiveTool();
|
||||
|
||||
useEffect(() => {
|
||||
// Function to handle keydown events
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
const isTyping =
|
||||
activeElement instanceof HTMLInputElement ||
|
||||
activeElement instanceof HTMLTextAreaElement ||
|
||||
(activeElement && activeElement.getAttribute('contenteditable') === 'true');
|
||||
|
||||
if (isTyping) {
|
||||
return; // Don't trigger shortcuts while typing
|
||||
}
|
||||
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
// Allow default behavior for F5 and F12
|
||||
if (["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default action for the key press
|
||||
event.preventDefault();
|
||||
|
||||
// Detect the key combination pressed
|
||||
if (keyCombination) {
|
||||
// Check for specific key combinations to switch modules
|
||||
if (keyCombination === "1" && !toggleView) {
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
setActiveModule("builder");
|
||||
}
|
||||
if (keyCombination === "2" && !toggleView) {
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
setActiveModule("simulation");
|
||||
}
|
||||
if (keyCombination === "3" && !toggleView) {
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
setActiveModule("visualization");
|
||||
}
|
||||
if (keyCombination === "4" && !toggleView) {
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
setToggleUI(false);
|
||||
setActiveModule("market");
|
||||
}
|
||||
|
||||
// sidebar toggle
|
||||
if (keyCombination === "Ctrl+." && activeModule !== "market") {
|
||||
setToggleUI(!toggleUI);
|
||||
}
|
||||
|
||||
// tools toggle
|
||||
if (keyCombination === "V") {
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
}
|
||||
if (keyCombination === "X") {
|
||||
setActiveTool("delete");
|
||||
setActiveSubTool("delete");
|
||||
}
|
||||
if (keyCombination === "H") {
|
||||
setActiveTool("free-hand");
|
||||
setActiveSubTool("free-hand");
|
||||
}
|
||||
|
||||
// player toggle
|
||||
if (keyCombination === "Ctrl+P" && !toggleView) {
|
||||
setIsPlaying(true);
|
||||
}
|
||||
|
||||
// builder key combination
|
||||
if (activeModule === "builder") {
|
||||
if (keyCombination === "TAB") {
|
||||
if (toggleView) {
|
||||
setToggleView(false);
|
||||
setToggleThreeD(true);
|
||||
setActiveTool("cursor");
|
||||
} else {
|
||||
setSelectedWallItem(null);
|
||||
setDeleteTool(false);
|
||||
setAddAction(null);
|
||||
setToggleView(true);
|
||||
setToggleThreeD(false);
|
||||
setActiveTool("cursor");
|
||||
}
|
||||
}
|
||||
// builder tools
|
||||
if (toggleView) {
|
||||
if (keyCombination === "Q" || keyCombination === "6") {
|
||||
setActiveTool("draw-wall");
|
||||
setToolMode("Wall");
|
||||
}
|
||||
if (keyCombination === "R" || keyCombination === "7") {
|
||||
setActiveTool("draw-aisle");
|
||||
setToolMode("Aisle");
|
||||
}
|
||||
if (keyCombination === "E" || keyCombination === "8") {
|
||||
setActiveTool("draw-zone");
|
||||
setToolMode("Zone");
|
||||
}
|
||||
if (keyCombination === "T" || keyCombination === "9") {
|
||||
setActiveTool("draw-floor");
|
||||
setToolMode("Floor");
|
||||
}
|
||||
}
|
||||
if (keyCombination === "M") {
|
||||
setActiveTool("measure");
|
||||
setToolMode("MeasurementScale");
|
||||
}
|
||||
}
|
||||
|
||||
// Undo redo
|
||||
if (keyCombination === "Ctrl+Z") {
|
||||
// Handle undo action here
|
||||
}
|
||||
if (keyCombination === "Ctrl+Y" || keyCombination === "Ctrl+Shift+Z") {
|
||||
// Handle redo action here
|
||||
}
|
||||
|
||||
// cleanup function to remove event listener
|
||||
if (keyCombination === "ESCAPE") {
|
||||
setActiveTool("cursor");
|
||||
setIsPlaying(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listener for keydown
|
||||
window.addEventListener("keydown", handleKeyPress);
|
||||
|
||||
// Cleanup function to remove event listener
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyPress);
|
||||
};
|
||||
}, [activeModule, toggleUI, toggleView]); // Empty dependency array ensures this runs only once
|
||||
|
||||
return null; // No UI component to render, so return null
|
||||
};
|
||||
|
||||
export default KeyPressListener;
|
Loading…
Reference in New Issue